gruesome 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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