Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 5992fec27c9d72aa44e8e9ba3bc674f5073370b7 0 parents
@Deradon authored
8 .gitignore
@@ -0,0 +1,8 @@
+.bundle/
+log/*.log
+pkg/
+test/dummy/db/*.sqlite3
+test/dummy/log/*.log
+test/dummy/tmp/
+**~
+
2  .rspec
@@ -0,0 +1,2 @@
+--color
+--format progress
81 .rvmrc
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+
+# This is an RVM Project .rvmrc file, used to automatically load the ruby
+# development environment upon cd'ing into the directory
+
+# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
+environment_id="ruby-1.9.3-p0@dcpu_16"
+
+#
+# Uncomment the following lines if you want to verify rvm version per project
+#
+# rvmrc_rvm_version="1.10.2" # 1.10.1 seams as a safe start
+# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
+# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
+# return 1
+# }
+#
+
+#
+# Uncomment following line if you want options to be set only for given project.
+#
+# PROJECT_JRUBY_OPTS=( --1.9 )
+#
+# The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
+#
+# chmod +x ${rvm_path}/hooks/after_use_jruby_opts
+#
+
+#
+# First we attempt to load the desired environment directly from the environment
+# file. This is very fast and efficient compared to running through the entire
+# CLI and selector. If you want feedback on which environment was used then
+# insert the word 'use' after --create as this triggers verbose mode.
+#
+if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
+then
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
+
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
+ then
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
+ fi
+else
+ # If the environment file has not yet been created, use the RVM CLI to select.
+ if ! rvm --create use "$environment_id"
+ then
+ echo "Failed to create RVM environment '${environment_id}'."
+ return 1
+ fi
+fi
+
+#
+# If you use an RVM gemset file to install a list of gems (*.gems), you can have
+# it be automatically loaded. Uncomment the following and adjust the filename if
+# necessary.
+#
+# filename=".gems"
+# if [[ -s "$filename" ]]
+# then
+# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
+# fi
+
+# If you use bundler, this might be useful to you:
+# if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
+# then
+# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
+# gem install bundler
+# fi
+# if [[ -s Gemfile ]] && command -v bundle
+# then
+# bundle install
+# fi
+
+if [[ $- == *i* ]] # check for interactive shells
+then
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
+else
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
+fi
+
5 Gemfile
@@ -0,0 +1,5 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in foodie.gemspec
+gemspec
+
24 Gemfile.lock
@@ -0,0 +1,24 @@
+PATH
+ remote: .
+ specs:
+ dcpu16 (0.0.1)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ diff-lcs (1.1.3)
+ rspec (2.8.0)
+ rspec-core (~> 2.8.0)
+ rspec-expectations (~> 2.8.0)
+ rspec-mocks (~> 2.8.0)
+ rspec-core (2.8.0)
+ rspec-expectations (2.8.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.8.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ dcpu16!
+ rspec (~> 2.6)
21 MIT-LICENSE
@@ -0,0 +1,21 @@
+Copyright 2012 Patrick Helm
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
10 README.rdoc
@@ -0,0 +1,10 @@
+= Rdcpu16
+
+== Ruby port of: http://0x10c.com/doc/dcpu-16.txt
+This is a simple Ruby port of the fictive 16-bit-cpu DCPU16.
+
+== Make sure to checkout these resources:
+
+ * http://0x10c.com/
+ * http://0x10cforum.com/
+
42 Rakefile
@@ -0,0 +1,42 @@
+##!/usr/bin/env rake
+##include Rake::DSL
+
+require "bundler/gem_tasks"
+
+#begin
+# require 'bundler/setup'
+#rescue LoadError
+# puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
+#end
+#begin
+# require 'rdoc/task'
+#rescue LoadError
+# require 'rdoc/rdoc'
+# require 'rake/rdoctask'
+# RDoc::Task = Rake::RDocTask
+#end
+
+#RDoc::Task.new(:rdoc) do |rdoc|
+# rdoc.rdoc_dir = 'rdoc'
+# rdoc.title = 'RubyRescuetime'
+# rdoc.options << '--line-numbers'
+# rdoc.rdoc_files.include('README.rdoc')
+# rdoc.rdoc_files.include('lib/**/*.rb')
+#end
+
+
+
+#Bundler::GemHelper.install_tasks
+
+## require 'rake/testtask'
+
+##Rake::TestTask.new(:test) do |t|
+## t.libs << 'lib'
+## t.libs << 'test'
+## t.pattern = 'test/**/*_test.rb'
+## t.verbose = true
+##end
+
+
+##task :default => :test
+
24 dcpu16.gemspec
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+
+# Maintain your gem's version:
+require "dcpu16/version"
+
+# Describe your gem and declare its dependencies:
+Gem::Specification.new do |s|
+ s.name = "Rdcpu16"
+ s.version = DCPU16::VERSION
+ s.authors = ["Patrick Helm"]
+ s.email = ["deradon87@gmail.com"]
+ s.homepage = "https://github.com/Deradon/Rdcpu16"#"https://github.com/Deradon/Ruby-Rescuetime"
+ s.summary = "Ruby port of DCPU16"
+ s.description = "This is a simple Ruby port of the fictive 16-bit-cpu DCPU16."
+
+# s.executables = ["rdcpu16"]
+ s.files = Dir["{bin,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
+ s.test_files = Dir["{spec}/**/*"]
+ s.require_paths = ["lib"]
+
+ s.add_development_dependency "rspec", "~> 2.6"
+end
+
6 lib/dcpu16.rb
@@ -0,0 +1,6 @@
+require 'dcpu16/cpu'
+require 'dcpu16/version'
+
+module DCPU16
+end
+
93 lib/dcpu16/cpu.rb
@@ -0,0 +1,93 @@
+require 'dcpu16/support/debug'
+
+require 'dcpu16/cpu/instructions'
+
+require 'dcpu16/instruction'
+require 'dcpu16/literal'
+require 'dcpu16/memory'
+require 'dcpu16/operand'
+require 'dcpu16/register'
+
+module DCPU16
+ class CPU
+ include DCPU16::Debug
+ include DCPU16::Instructions
+
+ RAM_SIZE = 0x10000
+ REGISTERS = [:A, :B, :C, :X, :Y, :Z, :I, :J]
+ REGISTERS_COUNT = REGISTERS.length
+ # CLOCK_CYCLE = 100000
+
+ attr_accessor :cycle, :memory, :registers
+
+ # program counter (PC), stack pointer (SP), overflow (O)
+ attr_accessor :PC, :SP, :O
+
+ # HACK: actually used to determine if we need to skip next instruction
+ attr_accessor :skip
+
+ # attr_reader :clock_cycle
+
+ def initialize(memory = [])
+ @cycle = 0
+ @memory = DCPU16::Memory.new(memory)
+ @registers = Array.new(REGISTERS_COUNT) { DCPU16::Register.new }
+ @PC = DCPU16::Register.new(0x0)
+ @SP = DCPU16::Register.new(0xFFFF)
+ @O = DCPU16::Register.new(0x0)
+
+ @debug = true
+ @skip = false
+ end
+
+ # Define alias_methods for Registers: A(), B(), ...
+ REGISTERS.each_with_index { |k, v| define_method(k) { registers[v] } }
+
+ def run
+ step while true
+ end
+
+ # Resets the CPU and its sub-systems (registers, memory, ...)
+ def reset
+ @cycle = 0
+ @memory.reset
+ @registers.each { |r| r.reset }
+ @PC.reset
+ @SP.reset
+ @O.reset
+ end
+
+ # DOC
+ def last_instruction
+ @instruction
+ end
+
+ # Perform a single step
+ # TODO: Refacor if/else/if/else ...
+ def step
+ @instruction = Instruction.new(@memory.read(@PC))
+ @PC += 1
+
+ op = @instruction.op
+ a = get_operand(@instruction.a)
+ b = get_operand(@instruction.b) if @instruction.b
+
+ if @skip
+ @skip = false
+ else
+ if b
+ result = self.send(op, a.value, b.value)
+ else
+ result = self.send(op, a.value)
+ end
+ a.write(result) if result
+ end
+ end
+
+ # TODO: May be removed
+ def get_operand(value)
+ DCPU16::Operand.new(self, value)
+ end
+ end
+end
+
137 lib/dcpu16/cpu/instructions.rb
@@ -0,0 +1,137 @@
+module DCPU16
+ module Instructions
+ # sets a to b
+ def set(a, b)
+ @cycle += 1
+ b
+ end
+
+ # sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise
+ def add(a, b)
+ @cycle += 2
+ (a + b > 0xFFFF) ? @O.write(0x0001) : @O.write(0x0)
+ a + b
+ end
+
+ # sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise
+ def sub(a, b)
+ @cycle += 2
+ (b > a) ? @O.write(0xffff) : @O.write(0x0)
+ a - b
+ end
+
+ # sets a to a*b, sets O to
+ def mul(a, b)
+ @cycle += 2
+ @O.write( ( (a * b) >> 16) & 0xffff )
+ a * b
+ end
+
+ # sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead.
+ def div(a, b)
+ @cycle += 3
+ if b == 0
+ @O.write(0)
+ return 0
+ else
+ @O.write( ( (a << 16) / b) & 0xffff )
+ return (a / b)
+ end
+ end
+
+ # sets a to a%b. if b==0, sets a to 0 instead.
+ def mod(a, b)
+ @cycle += 3
+ (b == 0) ? 0 : a % b
+ end
+
+ # sets a to a<<b, sets O to ((a<<b)>>16)&0xffff
+ def shl(a, b)
+ @cycle += 2
+ @O.write( ( (a << b) >> 16 ) & 0xffff )
+ a << b
+ end
+
+ # sets a to a>>b, sets O to ((a<<16)>>b)&0xffff
+ def shr(a, b)
+ @cycle += 2
+ @O.write( ( (a << 16) >> b) & 0xffff )
+ a >> b
+ end
+
+ # sets a to a & b
+ def and(a, b)
+ @cycle += 1
+ a & b
+ end
+
+ # sets a to a | b
+ def bor(a, b)
+ @cycle += 1
+ a | b
+ end
+
+ # sets a to a ^ b
+ def xor(a, b)
+ @cycle += 1
+ a ^ b
+ end
+
+ # performs next instruction only if a == b
+ def ife(a, b)
+ @cycle += 2
+ @skip = !(a == b)
+ @cycle += 1 unless a == b
+
+ return nil
+ end
+
+ # performs next instruction only if a != b
+ def ifn(a, b)
+ @cycle += 2
+ @skip = !(a != b)
+ @cycle += 1 unless a != b
+
+ return nil
+ end
+
+ # performs next instruction only if a > b
+ def ifg(a, b)
+ @cycle += 2
+ @skip = !(a > b)
+ @cycle += 1 unless a > b
+
+ return nil
+ end
+
+ # performs next instruction only if (a & b) != 0
+ def ifb(a, b)
+ @cycle += 2
+ @skip = !((a & b) != 0)
+ @cycle += 1 unless (a & b) != 0
+
+ return nil
+ end
+
+
+ class Reserved < StandardError; end
+
+ ### NON - Basic ###
+ def reserved(a)
+ raise Reserved
+ end
+
+ # JSR a - pushes the address of the next instruction to the stack, then sets PC to a
+ def jsr(a)
+ @cycle += 2
+
+ # pushes the address of the next instruction to the stack
+ @SP -= 1
+ @memory.write(@SP, @PC.read)
+
+ @PC.write(a)
+ return nil
+ end
+ end
+end
+
57 lib/dcpu16/instruction.rb
@@ -0,0 +1,57 @@
+# Instructions are 1-3 words long and are fully defined by the first word.
+# In a basic instruction, the lower four bits of the first word of the instruction are the opcode,
+# and the remaining twelve bits are split into two six bit values, called a and b.
+# a is always handled by the processor before b, and is the lower six bits.
+
+# In bits (with the least significant being last),
+# a basic instruction has the format: bbbbbbaaaaaaoooo
+
+# bbbb bbaa aaaa oooo
+# 0000 0000 0000 1111 -> 0x000f
+# 0000 0011 1111 0000 -> 0x03f0
+# 1111 1100 0000 0000 -> 0xfc00
+
+# Non-basic opcodes always have their lower four bits unset, have one value and a six bit opcode.
+# In binary, they have the format: aaaaaaoooooo0000
+
+# aaaa aaoo oooo 0000
+# 0000 0011 1111 0000 -> 0x03f0
+# 1111 1100 0000 0000 -> 0xfc00
+
+module DCPU16
+ class Instruction
+ INSTRUCTIONS = [
+ :reserved,
+ :set, :add, :sub, :mul, :div, :mod,
+ :shl, :shr,
+ :and, :bor, :xor,
+ :ife, :ifn, :ifg, :ifb
+ ]
+
+ NON_BASIC_INSTRUCTIONS = [
+ :reserved,
+ :jsr
+ ]
+
+ attr_reader :opcode, :a, :b, :word
+
+ def initialize(word = nil)
+ @word = word.value
+ @opcode = @word & 0x000F
+
+ if @opcode == 0x0
+ @non_basic = true
+ @opcode = (@word >> 4) & 0x3f
+ @a = @word >> 10
+ else
+ @a = (@word >> 4) & 0x3f
+ @b = @word >> 10
+ end
+ end
+
+ def op
+ @non_basic ? NON_BASIC_INSTRUCTIONS[@opcode] : INSTRUCTIONS[@opcode]
+ end
+ end
+end
+
19 lib/dcpu16/literal.rb
@@ -0,0 +1,19 @@
+# Ruby, Y U NO INHERIT FROM INTEGER?
+module DCPU16
+ class Literal
+ def initialize(value)
+ @value = value
+ end
+
+ def value
+ @value
+ end
+ alias_method :read, :value
+
+ # If any instruction tries to assign a literal value, the assignment fails silently
+ def write(value)
+ # pass
+ end
+ end
+end
+
57 lib/dcpu16/memory.rb
@@ -0,0 +1,57 @@
+module DCPU16
+ class Memory < Array
+ SIZE = 0x10000
+ DEFAULT_VALUE = 0x0
+
+ def initialize(default = [])
+ super(SIZE, DEFAULT_VALUE)
+ default.each_with_index { |word, offset| write(offset, word) }
+ end
+
+ def read(offset)
+ # HACK: so we can just pass a Fixnum or a Register
+ offset = offset.value if offset.respond_to? :value
+
+ Word.new(self[offset], self, offset)
+ end
+
+ def write(offset, value)
+ # HACK: so we can just pass a Fixnum or a Register
+ offset = offset.value if offset.respond_to? :value
+
+ self[offset] = value
+ end
+
+ def reset
+ end
+
+ private
+ def [](key)
+ super(key)
+ end
+
+ def []=(key, value)
+ super(key, value)
+ end
+
+ # DOC
+ class Word
+ def initialize(value, memory = nil, offset = nil)
+ @value = value
+ @memory = memory
+ @offset = offset
+ end
+
+ def value
+ @value
+ end
+ alias_method :read, :value
+
+ def write(value)
+ @value = value
+ @memory.write(@offset, value)
+ end
+ end
+ end
+end
+
61 lib/dcpu16/operand.rb
@@ -0,0 +1,61 @@
+# TODO: Test && Refacoring
+module DCPU16
+ class Operand
+ class NotFound < StandardError; end
+
+ def self.new(cpu, value)
+ if (0x00..0x07).include?(value)
+ # register (A, B, C, X, Y, Z, I or J, in that order)
+ return cpu.registers[value]
+ elsif (0x08..0x0f).include?(value)
+ # [register]
+ register = cpu.registers[value - DCPU16::CPU::REGISTERS_COUNT]
+ return cpu.memory.read(register)
+ elsif (0x10..0x17).include?(value)
+ cpu.cycle += 1
+ offset = cpu.memory.read(cpu.PC).value
+ cpu.PC += 1
+ return cpu.memory.read(offset + cpu.registers[value - 0x10].read)
+ elsif value == 0x18
+ # POP / [SP++]
+ r = cpu.memory.read(cpu.SP)
+ cpu.SP += 1
+ return r
+ elsif value == 0x19
+ # PEEK / [SP]
+ return cpu.memory.read(cpu.SP)
+ elsif value == 0x1a
+ # PUSH / [--SP]
+ cpu.SP -= 1
+ cpu.memory.read(cpu.SP)
+ elsif value == 0x1b
+ # SP
+ return cpu.SP
+ elsif value == 0x1c
+ # PC
+ return cpu.PC
+ elsif value == 0x1d
+ # O
+ return cpu.O
+ elsif value == 0x1e
+ # [next word]
+ cpu.cycle += 1
+ offset = cpu.memory.read(cpu.PC)
+ cpu.PC += 1
+ return cpu.memory.read(offset)
+ elsif value == 0x1f
+ # next word (literal)
+ cpu.cycle += 1
+ r = cpu.memory.read(cpu.PC)
+ cpu.PC += 1
+ return r
+ elsif (0x20..0x3f).include?(value)
+ # literal value 0x00-0x1f (literal)
+ DCPU16::Literal.new(value - 0x20)
+ else
+ raise NotFound
+ end
+ end
+ end
+end
+
35 lib/dcpu16/register.rb
@@ -0,0 +1,35 @@
+module DCPU16
+ class Register
+ attr_reader :value
+
+ def initialize(value = 0x0)
+ @default_value = value
+ reset
+ end
+
+ def value
+ warn "[Register #{self}] No Value defined" unless @value
+ @value
+ end
+ alias_method :read, :value
+
+ def +(value)
+ @value += value
+ self
+ end
+
+ def -(value)
+ @value -= value
+ self
+ end
+
+ def write(value)
+ @value = value
+ end
+
+ def reset
+ @value = @default_value
+ end
+ end
+end
+
47 lib/dcpu16/support/debug.rb
@@ -0,0 +1,47 @@
+# Just a simple Debugger class
+module DCPU16
+ module Debug
+ # Debug-mode turned on?
+ def debug?
+ @debug ||= false
+ end
+
+ private
+ # Debug-Wrapper
+ def debug(msg = nil, &block)
+ return unless debug?
+
+ puts "\n[DEBUG] - #{caller.first}"
+ msg.each { |m| puts(m) } if msg.is_a?(Array)
+
+ if msg.is_a?(Hash)
+ msg.each do |k, v|
+ puts "[#{k.to_s}]"
+
+ if v.is_a?(Array)
+ v.each {|m| puts(m) }
+ else
+ puts v
+ end
+ end
+ elsif (msg.is_a?(String) || msg.is_a?(Symbol))
+ puts msg.to_s
+ end
+
+ yield if block_given?
+ puts "\n"
+ end
+
+ # DCPU16::CPU
+ def debug_state
+ debug do
+ puts "Cycle:\t#{@cycle}"
+ puts "PC:\t#{@PC}"
+ puts "SP:\t#{@SP}"
+ puts "O:\t#{@O}"
+ puts "R:\t#{@registers}"
+ end
+ end
+ end
+end
+
4 lib/dcpu16/version.rb
@@ -0,0 +1,4 @@
+module DCPU16
+ VERSION = "0.0.1"
+end
+
31 spec/integration/example1_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper.rb'
+
+describe DCPU16::CPU do
+ describe "quick example by notch" do
+ let(:dump) do
+ [ 0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d,
+ 0x7dc1, 0x001a, 0xa861, 0x7c01, 0x2000, 0x2161, 0x2000, 0x8463,
+ 0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018, 0x7dc1, 0x001a,
+ #0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000, 0x0000, 0x0000, 0x0000 ]
+ 0x9037, 0x61c1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 ]
+ end
+
+ subject { DCPU16::CPU.new(dump) }
+
+ # Just double-check that memory is loaded
+ specify { subject.memory.read(0x0).value.should == 0x7c01 }
+# specify { subject.memory.read(0x1b).value.should == 0x001a }
+
+ specify do
+ begin
+ subject.run.should raise_error(RuntimeError, /Reserved/)
+ rescue
+ end
+
+ subject.cycle.should == 104
+ subject.A.value.should == 0x2000
+ subject.X.value.should == 0x40
+ end
+ end
+end
+
2  spec/spec_helper.rb
@@ -0,0 +1,2 @@
+require 'dcpu16'
+
33 spec/unit/dcpu16_cpu_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper.rb'
+
+describe DCPU16::CPU do
+ describe "Register" do
+ its(:registers) { subject.length == 8 }
+ its(:registers) { subject[0] == be_kind_of(DCPU16::Register) }
+
+ its(:PC) { should be_kind_of(DCPU16::Register) }
+ its(:SP) { should be_kind_of(DCPU16::Register) }
+ its(:O) { should be_kind_of(DCPU16::Register) }
+ end
+
+ its(:cycle) { should == 0 }
+ its(:memory) { should be_kind_of(DCPU16::Memory) }
+
+
+ describe "#reset" do
+ it "resets its #cycle" do
+ subject.cycle = 2
+ subject.reset
+ subject.cycle.should == 0
+ end
+ it "resets its #memory" do
+ subject.memory.should_receive(:reset)
+ subject.reset
+ end
+ it "resets its #registers" do
+ subject.registers.each { |register| register.should_receive(:reset) }
+ subject.reset
+ end
+ end
+end
+
28 spec/unit/dcpu16_instruction_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper.rb'
+
+describe DCPU16::Instruction do
+ context "when initialized with 0x00" do
+ let(:word) { DCPU16::Memory::Word.new(0x00) }
+ subject { DCPU16::Instruction.new(word) }
+ its(:opcode) { should == 0 }
+ its(:a) { should == 0 }
+ its(:b) { should be_nil }
+ end
+
+ context "when initialized with 0x0001" do
+ let(:word) { DCPU16::Memory::Word.new(0x0001) }
+ subject { DCPU16::Instruction.new(word) }
+ its(:opcode) { should == 1 }
+ its(:a) { should == 0 }
+ its(:b) { should == 0 }
+ end
+
+ context "when initialized with 0xa861" do
+ let(:word) { DCPU16::Memory::Word.new(0xa861) }
+ subject { DCPU16::Instruction.new(word) }
+ its(:opcode) { should == 1 }
+ its(:a) { should == 0x6 }
+ its(:b) { should == 0xa + 0x20 } # Literal value (offset += 0x20)
+ end
+end
+
255 spec/unit/dcpu16_instructions_spec.rb
@@ -0,0 +1,255 @@
+require 'spec_helper.rb'
+
+describe DCPU16::Instructions do
+ subject { DCPU16::CPU.new }
+ let(:a) { 0x01 }
+ let(:b) { 0x02 }
+
+
+ describe "#set" do
+ it "sets a to b" do
+ subject.set(a, b).should == b
+ end
+ it "take 1 cycle, plus the cost of a and b" do
+ expect { subject.set(a, b) }.to change{subject.cycle}.by(1)
+ end
+ end
+
+
+ describe "#add" do
+ it "sets a to a+b" do
+ subject.add(2, 3).should == 5
+ end
+ context "with overflow" do
+ it "sets O to 0x0001" do
+ subject.add(0xFFFE, 2)
+ subject.O.value.should == 0x0001
+ end
+ end
+ context "without overflow" do
+ it "sets O to 0x0" do
+ subject.add(0xFFFE, 1)
+ subject.O.value.should == 0x0
+ end
+ end
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.add(a, b) }.to change{subject.cycle}.by(2)
+ end
+ end
+
+
+ describe "#sub" do
+ it "sets a to a-b" do
+ subject.sub(3, 2).should == 1
+ end
+ context "with underflow" do
+ it "set O to 0xffff" do
+ subject.sub(2, 3)
+ subject.O.value.should == 0xffff
+ end
+ end
+ context "without underflow" do
+ it "set O to 0x0" do
+ subject.sub(2, 2)
+ subject.O.value.should == 0x0
+ end
+ end
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.sub(a, b) }.to change{subject.cycle}.by(2)
+ end
+ end
+
+
+ describe "#mul" do
+ it "sets a to a*b" do
+ subject.mul(2, 3).should == 6
+ end
+ it "sets O to ((a*b)>>16)&0xffff" do
+ subject.mul(256, 256)
+ subject.O.value.should == ((256*256)>>16) & 0xFFFF
+ end
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.mul(a, b) }.to change{subject.cycle}.by(2)
+ end
+ end
+
+
+ describe "#div" do
+ it "sets a to a/b" do
+ subject.div(9, 3).should == 3
+ subject.div(8, 3).should == 2
+ end
+ context "when b != 0" do
+ it "sets O to ((a<<16)/b)&0xffff" do
+ subject.div(1, 2)
+ subject.O.value.should == ((1 << 16)/2) & 0xFFFF
+ end
+ end
+ context "when b == 0" do
+ it "sets a and O to 0" do
+ subject.div(3, 0).should == 0
+ subject.O.value.should == 0
+ end
+ end
+ it "take 3 cycles, plus the cost of a and b" do
+ expect { subject.div(a, b) }.to change{subject.cycle}.by(3)
+ end
+ end
+
+
+ describe "#mod" do
+ context "when b != 0" do
+ it "sets a to a % b" do
+ subject.mod(3, 2).should == 1
+ end
+ end
+ context "when b == 0" do
+ it "sets a to 0" do
+ subject.mod(3, 0).should == 0
+ end
+ end
+ it "take 3 cycles, plus the cost of a and b" do
+ expect { subject.mod(a, b) }.to change{subject.cycle}.by(3)
+ end
+ end
+
+
+ describe "#shl" do
+ it "sets a to a<<b" do
+ subject.shl(4, 2).should == 16
+ end
+ it "sets O to ((a<<b)>>16)&0xffff" do
+ subject.shl(1, 17)
+ subject.O.value.should == ((1 << 17) >> 16)&0xffff
+ end
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.shl(a, b) }.to change{subject.cycle}.by(2)
+ end
+ end
+
+
+ describe "#shr" do
+ it "sets a to a>>b" do
+ subject.shr(4, 2).should == 1
+ end
+ it "sets O to ((a<<16)>>b)&0xffff" do
+ subject.shr(2, 2)
+ subject.O.value.should == ((2 << 16) >> 2) & 0xffff
+ end
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.shr(a, b) }.to change{subject.cycle}.by(2)
+ end
+ end
+
+
+ describe "#and" do
+ it "sets a to a&b" do
+ subject.and(1, 2).should == 0
+ subject.and(1, 3).should == 1
+ end
+ it "take 1 cycles, plus the cost of a and b" do
+ expect { subject.and(a, b) }.to change{subject.cycle}.by(1)
+ end
+ end
+
+
+ describe "#bor" do
+ it "sets a to a|b" do
+ subject.bor(1, 2).should == 3
+ end
+ it "take 1 cycles, plus the cost of a and b" do
+ expect { subject.bor(a, b) }.to change{subject.cycle}.by(1)
+ end
+ end
+
+
+ describe "#xor" do
+ it "sets a to a^b" do
+ subject.xor(1, 3).should == 2
+ end
+ it "take 1 cycles, plus the cost of a and b" do
+ expect { subject.xor(a, b) }.to change{subject.cycle}.by(1)
+ end
+ end
+
+
+ describe "#ife" do
+ it "performs next instruction only if a==b" do
+ subject.ife(1, 1)
+ subject.skip.should be_false
+ subject.ife(1, 2)
+ subject.skip.should be_true
+ end
+ context "if test passes" do
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.ife(2, 2) }.to change{subject.cycle}.by(2)
+ end
+ end
+ context "if test fails" do
+ it "take 3 cycles, plus the cost of a and b" do
+ expect { subject.ife(2, 3) }.to change{subject.cycle}.by(3)
+ end
+ end
+ end
+
+
+ describe "#ifn" do
+ it "performs next instruction only if a!=b" do
+ subject.ifn(1, 1)
+ subject.skip.should be_true
+ subject.ifn(1, 2)
+ subject.skip.should be_false
+ end
+ context "if test passes" do
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.ifn(1, 2) }.to change{subject.cycle}.by(2)
+ end
+ end
+ context "if test fails" do
+ it "take 3 cycles, plus the cost of a and b" do
+ expect { subject.ifn(2, 2) }.to change{subject.cycle}.by(3)
+ end
+ end
+ end
+
+
+ describe "#ifg" do
+ it "performs next instruction only if a > b" do
+ subject.ifg(1, 2)
+ subject.skip.should be_true
+ subject.ifg(2, 1)
+ subject.skip.should be_false
+ end
+ context "if test passes" do
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.ifg(3, 2) }.to change{subject.cycle}.by(2)
+ end
+ end
+ context "if test fails" do
+ it "take 3 cycles, plus the cost of a and b" do
+ expect { subject.ifg(2, 3) }.to change{subject.cycle}.by(3)
+ end
+ end
+ end
+
+
+ describe "#ifb" do
+ it "performs next instruction only if (a&b)!=0" do
+ subject.ifb(1, 3)
+ subject.skip.should be_false
+ subject.ifb(1, 2)
+ subject.skip.should be_true
+ end
+ context "if test passes" do
+ it "take 2 cycles, plus the cost of a and b" do
+ expect { subject.ifb(1, 3) }.to change{subject.cycle}.by(2)
+ end
+ end
+ context "if test fails" do
+ it "take 3 cycles, plus the cost of a and b" do
+ expect { subject.ifb(1, 2) }.to change{subject.cycle}.by(3)
+ end
+ end
+ end
+end
+
19 spec/unit/dcpu16_memory_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper.rb'
+
+describe DCPU16::Memory do
+ its(:length) { should == 0x10000 }
+ specify { subject.read(0).value.should == 0 }
+
+ describe "#write" do
+ pending
+ end
+end
+
+#describe DCPU16::Memory::Word do
+# let(:memory) { DCPU16::Memory.new }
+
+# specify do
+# DCPU16::Memory::Word.new(2, memory, 0).should be_a_kind_of(DCPU16::Memory::Word)
+# end
+#end
+
79 spec/unit/dcpu16_operand_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper.rb'
+
+describe DCPU16::Instructions do
+ let(:cpu) { DCPU16::CPU.new }
+ subject { DCPU16::CPU.new } # HACK
+
+ context "when value is between 0x00 and 0x07" do
+ specify { DCPU16::Operand.new(cpu, 0x0).should == cpu.A }
+ specify { DCPU16::Operand.new(cpu, 0x1).should == cpu.B }
+ specify { DCPU16::Operand.new(cpu, 0x2).should == cpu.C }
+ specify { DCPU16::Operand.new(cpu, 0x3).should == cpu.X }
+ specify { DCPU16::Operand.new(cpu, 0x4).should == cpu.Y }
+ specify { DCPU16::Operand.new(cpu, 0x5).should == cpu.Z }
+ specify { DCPU16::Operand.new(cpu, 0x6).should == cpu.I }
+ specify { DCPU16::Operand.new(cpu, 0x7).should == cpu.J }
+ end
+
+ context "when value is between 0x08 and 0x17" do
+ before(:all) do
+ cpu.memory.write(0x1000, 0x2a)
+ cpu.registers.each { |r| r.write(0x1000) }
+ end
+
+ specify { DCPU16::Operand.new(cpu, 0x8).value.should == 0x2a }
+ specify { DCPU16::Operand.new(cpu, 0xF).value.should == 0x2a }
+ end
+
+
+ context "when value is between 0x10 and 0x17" do
+ pending
+ end
+
+ context "when value is 0x18" do
+ pending
+ end
+
+ context "when value is 0x19" do
+ pending
+ end
+
+ context "when value is 0x1A" do
+ pending
+ end
+
+ context "when value is 0x1B" do
+ pending
+ end
+
+ context "when value is 0x1C" do
+ pending
+ end
+
+ context "when value is 0x1D" do
+ pending
+ end
+
+ context "when value is 0x1E" do
+ pending
+ end
+
+ context "when value is 0x1F" do
+ pending
+ end
+
+ context "when value is between 0x20 and 0x3f" do
+ specify { DCPU16::Operand.new(cpu, 0x20).should be_a_kind_of(DCPU16::Literal) }
+ specify { DCPU16::Operand.new(cpu, 0x20).value.should == 0x0 }
+ specify { DCPU16::Operand.new(cpu, 0x3f).value.should == 0x1f }
+ end
+
+ context "when value is > 0x3f" do
+ specify do
+ expect {
+ DCPU16::Operand.new(cpu, 0x40)
+ }.to raise_error(DCPU16::Operand::NotFound)
+ end
+ end
+end
+
6 spec/unit/dcpu16_register_spec.rb
@@ -0,0 +1,6 @@
+require 'spec_helper.rb'
+
+describe DCPU16::Register do
+ subject { DCPU16::Register.new(0x00) }
+end
+
Please sign in to comment.
Something went wrong with that request. Please try again.