BadBoy-ruby-hackvm 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile ADDED
@@ -0,0 +1,61 @@
1
+ h1. Ruby Hack VM
2
+
3
+ h2. Information
4
+
5
+ h3. The Hack VM is a tiny, trivial, virtual machine.
6
+
7
+ Its purpose is to be used as a simple execution engine that can run very simple programs.
8
+
9
+ more Information on <a href="http://www.hacker.org/hvm/">http://www.hacker.org/hvm/</a>
10
+
11
+ The virtual machine executes a single program and terminates, either by reaching the end of the code, an '!' instruction, or because an exception was thrown during execution.
12
+ A program is represented by a string of single-character instructions.
13
+ The virtual machine starts with the first instruction, executes it, and moves on to the next instruction, etc...
14
+
15
+ The index of the current instruction is called the program counter.
16
+ The execution model is simple: the virtual machine has an operand stack, a memory buffer, and a call stack.
17
+
18
+ Each item on the operand stack or in memory is a cell that can hold a signed integer.
19
+ For implementation reasons, those integers are currently limited to 32 bits, but do not count on it, they could be large in future implementations.
20
+ The call stack is used to push the value of the program counter when jumping to a routine from which we want to return.
21
+
22
+ h2. Instructions
23
+
24
+ <pre>
25
+ Instruction Description
26
+ ' ' Do Nothing
27
+ 'p' Print S0 interpreted as an integer
28
+ 'P' Print S0 interpreted as an ASCII character (only the least significant 7 bits of the value are used)
29
+ '0' Push the value 0 on the stack
30
+ '1' Push the value 1 on the stack
31
+ '2' Push the value 2 on the stack
32
+ '3' Push the value 3 on the stack
33
+ '4' Push the value 4 on the stack
34
+ '5' Push the value 5 on the stack
35
+ '6' Push the value 6 on the stack
36
+ '7' Push the value 7 on the stack
37
+ '8' Push the value 8 on the stack
38
+ '9' Push the value 9 on the stack
39
+ '+' Push S1+S0
40
+ '-' Push S1-S0
41
+ '*' Push S1*S0
42
+ '/' Push S1/S0
43
+ ':' Push -1 if S1<S0, 0 if S1=S0, or 1 S1>S0
44
+ 'g' Add S0 to the program counter
45
+ '?' Add S0 to the program counter if S1 is 0
46
+ 'c' Push the program counter on the call stack and set the program counter to S0
47
+ '$' Set the program counter to the value pop'ed from the call stack
48
+ '<' Push the value of memory cell S0
49
+ '>' Store S1 into memory cell S0
50
+ '^' Push a copy of S<S0+1> (ex: 0^ duplicates S0)
51
+ 'v' Remove S<S0+1> from the stack and push it on top (ex: 1v swaps S0 and S1)
52
+ 'd' Drop S0
53
+ '!' Terminate the program
54
+ </pre>
55
+
56
+
57
+ h2. Examples
58
+
59
+ Input: "78*p" Output: 56
60
+
61
+ Input: "123451^2v5:4?9p2g8pppppp" Output: 945321
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'echoe'
3
+
4
+ Echoe.new('ruby-hackvm', '0.1.0') do |p|
5
+ p.description = "a virtual machine for hackers."
6
+ p.url = "http://github.com/BadBoy/ruby-hackvm"
7
+ p.author = "BadBoy_"
8
+ p.email = ""
9
+ p.ignore_pattern = ["tmp/*", "script/*"]
10
+ p.development_dependencies = []
11
+ end
12
+
data/bin/hackvm.rb ADDED
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # http://www.hacker.org/hvm/
4
+
5
+ class HackVM
6
+ class StackUnderFlow < Exception; end
7
+ class IntegerOverflow < Exception; end
8
+ class InitMemoryToBig < Exception; end
9
+ class OutOfStackException < Exception; end
10
+ class OutOfCodeBoundsException < Exception; end
11
+ class TooManyCyclesException < Exception; end
12
+ class WrongOpCode < Exception; end
13
+ class MemoryReadAccessViolation < Exception; end
14
+ class MemoryWriteAccessViolation < Exception; end
15
+
16
+ MAX_CYCLES=10000
17
+ MIN_INT=-(1<<63)
18
+ MAX_INT=(1<<63)-1
19
+
20
+ OPS = {
21
+ ' ' => lambda {},
22
+ "\n" => lambda {},
23
+ 'p' => :printi,
24
+ 'P' => :printc,
25
+ '0' => [:push, 0],
26
+ '1' => [:push, 1],
27
+ '2' => [:push, 2],
28
+ '3' => [:push, 3],
29
+ '4' => [:push, 4],
30
+ '5' => [:push, 5],
31
+ '6' => [:push, 6],
32
+ '7' => [:push, 7],
33
+ '8' => [:push, 8],
34
+ '9' => [:push, 9],
35
+ '+' => :add,
36
+ '-' => :sub,
37
+ '*' => :mul,
38
+ '/' => :div,
39
+ ':' => :cmp,
40
+ 'g' => :goto,
41
+ '?' => :goto_if_zero,
42
+ 'c' => :call,
43
+ '$' => :doreturn,
44
+ '<' => :peek,
45
+ '>' => :poke,
46
+ '^' => :pick,
47
+ 'v' => :roll,
48
+ 'd' => :drop,
49
+ '!' => :exit
50
+ }
51
+
52
+ def initialize
53
+ @memory = [0]*16384
54
+ @callstack = []
55
+ @operandstack = []
56
+ @programcounter = 0
57
+ @cyclecounter = 0
58
+ end
59
+
60
+ def init(memory)
61
+ if memory.size > 16384
62
+ raise InitMemoryToBig
63
+ end
64
+ memory.each_with_index do |e, i|
65
+ @memory[i] = e
66
+ end
67
+ end
68
+
69
+ def execute(code, trace)
70
+ codel = code.size
71
+ @codel = codel
72
+ while @programcounter != codel
73
+ op_code = code[@programcounter, 1]
74
+ STDERR.print('@'+@programcounter.to_s+' '+op_code) if trace
75
+ @programcounter += 1
76
+ @cyclecounter += 1
77
+ if @cyclecounter > MAX_CYCLES
78
+ raise TooManyCyclesException, 'too many cycles'
79
+ end
80
+ if OPS[op_code]
81
+ if OPS[op_code].kind_of? Symbol
82
+ method(OPS[op_code]).call
83
+ elsif OPS[op_code].kind_of? Proc
84
+ OPS[op_code].call
85
+ elsif OPS[op_code].kind_of? Array
86
+ m = method(OPS[op_code][0])
87
+ m.call(OPS[op_code][1])
88
+ else
89
+ raise WrongOpCode, "wrong op-code '#{op_code}'"
90
+ end
91
+ else
92
+ raise WrongOpCode, "wrong op-code '#{op_code}'"
93
+ end
94
+ if @programcounter < 0 || @programcounter > codel
95
+ raise OutOfCodeBoundsException, 'out of code bounds'
96
+ end
97
+ STDERR.puts(' ['+@operandstack.join(',')+']') if trace
98
+ end
99
+ puts
100
+
101
+ rescue Exception => e
102
+ STDERR.puts("!ERROR: exception while executing I=#{op_code} PC=#{@programcounter-1} STACK_SIZE=#{@operandstack.size}")
103
+ puts e
104
+ Kernel.exit(1)
105
+ end
106
+
107
+ private
108
+ def push(v)
109
+ if v < MIN_INT || v > MAX_INT
110
+ raise IntegerOverflow
111
+ end
112
+ @operandstack.push(v)
113
+ end
114
+
115
+ def pop
116
+ if @operandstack.size == 0
117
+ raise StackUnderFlow
118
+ end
119
+ @operandstack.pop
120
+ end
121
+
122
+ def printc
123
+ print( pop.chr )
124
+ end
125
+
126
+ def printi
127
+ print pop
128
+ end
129
+
130
+ def add
131
+ push(pop+pop)
132
+ end
133
+
134
+ def sub
135
+ a = pop
136
+ b = pop
137
+ push(b-a)
138
+ end
139
+
140
+ def mul
141
+ push(pop*pop)
142
+ end
143
+
144
+ def div
145
+ a = pop
146
+ b = pop
147
+ push(b/a)
148
+ end
149
+
150
+ def cmp
151
+ a = pop
152
+ b = pop
153
+ push( b<=>a )
154
+ end
155
+
156
+ def goto
157
+ @programcounter += pop
158
+ end
159
+
160
+ def goto_if_zero
161
+ offset = pop
162
+ @programcounter += offset if pop == 0
163
+ end
164
+
165
+ def call
166
+ @callstack.push(@programcounter)
167
+ @programcounter = pop
168
+ end
169
+
170
+ def doreturn
171
+ @programcounter = @callstack.pop
172
+ end
173
+
174
+ def peek
175
+ addr = pop
176
+ if addr < 0 || addr >= @memory.size
177
+ raise MemoryReadAccessViolation, 'memory read access violation @'+addr.to_s
178
+ end
179
+ push(@memory[addr])
180
+ end
181
+
182
+ def poke
183
+ addr = pop
184
+ if addr < 0 || addr >= @memory.size
185
+ raise MemoryWriteAccessViolation, 'memory write access violation @'+addr.to_s
186
+ end
187
+ @memory[addr] = pop
188
+ end
189
+
190
+ def pick
191
+ where = pop
192
+ if where < 0 || where >= @operandstack.size
193
+ raise OutOfStackException, 'out of stack @'+where.to_s
194
+ end
195
+ push(@operandstack[-1-where])
196
+ end
197
+
198
+ def roll
199
+ where = pop
200
+ if where < 0 || where >= @operandstack.size
201
+ raise OutOfStackException, 'out of stack @'+where.to_s
202
+ end
203
+ v = @perandstack[-1-where]
204
+ @perandStack.delete_at(-1-where)
205
+ push(v)
206
+ end
207
+
208
+ def drop
209
+ pop
210
+ end
211
+
212
+ def exit
213
+ @programcounter = @codel
214
+ end
215
+ end
216
+
217
+ require 'optparse'
218
+
219
+ trace = false
220
+ memory = []
221
+ ARGV.options do |o|
222
+ o.banner = "hackvm.rb [--init <init-mem-filename>] [--trace] <code-filename>\n"
223
+ o.banner += "The format for the initial memory file is: cell0,cell1,...\n"
224
+ o.banner += "More on http://www.hacker.org/hvm/"
225
+ o.separator('')
226
+
227
+ o.on('-t', '--trace', 'trace commands') do |t|
228
+ trace = t
229
+ end
230
+
231
+ o.on('-i', '--init FILE', 'init-mem-filename') do |i|
232
+ f = File.read(i)
233
+ memory = f.chomp.split(',').map{|e|Integer(e)}
234
+ end
235
+ end
236
+ begin
237
+ ARGV.parse!
238
+ rescue OptionParser::InvalidOption => e
239
+ STDERR.puts "!ERROR: #{e}"
240
+ exit 1
241
+ end
242
+
243
+ raise "not enough arguments" if ARGV.size < 1
244
+ hvm = HackVM.new
245
+ hvm.execute(File.read(ARGV[0]), trace)
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{ruby-hackvm}
3
+ s.version = "0.1.0"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["BadBoy_"]
7
+ s.date = %q{2008-11-13}
8
+ s.default_executable = %q{hackvm.rb}
9
+ s.description = %q{a virtual machine for hackers.}
10
+ s.email = %q{}
11
+ s.executables = ["hackvm.rb"]
12
+ s.extra_rdoc_files = ["bin/hackvm.rb", "README.textile"]
13
+ s.files = ["samples/helloworld.hvm", "Rakefile", "ruby-hackvm.gemspec", "bin/hackvm.rb", "Manifest", "README.textile"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/BadBoy/ruby-hackvm}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ruby-hackvm", "--main", "README.textile"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{ruby-hackvm}
19
+ s.rubygems_version = %q{1.2.0}
20
+ s.summary = %q{a virtual machine for hackers.}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if current_version >= 3 then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ 98*P
2
+ 99*29*2++P
3
+ 99*39*+P
4
+ 99*39*+P
5
+ 99*47*2++P
6
+ 48*52*+2+P
7
+ 48*P
8
+ 99*6+P
9
+ 99*47*2++P
10
+ 99*47*5++P
11
+ 99*39*+P
12
+ 99*29*1++P
13
+ 65+3*P
14
+
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: BadBoy-ruby-hackvm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - BadBoy_
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-13 00:00:00 -08:00
13
+ default_executable: hackvm.rb
14
+ dependencies: []
15
+
16
+ description: a virtual machine for hackers.
17
+ email: ""
18
+ executables:
19
+ - hackvm.rb
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - bin/hackvm.rb
24
+ - README.textile
25
+ files:
26
+ - samples/helloworld.hvm
27
+ - Rakefile
28
+ - ruby-hackvm.gemspec
29
+ - bin/hackvm.rb
30
+ - Manifest
31
+ - README.textile
32
+ has_rdoc: true
33
+ homepage: http://github.com/BadBoy/ruby-hackvm
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --line-numbers
37
+ - --inline-source
38
+ - --title
39
+ - Ruby-hackvm
40
+ - --main
41
+ - README.textile
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "1.2"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: ruby-hackvm
59
+ rubygems_version: 1.2.0
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: a virtual machine for hackers.
63
+ test_files: []
64
+