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.
@@ -16,277 +16,277 @@ require_relative 'instruction'
16
16
  require_relative 'zscii'
17
17
 
18
18
  module Gruesome
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
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