gruesome 0.0.3 → 0.0.4
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.md +1 -2
- data/lib/gruesome/cli.rb +62 -62
- data/lib/gruesome/logo.rb +8 -8
- data/lib/gruesome/machine.rb +11 -11
- data/lib/gruesome/version.rb +1 -1
- data/lib/gruesome/z/abbreviation_table.rb +17 -17
- data/lib/gruesome/z/decoder.rb +273 -273
- data/lib/gruesome/z/dictionary.rb +144 -144
- data/lib/gruesome/z/header.rb +40 -40
- data/lib/gruesome/z/instruction.rb +42 -42
- data/lib/gruesome/z/machine.rb +51 -50
- data/lib/gruesome/z/memory.rb +301 -232
- data/lib/gruesome/z/object_table.rb +424 -424
- data/lib/gruesome/z/opcode.rb +489 -491
- data/lib/gruesome/z/opcode_class.rb +9 -9
- data/lib/gruesome/z/operand_type.rb +10 -10
- data/lib/gruesome/z/processor.rb +378 -361
- data/lib/gruesome/z/zscii.rb +307 -307
- data/lib/gruesome.rb +3 -3
- data/spec/z/memory_spec.rb +72 -72
- data/spec/z/processor_spec.rb +1883 -1883
- metadata +3 -3
data/lib/gruesome/z/memory.rb
CHANGED
@@ -33,236 +33,305 @@ require_relative 'header'
|
|
33
33
|
# will specify an exact number.
|
34
34
|
|
35
35
|
module Gruesome
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
36
|
+
module Z
|
37
|
+
|
38
|
+
# This class holds the memory for the virtual machine
|
39
|
+
class Memory
|
40
|
+
attr_accessor :program_counter
|
41
|
+
attr_reader :num_locals
|
42
|
+
|
43
|
+
def initialize(contents, save_file_name)
|
44
|
+
@call_stack = []
|
45
|
+
@save_file_name = save_file_name
|
46
|
+
@stack = []
|
47
|
+
@memory = contents
|
48
|
+
@num_locals = 0
|
49
|
+
|
50
|
+
# Get the header information
|
51
|
+
@header = Header.new(@memory)
|
52
|
+
@program_counter = @header.entry
|
53
|
+
|
54
|
+
# With the header info, discover the bounds of each memory region
|
55
|
+
@dyn_base = 0x0
|
56
|
+
@dyn_limit = @header.static_mem_base
|
57
|
+
|
58
|
+
# Cannot Write to Static Memory
|
59
|
+
@static_base = @header.static_mem_base
|
60
|
+
@static_limit = @memory.length
|
61
|
+
|
62
|
+
# Cannot Access High Memory
|
63
|
+
@high_base = @header.high_mem_base
|
64
|
+
@high_limit = @memory.length
|
65
|
+
|
66
|
+
# Error if high memory overlaps dynamic memory
|
67
|
+
if @high_base < @dyn_limit
|
68
|
+
# XXX: ERROR
|
69
|
+
end
|
70
|
+
|
71
|
+
# Check machine endianess
|
72
|
+
@endian = [1].pack('S')[0] == 1 ? 'little' : 'big'
|
73
|
+
end
|
74
|
+
|
75
|
+
def packed_address_to_byte_address(address)
|
76
|
+
if @header.version <=3
|
77
|
+
address * 2
|
78
|
+
else
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sets up the environment for a new routine
|
83
|
+
def push_routine(return_addr, num_locals, destination)
|
84
|
+
# pushes the stack onto the call stack
|
85
|
+
@call_stack.push @num_locals
|
86
|
+
@call_stack.push destination
|
87
|
+
@call_stack.push @stack
|
88
|
+
|
89
|
+
# empties the current stack
|
90
|
+
@stack = Array.new()
|
91
|
+
|
92
|
+
# pushes the return address onto the stack
|
93
|
+
@stack.push(return_addr)
|
94
|
+
|
95
|
+
# push locals
|
96
|
+
num_locals.times do
|
97
|
+
@stack.push 0
|
98
|
+
end
|
99
|
+
|
100
|
+
@num_locals = num_locals
|
101
|
+
end
|
102
|
+
|
103
|
+
# Tears down the environment for the current routine
|
104
|
+
def pop_routine()
|
105
|
+
# return the return address
|
106
|
+
return_addr = @stack[0]
|
107
|
+
@stack = @call_stack.pop
|
108
|
+
destination = @call_stack.pop
|
109
|
+
@num_locals = @call_stack.pop
|
110
|
+
|
111
|
+
{:destination => destination, :return_address => return_addr}
|
112
|
+
end
|
113
|
+
|
114
|
+
def readb(address)
|
115
|
+
if address < @high_base
|
116
|
+
force_readb(address)
|
117
|
+
else
|
118
|
+
# XXX: Access violation
|
119
|
+
raise "Access Violation accessing $" + sprintf("%04x", address)
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def readw(address)
|
125
|
+
if (address + 1) < @high_base
|
126
|
+
force_readw(address)
|
127
|
+
else
|
128
|
+
# XXX: Access violation
|
129
|
+
raise "Access Violation accessing $" + sprintf("%04x", address)
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def writeb(address, value)
|
135
|
+
if address < @static_base
|
136
|
+
force_writeb(address, value)
|
137
|
+
else
|
138
|
+
# XXX: Access violation
|
139
|
+
raise "Access Violation (W) accessing $" + sprintf("%04x", address)
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def writew(address, value)
|
145
|
+
if (address + 1) < @static_base
|
146
|
+
force_writew(address, value)
|
147
|
+
else
|
148
|
+
# XXX: Access violation
|
149
|
+
raise "Access Violation (W) accessing $" + sprintf("%04x", address)
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def force_readb(address)
|
155
|
+
if address < @memory.size
|
156
|
+
@memory.getbyte(address)
|
157
|
+
else
|
158
|
+
# XXX: Access Violation
|
159
|
+
raise "Major Access Violation accessing $" + sprintf("%04x", address)
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def force_readw(address)
|
165
|
+
if (address + 1) < @memory.size
|
166
|
+
if @endian == 'little'
|
167
|
+
(@memory.getbyte(address+1) << 8) | @memory.getbyte(address)
|
168
|
+
else
|
169
|
+
(@memory.getbyte(address) << 8) | @memory.getbyte(address+1)
|
170
|
+
end
|
171
|
+
else
|
172
|
+
# XXX: Access Violation
|
173
|
+
raise "Major Access Violation accessing $" + sprintf("%04x", address)
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def force_writeb(address, value)
|
179
|
+
if address < @memory.size
|
180
|
+
@memory.setbyte(address, (value & 255))
|
181
|
+
else
|
182
|
+
# XXX: Access Violation
|
183
|
+
raise "Major Access (W) Violation accessing $" + sprintf("%04x", address)
|
184
|
+
nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def force_writew(address, value)
|
189
|
+
if (address + 1) < @memory.size
|
190
|
+
low_byte = value & 255
|
191
|
+
high_byte = (value >> 8) & 255
|
192
|
+
|
193
|
+
if @endian == 'little'
|
194
|
+
tmp = high_byte
|
195
|
+
high_byte = low_byte
|
196
|
+
low_byte = tmp
|
197
|
+
end
|
198
|
+
|
199
|
+
@memory.setbyte(address, high_byte)
|
200
|
+
@memory.setbyte(address+1, low_byte)
|
201
|
+
else
|
202
|
+
# XXX: Access Violation
|
203
|
+
raise "Major Access (W) Violation accessing $" + sprintf("%04x", address)
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def contents
|
209
|
+
@memory
|
210
|
+
end
|
211
|
+
|
212
|
+
# Read from variable number index
|
213
|
+
def readv(index)
|
214
|
+
if index == 0
|
215
|
+
# pop from stack
|
216
|
+
@stack.pop
|
217
|
+
elsif index >= 16
|
218
|
+
index -= 16
|
219
|
+
readw(@header.global_var_addr + (index*2))
|
220
|
+
elsif index <= @num_locals
|
221
|
+
@stack[index]
|
222
|
+
else
|
223
|
+
# XXX: Error
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Write value to variable number index
|
228
|
+
def writev(index, value)
|
229
|
+
value &= 65535
|
230
|
+
if index == 0
|
231
|
+
# push to stack
|
232
|
+
@stack.push value
|
233
|
+
elsif index >= 16
|
234
|
+
index -= 16
|
235
|
+
writew(@header.global_var_addr + (index*2), value)
|
236
|
+
elsif index <= @num_locals
|
237
|
+
@stack[index] = value
|
238
|
+
else
|
239
|
+
# XXX: Error
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def force_readzstr(index, max_len = -1)
|
244
|
+
chrs = []
|
245
|
+
continue = true
|
246
|
+
orig_index = index
|
247
|
+
|
248
|
+
until continue == false do
|
249
|
+
if max_len != -1 and (index + 2 - orig_index) > max_len
|
250
|
+
break
|
251
|
+
end
|
252
|
+
|
253
|
+
byte1 = force_readb(index)
|
254
|
+
byte2 = force_readb(index+1)
|
255
|
+
|
256
|
+
index += 2
|
257
|
+
|
258
|
+
chrs << ((byte1 >> 2) & 0b11111)
|
259
|
+
chrs << (((byte1 & 0b11) << 3) | (byte2 >> 5))
|
260
|
+
chrs << (byte2 & 0b11111)
|
261
|
+
|
262
|
+
continue = (byte1 & 0b10000000) == 0
|
263
|
+
end
|
264
|
+
|
265
|
+
return [index - orig_index, chrs]
|
266
|
+
end
|
267
|
+
|
268
|
+
def save
|
269
|
+
# Save contents of dynamic memory to disk
|
270
|
+
File.open(@save_file_name, "wb+") do |f|
|
271
|
+
f.puts @program_counter
|
272
|
+
f.puts (@call_stack.size / 3)
|
273
|
+
@call_stack.each_with_index do |call_stack, i|
|
274
|
+
if (i % 3) == 0 or (i % 3) == 1
|
275
|
+
f.puts call_stack
|
276
|
+
else
|
277
|
+
# this is stack
|
278
|
+
stack = call_stack
|
279
|
+
f.puts stack.size
|
280
|
+
stack.each do |stack_entry|
|
281
|
+
f.puts stack_entry
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
f.puts @num_locals
|
286
|
+
f.puts @stack.size
|
287
|
+
@stack.each do |stack_entry|
|
288
|
+
f.puts stack_entry
|
289
|
+
end
|
290
|
+
|
291
|
+
@dyn_limit.times do |i|
|
292
|
+
f.write force_readb(i).chr
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def restore
|
298
|
+
# Restore, if it can, the contents of memory from disk
|
299
|
+
File.open(@save_file_name, "rb") do |f|
|
300
|
+
@call_stack = []
|
301
|
+
|
302
|
+
@program_counter = f.readline.to_i
|
303
|
+
call_stack_size = f.readline.to_i
|
304
|
+
|
305
|
+
call_stack_size.times do
|
306
|
+
num_locals = f.readline.to_i
|
307
|
+
destination = f.readline.to_i
|
308
|
+
stack_size = f.readline.to_i
|
309
|
+
|
310
|
+
stack = []
|
311
|
+
stack_size.times do |i|
|
312
|
+
stack.push f.readline.to_i
|
313
|
+
end
|
314
|
+
|
315
|
+
@call_stack.push num_locals
|
316
|
+
@call_stack.push destination
|
317
|
+
@call_stack.push stack
|
318
|
+
end
|
319
|
+
|
320
|
+
@num_locals = f.readline.to_i
|
321
|
+
stack_size = f.readline.to_i
|
322
|
+
|
323
|
+
@stack = []
|
324
|
+
stack_size.times do |i|
|
325
|
+
@stack.push f.readline.to_i
|
326
|
+
end
|
327
|
+
|
328
|
+
i = 0
|
329
|
+
f.read.each_byte do |b|
|
330
|
+
force_writeb(i, b)
|
331
|
+
i += 1
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
268
337
|
end
|