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/processor.rb
CHANGED
@@ -9,390 +9,407 @@ require_relative 'dictionary'
|
|
9
9
|
require_relative 'object_table'
|
10
10
|
|
11
11
|
module Gruesome
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
module Z
|
13
|
+
class Processor
|
14
|
+
def initialize(memory)
|
15
|
+
@memory = memory
|
16
|
+
@header = Header.new(@memory.contents)
|
17
|
+
@abbreviation_table = AbbreviationTable.new(@memory)
|
18
|
+
@object_table = ObjectTable.new(@memory)
|
19
|
+
@dictionary = Dictionary.new(@memory)
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
def routine_call(address, arguments, result_variable = nil)
|
23
|
+
if address == 0
|
24
|
+
# special case, do not call, simply return false
|
25
|
+
if result_variable != nil
|
26
|
+
@memory.writev(result_variable, 0)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
return_addr = @memory.program_counter
|
30
|
+
@memory.program_counter = address
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
# read routine
|
33
|
+
num_locals = @memory.force_readb(@memory.program_counter)
|
34
|
+
@memory.program_counter += 1
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
# create environment
|
37
|
+
@memory.push_routine(return_addr, num_locals, result_variable)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
39
|
+
if @header.version <= 4
|
40
|
+
# read initial values when version 1-4
|
41
|
+
(1..num_locals).each do |i|
|
42
|
+
@memory.writev(i, @memory.force_readw(@memory.program_counter))
|
43
|
+
@memory.program_counter += 2
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# reset local vars to 0
|
47
|
+
(1..num_locals).each do |i|
|
48
|
+
@memory.writev(i, 0)
|
49
|
+
end
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
52
|
+
# copy arguments over into locals
|
53
|
+
idx = 1
|
54
|
+
arguments.each do |i|
|
55
|
+
@memory.writev(idx, i)
|
56
|
+
idx += 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
60
|
|
61
|
-
|
62
|
-
|
61
|
+
def routine_return(result)
|
62
|
+
frame = @memory.pop_routine
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
if frame[:destination] != nil
|
65
|
+
@memory.writev(frame[:destination], result)
|
66
|
+
end
|
67
|
+
@memory.program_counter = frame[:return_address]
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
|
70
|
+
def branch(branch_to, branch_on, result)
|
71
|
+
if (result == branch_on)
|
72
|
+
if branch_to == 0
|
73
|
+
routine_return(0)
|
74
|
+
elsif branch_to == 1
|
75
|
+
routine_return(1)
|
76
|
+
else
|
77
|
+
@memory.program_counter = branch_to
|
78
|
+
# @memory.program_counter -= 2
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
83
|
+
def execute(instruction)
|
84
|
+
# Pull values from the variables if needed by the instruction
|
85
|
+
# there are some exceptions for variable-by-reference instructions
|
86
|
+
operands = instruction.operands.each_with_index.map do |operand, idx|
|
87
|
+
if idx == 0 and Opcode.is_variable_by_reference?(instruction.opcode, @header.version)
|
88
|
+
operand
|
89
|
+
elsif instruction.types[idx] == OperandType::VARIABLE
|
90
|
+
@memory.readv(operand)
|
91
|
+
else
|
92
|
+
operand
|
93
|
+
end
|
94
|
+
end
|
95
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
|
-
|
96
|
+
case instruction.opcode
|
97
|
+
when Opcode::ART_SHIFT
|
98
|
+
places = unsigned_to_signed(operands[1])
|
99
|
+
if places < 0
|
100
|
+
@memory.writev(instruction.destination, unsigned_to_signed(operands[0]) >> places.abs)
|
101
|
+
else
|
102
|
+
@memory.writev(instruction.destination, operands[0] << places)
|
103
|
+
end
|
104
|
+
when Opcode::CALL, Opcode::CALL_1N
|
105
|
+
routine_call(@memory.packed_address_to_byte_address(operands[0]), operands[1..-1], instruction.destination)
|
106
|
+
when Opcode::CLEAR_ATTR
|
107
|
+
@object_table.object_clear_attribute(operands[0], operands[1])
|
108
|
+
when Opcode::DEC
|
109
|
+
@memory.writev(operands[0], unsigned_to_signed(@memory.readv(operands[0])) - 1)
|
110
|
+
when Opcode::INC
|
111
|
+
@memory.writev(operands[0], unsigned_to_signed(@memory.readv(operands[0])) + 1)
|
112
|
+
when Opcode::INC_CHK
|
113
|
+
new_value = unsigned_to_signed(@memory.readv(operands[0])) + 1
|
114
|
+
@memory.writev(operands[0], new_value)
|
115
|
+
result = new_value > unsigned_to_signed(operands[1])
|
116
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
117
|
+
when Opcode::DEC_CHK
|
118
|
+
new_value = unsigned_to_signed(@memory.readv(operands[0])) - 1
|
119
|
+
@memory.writev(operands[0], new_value)
|
120
|
+
result = new_value < unsigned_to_signed(operands[1])
|
121
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
122
|
+
when Opcode::INSERT_OBJ
|
123
|
+
@object_table.object_insert_object(operands[1], operands[0])
|
124
|
+
when Opcode::GET_CHILD
|
125
|
+
child = @object_table.object_get_child(operands[0])
|
126
|
+
@memory.writev(instruction.destination, child)
|
127
|
+
result = child != 0
|
128
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
129
|
+
when Opcode::GET_PARENT
|
130
|
+
parent = @object_table.object_get_parent(operands[0])
|
131
|
+
@memory.writev(instruction.destination, parent)
|
132
|
+
when Opcode::GET_PROP
|
133
|
+
prop = @object_table.object_get_property_word(operands[0], operands[1])
|
134
|
+
@memory.writev(instruction.destination, prop)
|
135
|
+
when Opcode::GET_PROP_ADDR
|
136
|
+
prop = @object_table.object_get_property_addr(operands[0], operands[1])
|
137
|
+
@memory.writev(instruction.destination, prop)
|
138
|
+
when Opcode::GET_PROP_LEN
|
139
|
+
if operands[0] == 0
|
140
|
+
result = 0
|
141
|
+
else
|
142
|
+
result = @object_table.object_get_property_data_size_from_address(operands[0])
|
143
|
+
end
|
144
|
+
@memory.writev(instruction.destination, result)
|
145
|
+
when Opcode::GET_SIBLING
|
146
|
+
sibling = @object_table.object_get_sibling(operands[0])
|
147
|
+
@memory.writev(instruction.destination, sibling)
|
148
|
+
result = sibling != 0
|
149
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
150
|
+
when Opcode::JUMP, Opcode::PIRACY
|
151
|
+
@memory.program_counter += unsigned_to_signed(operands[0])
|
152
|
+
@memory.program_counter -= 2
|
153
|
+
when Opcode::JE
|
154
|
+
result = operands[1..-1].inject(false) { |result, element|
|
155
|
+
result | (operands[0] == element)
|
156
|
+
}
|
157
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
158
|
+
when Opcode::JG
|
159
|
+
result = operands[1..-1].inject(false) { |result, element|
|
160
|
+
result | (unsigned_to_signed(operands[0]) > unsigned_to_signed(element))
|
161
|
+
}
|
162
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
163
|
+
when Opcode::JIN
|
164
|
+
result = @object_table.object_get_parent(operands[0]) == operands[1]
|
165
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
166
|
+
when Opcode::JL
|
167
|
+
result = operands[1..-1].inject(false) { |result, element|
|
168
|
+
result | (unsigned_to_signed(operands[0]) < unsigned_to_signed(element))
|
169
|
+
}
|
170
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
171
|
+
when Opcode::JZ
|
172
|
+
result = operands[0] == 0
|
173
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
174
|
+
when Opcode::LOAD
|
175
|
+
if operands[0] != instruction.destination
|
176
|
+
@memory.writev(instruction.destination, @memory.readv(operands[0]))
|
177
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
|
-
|
178
|
+
# make sure to re-push the value since LOAD does not
|
179
|
+
# change the stack but uses the value directly
|
180
|
+
if operands[0] == 0
|
181
|
+
@memory.writev(0, @memory.readv(instruction.destination))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
when Opcode::LOADB
|
185
|
+
@memory.writev(instruction.destination, @memory.readb(operands[0] + unsigned_to_signed(operands[1])))
|
186
|
+
when Opcode::LOADW
|
187
|
+
@memory.writev(instruction.destination, @memory.readw(operands[0] + unsigned_to_signed(operands[1])*2))
|
188
|
+
when Opcode::LOG_SHIFT
|
189
|
+
places = unsigned_to_signed(operands[1])
|
190
|
+
if places < 0
|
191
|
+
@memory.writev(instruction.destination, operands[0] >> places.abs)
|
192
|
+
else
|
193
|
+
@memory.writev(instruction.destination, operands[0] << places)
|
194
|
+
end
|
195
|
+
when Opcode::NOP
|
196
|
+
when Opcode::NEW_LINE
|
197
|
+
puts
|
198
|
+
when Opcode::POP
|
199
|
+
# get rid of the first item on stack
|
200
|
+
@memory.readv(0)
|
201
|
+
when Opcode::PRINT
|
202
|
+
print operands[0]
|
203
|
+
when Opcode::PRINT_ADDR
|
204
|
+
print ZSCII.translate(0, @header.version, @memory.force_readzstr(operands[0])[1], @abbreviation_table)
|
205
|
+
when Opcode::PRINT_CHAR
|
206
|
+
print ZSCII.translate_Zchar(operands[0])
|
207
|
+
when Opcode::PRINT_NUM
|
208
|
+
print unsigned_to_signed(operands[0]).to_s
|
209
|
+
when Opcode::PRINT_OBJ
|
210
|
+
print @object_table.object_short_text(operands[0])
|
211
|
+
when Opcode::PRINT_PADDR
|
212
|
+
str_addr = @memory.packed_address_to_byte_address(operands[0])
|
213
|
+
print ZSCII.translate(0, @header.version, @memory.force_readzstr(str_addr)[1], @abbreviation_table)
|
214
|
+
when Opcode::PRINT_RET
|
215
|
+
puts operands[0]
|
216
|
+
routine_return(1)
|
217
|
+
when Opcode::PULL
|
218
|
+
if @header.version == 6
|
219
|
+
# TODO: Version 6 PULL instruction
|
220
|
+
#
|
221
|
+
# stack to pull from is given as operand[0]
|
222
|
+
# instruction.destination is the destination resister
|
223
|
+
else
|
224
|
+
# pop value from stack
|
225
|
+
@memory.writev(operands[0], @memory.readv(0))
|
226
|
+
end
|
227
|
+
when Opcode::PUSH
|
228
|
+
# add value to stack
|
229
|
+
@memory.writev(0, operands[0])
|
230
|
+
when Opcode::PUT_PROP
|
231
|
+
object_id = operands[0]
|
232
|
+
prop_id = operands[1]
|
233
|
+
prop_value = operands[2]
|
234
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
|
-
|
235
|
+
@object_table.object_set_property_word(object_id, prop_id, prop_value)
|
236
|
+
when Opcode::RANDOM
|
237
|
+
value = unsigned_to_signed(operands[0])
|
238
|
+
if value < 0
|
239
|
+
# seed with value
|
240
|
+
srand(value)
|
241
|
+
result = 0
|
242
|
+
elsif value == 0
|
243
|
+
# seed with timestamp
|
244
|
+
srand()
|
245
|
+
result = 0
|
246
|
+
else
|
247
|
+
# pull random number from between 1 and value
|
248
|
+
result = rand(value-1) + 1
|
249
|
+
end
|
250
|
+
@memory.writev(instruction.destination, result)
|
251
|
+
when Opcode::REMOVE_OBJ
|
252
|
+
@object_table.object_remove_object(operands[0])
|
253
|
+
when Opcode::RESTORE
|
254
|
+
if @header.version <= 4
|
255
|
+
@memory.restore
|
256
|
+
if @header.version >= 4
|
257
|
+
@memory.writev(instruction.destination, 1)
|
258
|
+
end
|
259
|
+
branch(instruction.branch_to, instruction.branch_on, true)
|
260
|
+
end
|
261
|
+
when Opcode::RET
|
262
|
+
routine_return(operands[0])
|
263
|
+
when Opcode::RET_POPPED
|
264
|
+
routine_return(@memory.readv(0))
|
265
|
+
when Opcode::RTRUE
|
266
|
+
routine_return(1)
|
267
|
+
when Opcode::RFALSE
|
268
|
+
routine_return(0)
|
269
|
+
when Opcode::SAVE
|
270
|
+
if @header.version <= 4
|
271
|
+
@memory.save
|
272
|
+
if @header.version >= 4
|
273
|
+
@memory.writev(instruction.destination, 1)
|
274
|
+
end
|
275
|
+
branch(instruction.branch_to, instruction.branch_on, true)
|
276
|
+
end
|
277
|
+
when Opcode::SET_ATTR
|
278
|
+
@object_table.object_set_attribute(operands[0], operands[1])
|
279
|
+
when Opcode::SREAD
|
280
|
+
# read the maximum number of bytes in the text-buffer
|
281
|
+
max_bytes = @memory.force_readb(operands[0])
|
266
282
|
|
267
|
-
|
268
|
-
|
283
|
+
# address of the next byte in the text-buffer
|
284
|
+
addr = operands[0] + 1
|
269
285
|
|
270
|
-
|
271
|
-
|
286
|
+
# read in a line of input from stdin
|
287
|
+
line = $stdin.readline[0..-2]
|
272
288
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
289
|
+
# truncate line to fit the max characters given by text-buffer
|
290
|
+
if line.length > max_bytes
|
291
|
+
line = line[0..max_bytes]
|
292
|
+
end
|
277
293
|
|
278
|
-
|
279
|
-
|
294
|
+
# tokenize
|
295
|
+
lexed = @dictionary.tokenize(line)
|
280
296
|
|
281
|
-
|
282
|
-
|
297
|
+
# parse
|
298
|
+
parsed = @dictionary.parse(lexed)
|
283
299
|
|
284
|
-
|
285
|
-
|
300
|
+
# encode the line into a ZChar stream
|
301
|
+
str = ZSCII.encode_to_zchars(line, @header.version)
|
286
302
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
303
|
+
# write the text to the text-buffer
|
304
|
+
num_bytes = 1
|
305
|
+
first_position = 1
|
306
|
+
if @header.version >= 5
|
307
|
+
num_bytes += 1
|
308
|
+
@memory.force_writeb(addr, line.length)
|
309
|
+
first_position = 2
|
310
|
+
addr += 1
|
311
|
+
end
|
296
312
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
313
|
+
str.each do |zchr|
|
314
|
+
num_bytes += 1
|
315
|
+
if num_bytes > max_bytes
|
316
|
+
break
|
317
|
+
end
|
318
|
+
@memory.force_writeb(addr, zchr)
|
319
|
+
addr += 1
|
320
|
+
end
|
305
321
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
322
|
+
if @header.version < 5
|
323
|
+
# terminator
|
324
|
+
if num_bytes <= max_bytes
|
325
|
+
@memory.force_writeb(addr, 0)
|
326
|
+
end
|
327
|
+
end
|
312
328
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
329
|
+
# write the parse-buffer
|
330
|
+
max_bytes = @memory.force_readb(operands[1])
|
331
|
+
max_bytes = 2 + max_bytes * 4
|
332
|
+
addr = operands[1] + 1
|
333
|
+
num_bytes = 1
|
318
334
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
335
|
+
if num_bytes <= max_bytes
|
336
|
+
@memory.force_writeb(addr, lexed.length)
|
337
|
+
addr += 1
|
338
|
+
end
|
323
339
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
340
|
+
parsed.each do |token, index|
|
341
|
+
num_bytes += 4
|
342
|
+
if num_bytes > max_bytes
|
343
|
+
break
|
344
|
+
end
|
345
|
+
@memory.force_writew(addr, token[:address])
|
346
|
+
addr += 2
|
347
|
+
@memory.force_writeb(addr, token[:size])
|
348
|
+
addr += 1
|
349
|
+
@memory.force_writeb(addr, token[:position]+first_position)
|
350
|
+
addr += 1
|
351
|
+
end
|
352
|
+
when Opcode::TEST
|
353
|
+
result = (operands[0] & operands[1]) == operands[1]
|
354
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
355
|
+
when Opcode::TEST_ATTR
|
356
|
+
result = @object_table.object_has_attribute?(operands[0], operands[1])
|
357
|
+
branch(instruction.branch_to, instruction.branch_on, result)
|
358
|
+
when Opcode::ADD
|
359
|
+
@memory.writev(instruction.destination,
|
360
|
+
unsigned_to_signed(operands[0]) + unsigned_to_signed(operands[1]))
|
361
|
+
when Opcode::SUB
|
362
|
+
@memory.writev(instruction.destination,
|
363
|
+
unsigned_to_signed(operands[0]) - unsigned_to_signed(operands[1]))
|
364
|
+
when Opcode::MUL
|
365
|
+
@memory.writev(instruction.destination,
|
366
|
+
unsigned_to_signed(operands[0]) * unsigned_to_signed(operands[1]))
|
367
|
+
when Opcode::DIV
|
368
|
+
result = unsigned_to_signed(operands[0]).to_f / unsigned_to_signed(operands[1]).to_f
|
369
|
+
if result < 0
|
370
|
+
result = -(result.abs.floor)
|
371
|
+
else
|
372
|
+
result = result.floor
|
373
|
+
end
|
374
|
+
@memory.writev(instruction.destination, result.to_i)
|
375
|
+
when Opcode::MOD
|
376
|
+
a = unsigned_to_signed(operands[0])
|
377
|
+
b = unsigned_to_signed(operands[1])
|
378
|
+
result = a.abs % b.abs
|
379
|
+
if a < 0
|
380
|
+
result = -result
|
381
|
+
end
|
382
|
+
@memory.writev(instruction.destination, result.to_i)
|
383
|
+
when Opcode::NOT
|
384
|
+
@memory.writev(instruction.destination, ~(operands[0]))
|
385
|
+
when Opcode::OR
|
386
|
+
@memory.writev(instruction.destination, operands[0] | operands[1])
|
387
|
+
when Opcode::AND
|
388
|
+
@memory.writev(instruction.destination, operands[0] & operands[1])
|
389
|
+
when Opcode::STORE
|
390
|
+
if operands[0] == 0
|
391
|
+
@memory.readv(operands[0])
|
392
|
+
end
|
393
|
+
@memory.writev(operands[0], operands[1])
|
394
|
+
when Opcode::STOREB
|
395
|
+
@memory.writeb(operands[0] + unsigned_to_signed(operands[1]), operands[2])
|
396
|
+
when Opcode::STOREW
|
397
|
+
@memory.writew(operands[0] + unsigned_to_signed(operands[1])*2, operands[2])
|
398
|
+
else
|
399
|
+
puts instruction.opcode
|
400
|
+
raise "opcode " + Opcode.name(instruction.opcode, @header.version) + " not implemented"
|
401
|
+
end
|
402
|
+
end
|
386
403
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
404
|
+
def unsigned_to_signed(op)
|
405
|
+
sign = op & 0x8000
|
406
|
+
if sign != 0
|
407
|
+
-(65536 - op)
|
408
|
+
else
|
409
|
+
op
|
410
|
+
end
|
411
|
+
end
|
412
|
+
private :unsigned_to_signed
|
413
|
+
end
|
414
|
+
end
|
398
415
|
end
|