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/decoder.rb
CHANGED
@@ -16,277 +16,277 @@ require_relative 'instruction'
|
|
16
16
|
require_relative 'zscii'
|
17
17
|
|
18
18
|
module Gruesome
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
19
|
+
module Z
|
20
|
+
|
21
|
+
# This is the instruction decoder
|
22
|
+
class Decoder
|
23
|
+
def initialize(memory)
|
24
|
+
@memory = memory
|
25
|
+
@instruction_cache = {}
|
26
|
+
@header = Header.new(@memory.contents)
|
27
|
+
@abbreviation_table = AbbreviationTable.new(@memory)
|
28
|
+
|
29
|
+
# For versions 1 and 2, there is a permanent alphabet
|
30
|
+
@alphabet = 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch
|
34
|
+
pc = @memory.program_counter
|
35
|
+
orig_pc = pc
|
36
|
+
|
37
|
+
# Determine type and form of the operand
|
38
|
+
# along with the number of operands
|
39
|
+
|
40
|
+
# read first byte to get opcode
|
41
|
+
opcode = @memory.force_readb(pc)
|
42
|
+
pc = pc + 1
|
43
|
+
|
44
|
+
# opcode form is top 2 bits
|
45
|
+
opcode_form = (opcode >> 6) & 3
|
46
|
+
operand_count = 0
|
47
|
+
operand_types = Array.new(8) { OperandType::OMITTED }
|
48
|
+
operand_values = []
|
49
|
+
|
50
|
+
# SHORT
|
51
|
+
if opcode_form == 2
|
52
|
+
# operand count is determined by bits 4 and 5
|
53
|
+
if ((opcode >> 4) & 3) == 3
|
54
|
+
operand_count = 0
|
55
|
+
opcode_class = OpcodeClass::OP0
|
56
|
+
else
|
57
|
+
operand_count = 1
|
58
|
+
opcode_class = OpcodeClass::OP1
|
59
|
+
end
|
60
|
+
|
61
|
+
operand_types[0] = (opcode >> 4) & 3
|
62
|
+
|
63
|
+
# opcode is given as bottom 4 bits
|
64
|
+
opcode = opcode & 0b1111
|
65
|
+
|
66
|
+
# VARIABLE
|
67
|
+
elsif opcode_form == 3
|
68
|
+
if (opcode & 0b100000) == 0
|
69
|
+
# when bit 5 is clear, there are two operands
|
70
|
+
operand_count = 2
|
71
|
+
opcode_class = OpcodeClass::OP2
|
72
|
+
else
|
73
|
+
# otherwise, there are VAR number
|
74
|
+
operand_count = 8
|
75
|
+
opcode_class = OpcodeClass::VAR
|
76
|
+
end
|
77
|
+
|
78
|
+
# opcode is given as bottom 5 bits
|
79
|
+
opcode = opcode & 0b11111
|
80
|
+
|
81
|
+
# EXTENDED
|
82
|
+
elsif opcode == 190 # extended form
|
83
|
+
opcode_class = OpcodeClass::EXT
|
84
|
+
|
85
|
+
# VAR number
|
86
|
+
operand_count = 8
|
87
|
+
|
88
|
+
# opcode is given as the next byte
|
89
|
+
opcode = @memory.force_readb(pc)
|
90
|
+
pc = pc + 1
|
91
|
+
|
92
|
+
# LONG
|
93
|
+
else
|
94
|
+
|
95
|
+
# there are always 2 operands
|
96
|
+
operand_count = 2
|
97
|
+
opcode_class = OpcodeClass::OP2
|
98
|
+
|
99
|
+
# bit 6 of opcode is type of operand 1
|
100
|
+
type = opcode & 0b1000000
|
101
|
+
if type == 0 # 0 means small constant
|
102
|
+
type = OperandType::SMALL
|
103
|
+
else # 1 means variable
|
104
|
+
type = OperandType::VARIABLE
|
105
|
+
end
|
106
|
+
operand_types[0] = type
|
107
|
+
|
108
|
+
# bit 5 of opcode is type of operand 2
|
109
|
+
type = opcode & 0b100000
|
110
|
+
if type == 0 # 0 means small constant
|
111
|
+
type = OperandType::SMALL
|
112
|
+
else # 1 means variable
|
113
|
+
type = OperandType::VARIABLE
|
114
|
+
end
|
115
|
+
operand_types[1] = type
|
116
|
+
|
117
|
+
# opcode is given as bottom 5 bits
|
118
|
+
opcode = opcode & 0b11111
|
119
|
+
end
|
120
|
+
|
121
|
+
# We need the opcode and opcode_class to be combined
|
122
|
+
opcode = (opcode << 3) | opcode_class
|
123
|
+
|
124
|
+
# convert some moved opcodes
|
125
|
+
if (@header.version <= 4)
|
126
|
+
if opcode == Opcode::CALL_1N
|
127
|
+
opcode = Opcode::NOT
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# handle VAR operands
|
132
|
+
if opcode_form == 3 or opcode_class == OpcodeClass::VAR or opcode_class == OpcodeClass::EXT
|
133
|
+
# each type for the operands is given by reading
|
134
|
+
# the next 1 or 2 bytes.
|
135
|
+
#
|
136
|
+
# This byte contains 4 type descriptions where
|
137
|
+
# the most significant 2 bits are the 0th type
|
138
|
+
# and the least 2 are the 3rd type
|
139
|
+
#
|
140
|
+
# If a type is deemed omitted, every subsequent
|
141
|
+
# type must also be omitted
|
142
|
+
|
143
|
+
byte = @memory.force_readb(pc)
|
144
|
+
pc = pc + 1
|
145
|
+
|
146
|
+
operand_types[0] = (byte >> 6) & 3
|
147
|
+
operand_types[1] = (byte >> 4) & 3
|
148
|
+
operand_types[2] = (byte >> 2) & 3
|
149
|
+
operand_types[3] = byte & 3
|
150
|
+
|
151
|
+
# Get the number of operands
|
152
|
+
idx = -1
|
153
|
+
first_omitted = -1
|
154
|
+
operand_count = operand_types.inject(0) do |result, element|
|
155
|
+
idx = idx + 1
|
156
|
+
if element == OperandType::OMITTED
|
157
|
+
first_omitted = idx
|
158
|
+
result
|
159
|
+
elsif first_omitted == -1
|
160
|
+
result + 1
|
161
|
+
else
|
162
|
+
# Error, OMITTED was found, but another type
|
163
|
+
# was defined as not omitted
|
164
|
+
# We will ignore
|
165
|
+
result
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if opcode == Opcode::CALL_VS2 or opcode == Opcode::CALL_VN2
|
170
|
+
# Certain opcodes can have up to 8 operands!
|
171
|
+
# These are given by a second byte
|
172
|
+
byte = @memory.force_readb(pc)
|
173
|
+
pc = pc + 1
|
174
|
+
|
175
|
+
operand_types[4] = (byte >> 6) & 3
|
176
|
+
operand_types[5] = (byte >> 4) & 3
|
177
|
+
operand_types[6] = (byte >> 2) & 3
|
178
|
+
operand_types[7] = byte & 3
|
179
|
+
|
180
|
+
# update operand_count once more
|
181
|
+
operand_count = operand_types.inject(operand_count) do |result, element|
|
182
|
+
idx = idx + 1
|
183
|
+
if element == OperandType::OMITTED
|
184
|
+
first_omitted = idx
|
185
|
+
result
|
186
|
+
elsif first_omitted == -1
|
187
|
+
result + 1
|
188
|
+
else
|
189
|
+
# Error, OMITTED was found, but another type
|
190
|
+
# was defined as not omitted
|
191
|
+
# We will ignore
|
192
|
+
result
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Retrieve the operand values
|
199
|
+
operand_types = operand_types.slice(0, operand_count)
|
200
|
+
operand_types.each do |i|
|
201
|
+
if i == OperandType::SMALL or i == OperandType::VARIABLE
|
202
|
+
operand_values << @memory.force_readb(pc)
|
203
|
+
pc = pc + 1
|
204
|
+
elsif i == OperandType::LARGE
|
205
|
+
operand_values << @memory.force_readw(pc)
|
206
|
+
pc = pc + 2
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# If the opcode stores, we need to pull the next byte to get the
|
211
|
+
# destination of the result
|
212
|
+
destination = nil
|
213
|
+
if Opcode.is_store?(opcode, @header.version)
|
214
|
+
destination = @memory.force_readb(pc)
|
215
|
+
pc = pc + 1
|
216
|
+
end
|
217
|
+
|
218
|
+
# The opcode may indicate that it's argument is a string literal
|
219
|
+
if Opcode.has_string?(opcode, @header.version)
|
220
|
+
str = ""
|
221
|
+
# Now we read in 2-byte words until the most significant bit is set
|
222
|
+
# We unencode them from the ZSCII encoding
|
223
|
+
|
224
|
+
continue = true
|
225
|
+
|
226
|
+
alphabet = 0
|
227
|
+
if (@header.version < 3)
|
228
|
+
alphabet = @alphabet
|
229
|
+
end
|
230
|
+
|
231
|
+
result = @memory.force_readzstr(pc)
|
232
|
+
pc = pc + result[0]
|
233
|
+
chrs = result[1]
|
234
|
+
|
235
|
+
# convert the string from ZSCII to UTF8
|
236
|
+
operand_types << OperandType::STRING
|
237
|
+
operand_values << ZSCII.translate(@alphabet, @header.version, chrs, @abbreviation_table)
|
238
|
+
|
239
|
+
# determine shift locks
|
240
|
+
if (@header.version < 3)
|
241
|
+
@alphabet = ZSCII.eval_alphabet(@alphabet, @header.version, chrs, @abbreviation_table)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# If the opcode is a branch, we need to pull the offset info
|
246
|
+
branch_destination = nil
|
247
|
+
branch_condition = false
|
248
|
+
if Opcode.is_branch?(opcode, @header.version)
|
249
|
+
branch_offset = @memory.force_readb(pc)
|
250
|
+
pc = pc + 1
|
251
|
+
|
252
|
+
# if bit 7 is set, the branch occurs on a true condition
|
253
|
+
# false otherwise
|
254
|
+
if (branch_offset & 0b10000000) != 0
|
255
|
+
branch_condition = true
|
256
|
+
end
|
257
|
+
|
258
|
+
# if bit 6 is clear, the branch offset is 14 bits (6 from first byte
|
259
|
+
# and the 8 from the next byte) This is _signed_
|
260
|
+
if (branch_offset & 0b01000000) == 0
|
261
|
+
branch_offset = branch_offset & 0b111111
|
262
|
+
negative = (branch_offset & 0b100000) > 0
|
263
|
+
branch_offset = branch_offset << 8
|
264
|
+
branch_offset = branch_offset | @memory.force_readb(pc)
|
265
|
+
if (negative)
|
266
|
+
branch_offset = -(16384 - branch_offset)
|
267
|
+
end
|
268
|
+
pc = pc + 1
|
269
|
+
else # otherwise, the offset is simply the remaining 6 bits _unsigned_
|
270
|
+
branch_offset = branch_offset & 0b111111
|
271
|
+
end
|
272
|
+
|
273
|
+
# calculate actual destination from the offset
|
274
|
+
if branch_offset == 0 or branch_offset == 1
|
275
|
+
# a return
|
276
|
+
branch_destination = branch_offset
|
277
|
+
else
|
278
|
+
branch_destination = pc + branch_offset - 2
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Create an Instruction class to hold this metadata
|
283
|
+
inst = Instruction.new(opcode, operand_types, operand_values, destination, branch_destination, branch_condition, pc - orig_pc)
|
284
|
+
|
285
|
+
# Store in the instruction cache
|
286
|
+
@instruction_cache[orig_pc] = inst
|
287
|
+
|
288
|
+
inst
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
292
|
end
|