BadBoy-ruby-hackvm 0.1.0

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