gruesome 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1956 @@
1
+ require_relative '../../lib/gruesome/z/instruction'
2
+ require_relative '../../lib/gruesome/z/operand_type'
3
+ require_relative '../../lib/gruesome/z/opcode'
4
+ require_relative '../../lib/gruesome/z/memory'
5
+ require_relative '../../lib/gruesome/z/processor'
6
+ require_relative '../../lib/gruesome/z/abbreviation_table'
7
+ require_relative '../../lib/gruesome/z/object_table'
8
+
9
+ # Note: The Z-Machine implementation, upon execution,
10
+ # already moves the PC to the next instruction
11
+
12
+ describe Gruesome::Z::Processor do
13
+ describe "#execute" do
14
+ before(:each) do
15
+ zork = File.open('test/zork1.z3', 'r')
16
+ @zork_memory = Gruesome::Z::Memory.new(zork.read(zork.size))
17
+ @processor = Gruesome::Z::Processor.new(@zork_memory)
18
+ @zork_memory.program_counter = 12345
19
+ end
20
+
21
+ # Note: The decoder already gets the exact branch address
22
+ # which is why we don't specify an offset here
23
+ describe "Branch Instruction" do
24
+ after(:each) do
25
+ end
26
+
27
+ # not necessarily a branch
28
+ describe "call" do
29
+ it "should branch to the routine with two local variables given as first operand and store result in destination variable" do
30
+ # set up a routine at address $2000
31
+ @zork_memory.force_writeb(0x2000, 2)
32
+ @zork_memory.force_writew(0x2001, 0)
33
+ @zork_memory.force_writew(0x2003, 0)
34
+
35
+ # The packed address is 0x2000 / 2
36
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
37
+ [Gruesome::Z::OperandType::LARGE],
38
+ [0x1000], 128, nil, nil, 0)
39
+ @processor.execute(i)
40
+ @zork_memory.program_counter.should eql(0x2005)
41
+ end
42
+
43
+ it "should simply set the destination variable to false when routine address is 0" do
44
+ @zork_memory.writev(128, 12345)
45
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
46
+ [Gruesome::Z::OperandType::LARGE],
47
+ [0], 128, nil, nil, 0)
48
+ @processor.execute(i)
49
+ @zork_memory.readv(128).should eql(0)
50
+ @zork_memory.program_counter.should eql(12345)
51
+ end
52
+
53
+ it "should create a stack with only the return address when no arguments or local variables are used" do
54
+ # set up a routine at address $2000 with no locals
55
+ @zork_memory.force_writeb(0x2000, 0)
56
+
57
+ # The packed address is 0x2000 / 2
58
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
59
+ [Gruesome::Z::OperandType::LARGE],
60
+ [0x1000], 128, nil, nil, 0)
61
+ @processor.execute(i)
62
+ @zork_memory.program_counter.should eql(0x2001)
63
+ @zork_memory.readv(0).should eql(12345)
64
+ end
65
+
66
+ it "should create a stack with only the return address and all arguments copied to locals" do
67
+ # set up a routine at address $2000 with two locals
68
+ @zork_memory.force_writeb(0x2000, 2)
69
+ @zork_memory.force_writew(0x2001, 345)
70
+ @zork_memory.force_writew(0x2003, 456)
71
+
72
+ # The packed address is 0x2000 / 2
73
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
74
+ [Gruesome::Z::OperandType::LARGE],
75
+ [0x1000], 128, nil, nil, 0)
76
+
77
+ @processor.execute(i)
78
+ @zork_memory.program_counter.should eql(0x2005)
79
+ @zork_memory.readv(0).should eql(456)
80
+ @zork_memory.readv(0).should eql(345)
81
+ @zork_memory.readv(0).should eql(12345)
82
+ end
83
+ end
84
+
85
+ # not necessarily a branch
86
+ describe "call_1n" do
87
+ it "should branch to the routine with two local variables given as first operand" do
88
+ # set up a routine at address $2000
89
+ @zork_memory.force_writeb(0x2000, 2)
90
+ @zork_memory.force_writew(0x2001, 0)
91
+ @zork_memory.force_writew(0x2003, 0)
92
+
93
+ # The packed address is 0x2000 / 2
94
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL_1N,
95
+ [Gruesome::Z::OperandType::LARGE],
96
+ [0x1000], nil, nil, nil, 0)
97
+ @processor.execute(i)
98
+ @zork_memory.program_counter.should eql(0x2005)
99
+ end
100
+
101
+ it "should simply do nothing when routine address is 0" do
102
+ @zork_memory.writev(128, 12345)
103
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL_1N,
104
+ [Gruesome::Z::OperandType::LARGE],
105
+ [0], nil, nil, nil, 0)
106
+ @processor.execute(i)
107
+ @zork_memory.program_counter.should eql(12345)
108
+ end
109
+
110
+ it "should create a stack with only the return address when no arguments or local variables are used" do
111
+ # set up a routine at address $2000 with no locals
112
+ @zork_memory.force_writeb(0x2000, 0)
113
+
114
+ # The packed address is 0x2000 / 2
115
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL_1N,
116
+ [Gruesome::Z::OperandType::LARGE],
117
+ [0x1000], nil, nil, nil, 0)
118
+ @processor.execute(i)
119
+ @zork_memory.program_counter.should eql(0x2001)
120
+ @zork_memory.readv(0).should eql(12345)
121
+ end
122
+ end
123
+
124
+ describe "dec_chk" do
125
+ it "should decrement the value in the variable given as the operand" do
126
+ @zork_memory.writev(128, 12345)
127
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
128
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
129
+ [128, 12345], nil, nil, nil, 0)
130
+ @processor.execute(i)
131
+ @zork_memory.readv(128).should eql(12344)
132
+ end
133
+
134
+ it "should consider the value signed and decrement 0 to -1" do
135
+ @zork_memory.writev(128, 0)
136
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
137
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
138
+ [128, 12345], nil, nil, nil, 0)
139
+ @processor.execute(i)
140
+ @zork_memory.readv(128).should eql(-1+65536)
141
+ end
142
+
143
+ it "should edit the value on the stack in place" do
144
+ @zork_memory.writev(0, 11111)
145
+ @zork_memory.writev(0, 12345)
146
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
147
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
148
+ [0, 12345], nil, nil, nil, 0)
149
+ @processor.execute(i)
150
+ @zork_memory.readv(0).should eql(12344)
151
+ @zork_memory.readv(0).should eql(11111)
152
+ end
153
+
154
+ it "should branch if the value after decrementing is less than the value given by the operand" do
155
+ @zork_memory.writev(128, 12345)
156
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
157
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
158
+ [128, 12345], nil, 2000, true, 0)
159
+ @processor.execute(i)
160
+ @zork_memory.program_counter = 2000
161
+ end
162
+
163
+ it "should not branch if the value after decrementing is not less than the value given by the operand" do
164
+ @zork_memory.writev(128, 12345)
165
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
166
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
167
+ [128, 12345], nil, 2000, true, 0)
168
+ @processor.execute(i)
169
+ @zork_memory.program_counter = 12345
170
+ end
171
+
172
+ it "should not branch if the value after decrementing is less than the value given by the operand and condition is negated" do
173
+ @zork_memory.writev(128, 12345)
174
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
175
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
176
+ [128, 12345], nil, 2000, false, 0)
177
+ @processor.execute(i)
178
+ @zork_memory.program_counter = 12345
179
+ end
180
+
181
+ it "should branch if the value after decrementing is not less than the value given by the operand and condition is negated" do
182
+ @zork_memory.writev(128, 12347)
183
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC_CHK,
184
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
185
+ [128, 12346], nil, 2000, false, 0)
186
+ @processor.execute(i)
187
+ @zork_memory.program_counter = 2000
188
+ end
189
+
190
+ end
191
+
192
+ describe "inc_chk" do
193
+ it "should increment the value in the variable given as the operand" do
194
+ @zork_memory.writev(128, 12345)
195
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
196
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
197
+ [128, 12345], nil, nil, nil, 0)
198
+ @processor.execute(i)
199
+ @zork_memory.readv(128).should eql(12346)
200
+ end
201
+
202
+ it "should consider the value signed and increment -1 to 0" do
203
+ @zork_memory.writev(128, -1+65536)
204
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
205
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
206
+ [128, 12345], nil, nil, nil, 0)
207
+ @processor.execute(i)
208
+ @zork_memory.readv(128).should eql(0)
209
+ end
210
+
211
+ it "should edit the value on the stack in place" do
212
+ @zork_memory.writev(0, 11111)
213
+ @zork_memory.writev(0, 12345)
214
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
215
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
216
+ [0, 12345], nil, nil, nil, 0)
217
+ @processor.execute(i)
218
+ @zork_memory.readv(0).should eql(12346)
219
+ @zork_memory.readv(0).should eql(11111)
220
+ end
221
+
222
+ it "should branch if the value after incrementing is greater than the value given by the operand" do
223
+ @zork_memory.writev(128, 12345)
224
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
225
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
226
+ [128, 12345], nil, 2000, true, 0)
227
+ @processor.execute(i)
228
+ @zork_memory.program_counter = 2000
229
+ end
230
+
231
+ it "should not branch if the value after incrementing is not greater than the value given by the operand" do
232
+ @zork_memory.writev(128, 12345)
233
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
234
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
235
+ [128, 12346], nil, 2000, true, 0)
236
+ @processor.execute(i)
237
+ @zork_memory.program_counter = 12345
238
+ end
239
+
240
+ it "should not branch if the value after incrementing is greater than the value given by the operand and condition is negated" do
241
+ @zork_memory.writev(128, 12345)
242
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
243
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
244
+ [128, 12345], nil, 2000, false, 0)
245
+ @processor.execute(i)
246
+ @zork_memory.program_counter = 12345
247
+ end
248
+
249
+ it "should branch if the value after incrementing is not greater than the value given by the operand and condition is negated" do
250
+ @zork_memory.writev(128, 12345)
251
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC_CHK,
252
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
253
+ [128, 12346], nil, 2000, false, 0)
254
+ @processor.execute(i)
255
+ @zork_memory.program_counter = 2000
256
+ end
257
+ end
258
+
259
+ # not necessarily a branch
260
+ describe "ret" do
261
+ it "should return to the routine that called it" do
262
+ # set up a routine at address $2000
263
+ @zork_memory.force_writeb(0x2000, 2)
264
+ @zork_memory.force_writew(0x2001, 0)
265
+ @zork_memory.force_writew(0x2003, 0)
266
+
267
+ # The packed address is 0x2000 / 2
268
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
269
+ [Gruesome::Z::OperandType::LARGE],
270
+ [0x1000], 128, nil, nil, 0)
271
+
272
+ @processor.execute(i)
273
+
274
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RET,
275
+ [Gruesome::Z::OperandType::LARGE],
276
+ [123], nil, nil, nil, 0)
277
+
278
+ @processor.execute(i)
279
+
280
+ @zork_memory.program_counter.should eql(12345)
281
+ end
282
+
283
+ it "should set the variable indicated by the call with the return value as an immediate" do
284
+ # set up a routine at address $2000
285
+ @zork_memory.force_writeb(0x2000, 2)
286
+ @zork_memory.force_writew(0x2001, 0)
287
+ @zork_memory.force_writew(0x2003, 0)
288
+
289
+ # The packed address is 0x2000 / 2
290
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
291
+ [Gruesome::Z::OperandType::LARGE],
292
+ [0x1000], 128, nil, nil, 0)
293
+
294
+ @processor.execute(i)
295
+
296
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RET,
297
+ [Gruesome::Z::OperandType::LARGE],
298
+ [123], nil, nil, nil, 0)
299
+
300
+ @processor.execute(i)
301
+
302
+ @zork_memory.readv(128).should eql(123)
303
+ end
304
+
305
+ it "should be able to push the result (given as an immediate) to the stack if the call requests variable 0" do
306
+ # set up a routine at address $2000
307
+ @zork_memory.force_writeb(0x2000, 2)
308
+ @zork_memory.force_writew(0x2001, 0)
309
+ @zork_memory.force_writew(0x2003, 0)
310
+
311
+ # The packed address is 0x2000 / 2
312
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
313
+ [Gruesome::Z::OperandType::LARGE],
314
+ [0x1000], 0, nil, nil, 0)
315
+
316
+ @processor.execute(i)
317
+
318
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RET,
319
+ [Gruesome::Z::OperandType::LARGE],
320
+ [123], nil, nil, nil, 0)
321
+
322
+ @processor.execute(i)
323
+
324
+ @zork_memory.readv(0).should eql(123)
325
+ end
326
+ end
327
+
328
+ # not necessarily a branch
329
+ describe "ret_popped" do
330
+ it "should return to the routine that called it" do
331
+ # set up a routine at address $2000
332
+ @zork_memory.force_writeb(0x2000, 2)
333
+ @zork_memory.force_writew(0x2001, 0)
334
+ @zork_memory.force_writew(0x2003, 0)
335
+
336
+ # The packed address is 0x2000 / 2
337
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
338
+ [Gruesome::Z::OperandType::LARGE],
339
+ [0x1000], 128, nil, nil, 0)
340
+
341
+ @processor.execute(i)
342
+
343
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RET_POPPED,
344
+ [Gruesome::Z::OperandType::LARGE],
345
+ [123], nil, nil, nil, 0)
346
+
347
+ @processor.execute(i)
348
+
349
+ @zork_memory.program_counter.should eql(12345)
350
+ end
351
+
352
+ it "should be able to push top of callee stack to the stack of the caller" do
353
+ # set up a routine at address $2000
354
+ @zork_memory.force_writeb(0x2000, 2)
355
+ @zork_memory.force_writew(0x2001, 0)
356
+ @zork_memory.force_writew(0x2003, 0)
357
+ @zork_memory.writev(0, 11111)
358
+
359
+ # The packed address is 0x2000 / 2
360
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
361
+ [Gruesome::Z::OperandType::LARGE],
362
+ [0x1000], 0, nil, nil, 0)
363
+
364
+ @processor.execute(i)
365
+ @zork_memory.writev(0, 22222)
366
+ @zork_memory.writev(0, 23456)
367
+
368
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RET_POPPED,
369
+ [Gruesome::Z::OperandType::LARGE],
370
+ [123], nil, nil, nil, 0)
371
+
372
+ @processor.execute(i)
373
+
374
+ @zork_memory.readv(0).should eql(23456)
375
+ @zork_memory.readv(0).should eql(11111)
376
+ end
377
+ end
378
+
379
+ # not necessarily a branch
380
+ describe "rfalse" do
381
+ it "should set the variable indicated by the call with 0" do
382
+ # set up a routine at address $2000
383
+ @zork_memory.force_writeb(0x2000, 2)
384
+ @zork_memory.force_writew(0x2001, 0)
385
+ @zork_memory.force_writew(0x2003, 0)
386
+
387
+ # The packed address is 0x2000 / 2
388
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
389
+ [Gruesome::Z::OperandType::LARGE],
390
+ [0x1000], 128, nil, nil, 0)
391
+
392
+ @processor.execute(i)
393
+
394
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RFALSE,
395
+ [], [], nil, nil, nil, 0)
396
+
397
+ @processor.execute(i)
398
+
399
+ @zork_memory.readv(128).should eql(0)
400
+ end
401
+
402
+ it "should push 0 to the caller's stack when indicated" do
403
+ # set up a routine at address $2000
404
+ @zork_memory.force_writeb(0x2000, 2)
405
+ @zork_memory.force_writew(0x2001, 0)
406
+ @zork_memory.force_writew(0x2003, 0)
407
+
408
+ # The packed address is 0x2000 / 2
409
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
410
+ [Gruesome::Z::OperandType::LARGE],
411
+ [0x1000], 0, nil, nil, 0)
412
+
413
+ @processor.execute(i)
414
+
415
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RFALSE,
416
+ [], [], nil, nil, nil, 0)
417
+
418
+ @processor.execute(i)
419
+
420
+ @zork_memory.readv(0).should eql(0)
421
+ end
422
+ end
423
+
424
+ # not necessarily a branch
425
+ describe "rtrue" do
426
+ it "should set the variable indicated by the call with 1" do
427
+ # set up a routine at address $2000
428
+ @zork_memory.force_writeb(0x2000, 2)
429
+ @zork_memory.force_writew(0x2001, 0)
430
+ @zork_memory.force_writew(0x2003, 0)
431
+
432
+ # The packed address is 0x2000 / 2
433
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
434
+ [Gruesome::Z::OperandType::LARGE],
435
+ [0x1000], 128, nil, nil, 0)
436
+
437
+ @processor.execute(i)
438
+
439
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RTRUE,
440
+ [], [], nil, nil, nil, 0)
441
+
442
+ @processor.execute(i)
443
+
444
+ @zork_memory.readv(128).should eql(1)
445
+ end
446
+
447
+ it "should push 1 to the caller's stack when indicated" do
448
+ # set up a routine at address $2000
449
+ @zork_memory.force_writeb(0x2000, 2)
450
+ @zork_memory.force_writew(0x2001, 0)
451
+ @zork_memory.force_writew(0x2003, 0)
452
+
453
+ # The packed address is 0x2000 / 2
454
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
455
+ [Gruesome::Z::OperandType::LARGE],
456
+ [0x1000], 0, nil, nil, 0)
457
+
458
+ @processor.execute(i)
459
+
460
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RTRUE,
461
+ [], [], nil, nil, nil, 0)
462
+
463
+ @processor.execute(i)
464
+
465
+ @zork_memory.readv(0).should eql(1)
466
+ end
467
+ end
468
+
469
+ describe "jg" do
470
+ it "should branch if the first operand is greater than the second" do
471
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JG,
472
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
473
+ [(-12345+65536), (-23456+65536)], nil, 2000, true, 0)
474
+
475
+ @processor.execute(i)
476
+ @zork_memory.program_counter.should eql(2000)
477
+ end
478
+
479
+ it "should not branch if the first operand is not greater than the second" do
480
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JG,
481
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
482
+ [12345, 12345], nil, 2000, true, 0)
483
+
484
+ @processor.execute(i)
485
+ @zork_memory.program_counter.should eql(12345)
486
+ end
487
+
488
+ it "should not branch if the first operand is greater than the second and condition is negated" do
489
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JG,
490
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
491
+ [(-12345+65536), (-23456+65536)], nil, 2000, false, 0)
492
+
493
+ @processor.execute(i)
494
+ @zork_memory.program_counter.should eql(12345)
495
+ end
496
+
497
+ it "should branch if the first operand is not greater than the second and condition is negated" do
498
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JG,
499
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
500
+ [(-12345+65536), 12345], nil, 2000, false, 0)
501
+
502
+ @processor.execute(i)
503
+ @zork_memory.program_counter.should eql(2000)
504
+ end
505
+ end
506
+
507
+ describe "je" do
508
+ it "should branch if the first operand is equal to one of four operands" do
509
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
510
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE,
511
+ Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
512
+ [(-12345+65536), 123, 1234, (-12345+65536)], nil, 2000, true, 0)
513
+
514
+ @processor.execute(i)
515
+ @zork_memory.program_counter.should eql(2000)
516
+ end
517
+
518
+ it "should not branch if the first operand is not equal to one of four operands" do
519
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
520
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE,
521
+ Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
522
+ [(-123+65536), 123, 1234, (12345+65536)], nil, 2000, true, 0)
523
+
524
+ @processor.execute(i)
525
+ @zork_memory.program_counter.should eql(12345)
526
+ end
527
+
528
+ it "should not branch if the first operand is equal to one of four operands and condition is negated" do
529
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
530
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE,
531
+ Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
532
+ [(-12345+65536), 1234, 123, (-12345+65536)], nil, 2000, false, 0)
533
+
534
+ @processor.execute(i)
535
+ @zork_memory.program_counter.should eql(12345)
536
+ end
537
+
538
+ it "should branch if the first operand is not equal to one of four operands and condition is negated" do
539
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
540
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE,
541
+ Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
542
+ [(-123+65536), 123, 12345, 12344], nil, 2000, false, 0)
543
+
544
+ @processor.execute(i)
545
+ @zork_memory.program_counter.should eql(2000)
546
+ end
547
+
548
+ it "should branch if the first operand is equal to one of three operands" do
549
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
550
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
551
+ [(-12345+65536), 123, (-12345+65536)], nil, 2000, true, 0)
552
+
553
+ @processor.execute(i)
554
+ @zork_memory.program_counter.should eql(2000)
555
+ end
556
+
557
+ it "should not branch if the first operand is not equal to one of three operands" do
558
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
559
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
560
+ [(-123+65536), 123, (12345+65536)], nil, 2000, true, 0)
561
+
562
+ @processor.execute(i)
563
+ @zork_memory.program_counter.should eql(12345)
564
+ end
565
+
566
+ it "should not branch if the first operand is equal to one of three operands and condition is negated" do
567
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
568
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
569
+ [(-12345+65536), 123, (-12345+65536)], nil, 2000, false, 0)
570
+
571
+ @processor.execute(i)
572
+ @zork_memory.program_counter.should eql(12345)
573
+ end
574
+
575
+ it "should branch if the first operand is not equal to one of three operands and condition is negated" do
576
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
577
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
578
+ [(-123+65536), 12345, 12344], nil, 2000, false, 0)
579
+
580
+ @processor.execute(i)
581
+ @zork_memory.program_counter.should eql(2000)
582
+ end
583
+
584
+ it "should branch if the first operand is equal to the second of two operands" do
585
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
586
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
587
+ [(-12345+65536), (-12345+65536)], nil, 2000, true, 0)
588
+
589
+ @processor.execute(i)
590
+ @zork_memory.program_counter.should eql(2000)
591
+ end
592
+
593
+ it "should not branch if the first operand is not equal to the second of two operands" do
594
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
595
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
596
+ [(-123+65536), (12345+65536)], nil, 2000, true, 0)
597
+
598
+ @processor.execute(i)
599
+ @zork_memory.program_counter.should eql(12345)
600
+ end
601
+
602
+ it "should not branch if the first operand is equal to the second of two operands and condition is negated" do
603
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
604
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
605
+ [(-12345+65536), (-12345+65536)], nil, 2000, false, 0)
606
+
607
+ @processor.execute(i)
608
+ @zork_memory.program_counter.should eql(12345)
609
+ end
610
+
611
+ it "should branch if the first operand is not equal to the second of two operands and condition is negated" do
612
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JE,
613
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
614
+ [(-123+65536), (12345+65536)], nil, 2000, false, 0)
615
+
616
+ @processor.execute(i)
617
+ @zork_memory.program_counter.should eql(2000)
618
+ end
619
+ end
620
+
621
+ describe "jl" do
622
+ it "should branch if the first operand is less than the second" do
623
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JL,
624
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
625
+ [(-23456+65536), (-12345+65536)], nil, 2000, true, 0)
626
+
627
+ @processor.execute(i)
628
+ @zork_memory.program_counter.should eql(2000)
629
+ end
630
+
631
+ it "should not branch if the first operand is not less than the second" do
632
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JL,
633
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
634
+ [12345, 12345], nil, 2000, true, 0)
635
+
636
+ @processor.execute(i)
637
+ @zork_memory.program_counter.should eql(12345)
638
+ end
639
+
640
+ it "should not branch if the first operand is less than the second and condition is negated" do
641
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JL,
642
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
643
+ [(-23456+65536), (-12345+65536)], nil, 2000, false, 0)
644
+
645
+ @processor.execute(i)
646
+ @zork_memory.program_counter.should eql(12345)
647
+ end
648
+
649
+ it "should branch if the first operand is not less than the second and condition is negated" do
650
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JL,
651
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
652
+ [12345, (-12345+65536)], nil, 2000, false, 0)
653
+
654
+ @processor.execute(i)
655
+ @zork_memory.program_counter.should eql(2000)
656
+ end
657
+ end
658
+
659
+ describe "jz" do
660
+ it "should branch if the first operand is zero" do
661
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JZ,
662
+ [Gruesome::Z::OperandType::LARGE],
663
+ [0], nil, 2000, true, 0)
664
+
665
+ @processor.execute(i)
666
+ @zork_memory.program_counter.should eql(2000)
667
+ end
668
+
669
+ it "should not branch if the first operand is not zero" do
670
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JZ,
671
+ [Gruesome::Z::OperandType::LARGE],
672
+ [12345], nil, 2000, true, 0)
673
+
674
+ @processor.execute(i)
675
+ @zork_memory.program_counter.should eql(12345)
676
+ end
677
+
678
+ it "should not branch if the first operand is zero and condition is negated" do
679
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JZ,
680
+ [Gruesome::Z::OperandType::LARGE],
681
+ [0], nil, 2000, false, 0)
682
+
683
+ @processor.execute(i)
684
+ @zork_memory.program_counter.should eql(12345)
685
+ end
686
+
687
+ it "should branch if the first operand is not zero and condition is negated" do
688
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JZ,
689
+ [Gruesome::Z::OperandType::LARGE],
690
+ [12345], nil, 2000, false, 0)
691
+
692
+ @processor.execute(i)
693
+ @zork_memory.program_counter.should eql(2000)
694
+ end
695
+ end
696
+
697
+ describe "jump" do
698
+ it "should update the program counter via a signed offset" do
699
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JUMP,
700
+ [Gruesome::Z::OperandType::LARGE],
701
+ [(-12343+65536)], nil, nil, nil, 0)
702
+
703
+ @processor.execute(i)
704
+ @zork_memory.program_counter.should eql(0)
705
+ end
706
+ end
707
+
708
+ describe "piracy" do
709
+ it "should simply act as a jump and update the program counter via a signed offset" do
710
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PIRACY,
711
+ [Gruesome::Z::OperandType::LARGE],
712
+ [(-12343+65536)], nil, nil, nil, 0)
713
+
714
+ @processor.execute(i)
715
+ @zork_memory.program_counter.should eql(0)
716
+ end
717
+ end
718
+
719
+ describe "test" do
720
+ it "should branch if the first operand has all bits set that the second has set" do
721
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST,
722
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
723
+ [0b1000110110111101, 0b1000100010001000], nil, 2000, true, 0)
724
+
725
+ @processor.execute(i)
726
+ @zork_memory.program_counter.should eql(2000)
727
+ end
728
+
729
+ it "should not branch if the first operand does not have all bits set that the second has set" do
730
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST,
731
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
732
+ [0b1000110110111101, 0b1111100010001000], nil, 2000, true, 0)
733
+
734
+ @processor.execute(i)
735
+ @zork_memory.program_counter.should eql(12345)
736
+ end
737
+
738
+ it "should not branch if the first operand has all bits set that the second has set and condition is negated" do
739
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST,
740
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
741
+ [0b1000110110111101, 0b1000100010001000], nil, 2000, false, 0)
742
+
743
+ @processor.execute(i)
744
+ @zork_memory.program_counter.should eql(12345)
745
+ end
746
+
747
+ it "should branch if the first operand does not have all bits set that the second has set and condition is negated" do
748
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST,
749
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
750
+ [0b1000110110111101, 0b1111100010001000], nil, 2000, false, 0)
751
+
752
+ @processor.execute(i)
753
+ @zork_memory.program_counter.should eql(2000)
754
+ end
755
+ end
756
+ end
757
+
758
+ describe "Store Instruction" do
759
+ before(:each) do
760
+ @zork_memory.writev(128, 0)
761
+ end
762
+
763
+ after(:each) do
764
+ # instructions should not affect PC
765
+ @zork_memory.program_counter.should eql(12345)
766
+ end
767
+
768
+ describe "art_shift" do
769
+ it "should shift left when places is positive" do
770
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::ART_SHIFT,
771
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
772
+ [1234, 2], 128, nil, nil, 0)
773
+
774
+ @processor.execute(i)
775
+
776
+ @zork_memory.readv(128).should eql(((1234 << 2) & 65535))
777
+ end
778
+
779
+ it "should shift right when places is positive and sign extend" do
780
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::ART_SHIFT,
781
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
782
+ [-123+65536, -2+65536], 128, nil, nil, 0)
783
+
784
+ @processor.execute(i)
785
+
786
+ @zork_memory.readv(128).should eql((-123 >> 2) & 65535)
787
+ end
788
+ end
789
+
790
+ describe "load" do
791
+ it "should store the value of the variable into the result" do
792
+ @zork_memory.writev(128, 30)
793
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::LOAD,
794
+ [Gruesome::Z::OperandType::VARIABLE],
795
+ [128], 130, nil, nil, 0)
796
+ @processor.execute(i)
797
+ @zork_memory.readv(130).should eql(30)
798
+ end
799
+
800
+ it "should not pop the value off of the stack if variable %00 is used" do
801
+ @zork_memory.writev(0, 11111)
802
+ @zork_memory.writev(0, 12345)
803
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::LOAD,
804
+ [Gruesome::Z::OperandType::VARIABLE],
805
+ [0], 130, nil, nil, 0)
806
+ @processor.execute(i)
807
+ @zork_memory.readv(130).should eql(12345)
808
+ @zork_memory.readv(0).should eql(12345)
809
+ @zork_memory.readv(0).should eql(11111)
810
+ end
811
+ end
812
+
813
+ describe "loadb" do
814
+ it "should store the value of the byte at the location given by the base and offset into the result" do
815
+ @zork_memory.writeb(2589+200, 127)
816
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::LOADB,
817
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
818
+ [2589, 200], 130, nil, nil, 0)
819
+ @processor.execute(i)
820
+ @zork_memory.readv(130).should eql(127)
821
+ end
822
+ end
823
+
824
+ describe "loadw" do
825
+ it "should store the value of the word at the location given by the base and offset into the result" do
826
+ @zork_memory.writew(2480+200, 12345)
827
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::LOADW,
828
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
829
+ [2480, 100], 130, nil, nil, 0)
830
+ @processor.execute(i)
831
+ @zork_memory.readv(130).should eql(12345)
832
+ end
833
+ end
834
+
835
+ describe "random" do
836
+ it "should return 0 when a negative value is used to seed the generator" do
837
+ @zork_memory.writev(130, 11111)
838
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RANDOM,
839
+ [Gruesome::Z::OperandType::LARGE],
840
+ [-1+65536], 130, nil, nil, 0)
841
+ @processor.execute(i)
842
+ @zork_memory.readv(130).should eql(0)
843
+ end
844
+
845
+ it "should return 0 when 0 is used to seed the generator" do
846
+ @zork_memory.writev(130, 11111)
847
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RANDOM,
848
+ [Gruesome::Z::OperandType::LARGE],
849
+ [-1+65536], 130, nil, nil, 0)
850
+ @processor.execute(i)
851
+ @zork_memory.readv(130).should eql(0)
852
+ end
853
+
854
+ it "should be able to be seeded with a negative value such that the behavior is predicable" do
855
+ srand = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RANDOM,
856
+ [Gruesome::Z::OperandType::LARGE],
857
+ [-1+65536], 130, nil, nil, 0)
858
+ @processor.execute(srand)
859
+
860
+ rnd = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::RANDOM,
861
+ [Gruesome::Z::OperandType::LARGE],
862
+ [1234], 130, nil, nil, 0)
863
+ @processor.execute(rnd)
864
+ store1 = @zork_memory.readv(130)
865
+ @processor.execute(rnd)
866
+ store2 = @zork_memory.readv(130)
867
+
868
+ @processor.execute(srand)
869
+
870
+ @processor.execute(rnd)
871
+ @zork_memory.readv(130).should eql(store1)
872
+ @processor.execute(rnd)
873
+ @zork_memory.readv(130).should eql(store2)
874
+ end
875
+ end
876
+ end
877
+
878
+ describe "Input Instruction" do
879
+ before(:each) do
880
+ # We take over stdin so that we can simulate the keyboard input
881
+ @stdin = $stdin
882
+
883
+ # sread text-buffer parse-buffer (v 1-3)
884
+
885
+ # text-buffer byte 0 indicates maximum length
886
+ # parse-buffer byte 0 indicates maximum words
887
+
888
+ @zork_memory.force_writeb(0x1000, 50)
889
+ end
890
+
891
+ after(:each) do
892
+ $stdin = @stdin
893
+
894
+ # The program counter should not be changed
895
+ @zork_memory.program_counter.should eql(12345)
896
+ end
897
+
898
+ describe "sread" do
899
+ it "should read in a line of text" do
900
+ $stdin = StringIO.new("foo bar johnson\n")
901
+
902
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::SREAD,
903
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
904
+ [0x1000, 0x1200], nil, nil, nil, 0)
905
+ @processor.execute(i)
906
+ end
907
+ end
908
+ end
909
+
910
+ describe "Output and Branching Instruction" do
911
+ before(:each) do
912
+ # We take over stdout so that we can see what it prints
913
+ @stdout = $stdout
914
+ $stdout = StringIO.new
915
+ end
916
+
917
+ after(:each) do
918
+ $stdout = @stdout
919
+ end
920
+
921
+ describe "print_ret" do
922
+ it "should return to the routine that called it" do
923
+ # set up a routine at address $2000
924
+ @zork_memory.force_writeb(0x2000, 2)
925
+ @zork_memory.force_writew(0x2001, 0)
926
+ @zork_memory.force_writew(0x2003, 0)
927
+
928
+ # The packed address is 0x2000 / 2
929
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
930
+ [Gruesome::Z::OperandType::LARGE],
931
+ [0x1000], 128, nil, nil, 0)
932
+
933
+ @processor.execute(i)
934
+
935
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_RET,
936
+ [Gruesome::Z::OperandType::STRING],
937
+ ["Hello World"], nil, nil, nil, 0)
938
+
939
+ @processor.execute(i)
940
+
941
+ @zork_memory.program_counter.should eql(12345)
942
+ end
943
+
944
+ it "should print out the string given as an operand followed by a newline to stdout" do
945
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_RET,
946
+ [Gruesome::Z::OperandType::STRING],
947
+ ["Hello World"], nil, nil, nil, 0)
948
+
949
+ @processor.execute(i)
950
+ $stdout.string.should eql("Hello World\n")
951
+ end
952
+
953
+ it "should as a return value set the variable indicated by the call with 1" do
954
+ # set up a routine at address $2000
955
+ @zork_memory.force_writeb(0x2000, 2)
956
+ @zork_memory.force_writew(0x2001, 0)
957
+ @zork_memory.force_writew(0x2003, 0)
958
+
959
+ # The packed address is 0x2000 / 2
960
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
961
+ [Gruesome::Z::OperandType::LARGE],
962
+ [0x1000], 128, nil, nil, 0)
963
+
964
+ @processor.execute(i)
965
+
966
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_RET,
967
+ [Gruesome::Z::OperandType::STRING],
968
+ ["Hello World"], nil, nil, nil, 0)
969
+
970
+ @processor.execute(i)
971
+
972
+ @zork_memory.readv(128).should eql(1)
973
+ end
974
+
975
+ it "should as a return value push 1 to the caller's stack when indicated" do
976
+ # set up a routine at address $2000
977
+ @zork_memory.force_writeb(0x2000, 2)
978
+ @zork_memory.force_writew(0x2001, 0)
979
+ @zork_memory.force_writew(0x2003, 0)
980
+ @zork_memory.writev(0, 11111)
981
+
982
+ # The packed address is 0x2000 / 2
983
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CALL,
984
+ [Gruesome::Z::OperandType::LARGE],
985
+ [0x1000], 0, nil, nil, 0)
986
+
987
+ @processor.execute(i)
988
+
989
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_RET,
990
+ [Gruesome::Z::OperandType::STRING],
991
+ ["Hello World"], nil, nil, nil, 0)
992
+
993
+ @processor.execute(i)
994
+
995
+ @zork_memory.readv(0).should eql(1)
996
+ @zork_memory.readv(0).should eql(11111)
997
+ end
998
+ end
999
+ end
1000
+
1001
+ describe "Output Instruction" do
1002
+ before(:each) do
1003
+ # We take over stdout so that we can see what it prints
1004
+ @stdout = $stdout
1005
+ $stdout = StringIO.new
1006
+ end
1007
+
1008
+ after(:each) do
1009
+ $stdout = @stdout
1010
+
1011
+ # The program counter should not be changed
1012
+ @zork_memory.program_counter.should eql(12345)
1013
+ end
1014
+
1015
+ describe "new_line" do
1016
+ it "should print a carriage return" do
1017
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::NEW_LINE,
1018
+ [], [], nil, nil, nil, 0)
1019
+ @processor.execute(i)
1020
+ $stdout.string.should eql("\n")
1021
+ end
1022
+ end
1023
+
1024
+ describe "print_char" do
1025
+ it "should print out the zchar given as an operand to stdout" do
1026
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_CHAR,
1027
+ [Gruesome::Z::OperandType::LARGE],
1028
+ [215], nil, nil, nil, 0)
1029
+
1030
+ @processor.execute(i)
1031
+ $stdout.string.should eql("\u00fe")
1032
+ end
1033
+ end
1034
+
1035
+ describe "print_num" do
1036
+ it "should print out a number given as an operand in decimal to stdout" do
1037
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_NUM,
1038
+ [Gruesome::Z::OperandType::LARGE],
1039
+ [12345], nil, nil, nil, 0)
1040
+
1041
+ @processor.execute(i)
1042
+ $stdout.string.should eql("12345")
1043
+ end
1044
+
1045
+ it "should print out a signed number given as an operand in decimal to stdout" do
1046
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_NUM,
1047
+ [Gruesome::Z::OperandType::LARGE],
1048
+ [-12345+65536], nil, nil, nil, 0)
1049
+
1050
+ @processor.execute(i)
1051
+ $stdout.string.should eql("-12345")
1052
+ end
1053
+ end
1054
+
1055
+ describe "print" do
1056
+ it "should print out the string given as an operand to stdout" do
1057
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT,
1058
+ [Gruesome::Z::OperandType::STRING],
1059
+ ["Hello World"], nil, nil, nil, 0)
1060
+
1061
+ @processor.execute(i)
1062
+ $stdout.string.should eql("Hello World")
1063
+ end
1064
+ end
1065
+
1066
+ describe "print_addr" do
1067
+ it "should print out the string located at the byte address given by the operand" do
1068
+ # 'grue' is written at 0x4291
1069
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_ADDR,
1070
+ [Gruesome::Z::OperandType::LARGE],
1071
+ [0x4291], nil, nil, nil, 0)
1072
+
1073
+ @processor.execute(i)
1074
+ $stdout.string.should eql("grue")
1075
+ end
1076
+ end
1077
+
1078
+ describe "print_paddr" do
1079
+ it "should print out the string located at the packed address given by the operand" do
1080
+ # 'ZORK I: The Great Underground Empire' is written at 0x6ee4 (packed: 0x3772)
1081
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_PADDR,
1082
+ [Gruesome::Z::OperandType::LARGE],
1083
+ [0x3772], nil, nil, nil, 0)
1084
+
1085
+ @processor.execute(i)
1086
+ $stdout.string[/(.*)\n/,1].should eql("ZORK I: The Great Underground Empire")
1087
+ end
1088
+ end
1089
+
1090
+ describe "log_shift" do
1091
+ it "should shift left when places is positive" do
1092
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::LOG_SHIFT,
1093
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1094
+ [-12345+65536, 2], 128, nil, nil, 0)
1095
+
1096
+ @processor.execute(i)
1097
+
1098
+ @zork_memory.readv(128).should eql((((-12345+65536) << 2) & 65535))
1099
+ end
1100
+
1101
+ it "should shift right when places is positive and not sign extend" do
1102
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::LOG_SHIFT,
1103
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1104
+ [-123+65536, -2+65536], 128, nil, nil, 0)
1105
+
1106
+ @processor.execute(i)
1107
+
1108
+ @zork_memory.readv(128).should eql(((-123+65536) >> 2))
1109
+ end
1110
+ end
1111
+
1112
+ describe "not" do
1113
+ it "should perform a logical negation on the operand and assign to the appropriate variable" do
1114
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::NOT,
1115
+ [Gruesome::Z::OperandType::LARGE],
1116
+ [12345], 128, nil, nil, 0)
1117
+
1118
+ @processor.execute(i)
1119
+
1120
+ @zork_memory.readv(128).should eql((~12345)+65536)
1121
+ end
1122
+ end
1123
+
1124
+ describe "and" do
1125
+ it "should bitwise and two signed shorts together and assign to the appropriate variable" do
1126
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::AND,
1127
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1128
+ [-12345+65536, 12344], 128, nil, nil, 0)
1129
+
1130
+ @processor.execute(i)
1131
+
1132
+ @zork_memory.readv(128).should eql((-12345 & 12344) & 65535)
1133
+ end
1134
+ end
1135
+
1136
+ describe "or" do
1137
+ it "should bitwise or two signed shorts together and assign to the appropriate variable" do
1138
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::OR,
1139
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1140
+ [-12345+65536, 12344], 128, nil, nil, 0)
1141
+
1142
+ @processor.execute(i)
1143
+
1144
+ @zork_memory.readv(128).should eql((-12345 | 12344) & 65535)
1145
+ end
1146
+ end
1147
+
1148
+ describe "add" do
1149
+ it "should add two signed shorts together and assign to the appropriate variable" do
1150
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::ADD,
1151
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1152
+ [-12345+65536, 12344], 128, nil, nil, 0)
1153
+
1154
+ @processor.execute(i)
1155
+
1156
+ @zork_memory.readv(128).should eql(-1+65536)
1157
+ end
1158
+ end
1159
+
1160
+ describe "sub" do
1161
+ it "should subtract two signed shorts together and assign to the appropriate variable" do
1162
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::SUB,
1163
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1164
+ [-12345+65536, 12344], 128, nil, nil, 0)
1165
+
1166
+ @processor.execute(i)
1167
+
1168
+ @zork_memory.readv(128).should eql(-24689+65536)
1169
+ end
1170
+ end
1171
+
1172
+ describe "mul" do
1173
+ it "should multiply two signed shorts together and assign to the appropriate variable" do
1174
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::MUL,
1175
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1176
+ [-12345+65536, 12344], 128, nil, nil, 0)
1177
+
1178
+ @processor.execute(i)
1179
+
1180
+ @zork_memory.readv(128).should eql(50056)
1181
+ end
1182
+ end
1183
+
1184
+ describe "div" do
1185
+ it "should divide one negative and one positive short together and assign to the appropriate variable" do
1186
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DIV,
1187
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1188
+ [-11+65536, 2], 128, nil, nil, 0)
1189
+
1190
+ @processor.execute(i)
1191
+
1192
+ @zork_memory.readv(128).should eql(-5+65536)
1193
+ end
1194
+
1195
+ it "should divide one positive and one negative short together and assign to the appropriate variable" do
1196
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DIV,
1197
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1198
+ [11, -2+65536], 128, nil, nil, 0)
1199
+
1200
+ @processor.execute(i)
1201
+
1202
+ @zork_memory.readv(128).should eql(-5+65536)
1203
+ end
1204
+
1205
+ it "should divide two positive shorts together and assign to the appropriate variable" do
1206
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DIV,
1207
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1208
+ [11, 2], 128, nil, nil, 0)
1209
+
1210
+ @processor.execute(i)
1211
+
1212
+ @zork_memory.readv(128).should eql(5)
1213
+ end
1214
+
1215
+ it "should divide two negative shorts together and assign to the appropriate variable" do
1216
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DIV,
1217
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1218
+ [-11+65536, -2+65536], 128, nil, nil, 0)
1219
+
1220
+ @processor.execute(i)
1221
+
1222
+ @zork_memory.readv(128).should eql(5)
1223
+ end
1224
+ end
1225
+
1226
+ describe "mul" do
1227
+ it "should modulo one negative and one positive short together and assign to the appropriate variable" do
1228
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::MOD,
1229
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1230
+ [-13+65536, 5], 128, nil, nil, 0)
1231
+
1232
+ @processor.execute(i)
1233
+
1234
+ @zork_memory.readv(128).should eql(-3+65536)
1235
+ end
1236
+
1237
+ it "should modulo one positive and one negative short together and assign to the appropriate variable" do
1238
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::MOD,
1239
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1240
+ [13, -5+65536], 128, nil, nil, 0)
1241
+
1242
+ @processor.execute(i)
1243
+
1244
+ @zork_memory.readv(128).should eql(3)
1245
+ end
1246
+
1247
+ it "should modulo two positive shorts together and assign to the appropriate variable" do
1248
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::MOD,
1249
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1250
+ [13, 5], 128, nil, nil, 0)
1251
+
1252
+ @processor.execute(i)
1253
+
1254
+ @zork_memory.readv(128).should eql(3)
1255
+ end
1256
+
1257
+ it "should modulo two negative shorts together and assign to the appropriate variable" do
1258
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::MOD,
1259
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1260
+ [-13+65536, -5+65536], 128, nil, nil, 0)
1261
+
1262
+ @processor.execute(i)
1263
+
1264
+ @zork_memory.readv(128).should eql(-3+65536)
1265
+ end
1266
+ end
1267
+ end
1268
+
1269
+ describe "Object" do
1270
+ before(:each) do
1271
+ # Build object table
1272
+ @zork_memory.force_writew(0x0a, 0x200)
1273
+
1274
+ # Build property defaults (for versions 1-3)
1275
+ # Each property will have the default value as
1276
+ # equally the property number (remember properties
1277
+ # start counting at 1)
1278
+ 31.times do |i|
1279
+ @zork_memory.force_writew(0x200 + (i*2), i+1)
1280
+ end
1281
+ addr = 0x200 + (31 * 2)
1282
+
1283
+ # Build a couple of objects
1284
+
1285
+ # Object #1
1286
+
1287
+ # Attributes: 2, 3, 10, 31
1288
+ # Parent: nil
1289
+ # Sibling: nil
1290
+ # Child: #2
1291
+
1292
+ @zork_memory.force_writeb(addr+0, 0b00110000)
1293
+ @zork_memory.force_writeb(addr+1, 0b00100000)
1294
+ @zork_memory.force_writeb(addr+2, 0b00000000)
1295
+ @zork_memory.force_writeb(addr+3, 0b00000001)
1296
+
1297
+ @zork_memory.force_writeb(addr+4, 0)
1298
+ @zork_memory.force_writeb(addr+5, 0)
1299
+ @zork_memory.force_writeb(addr+6, 2)
1300
+
1301
+ @zork_memory.force_writew(addr+7, 0x300)
1302
+
1303
+ addr += 9
1304
+
1305
+ # Object #2
1306
+
1307
+ # Attributes: 5, 7, 11, 18, 25
1308
+ # Parent: #1
1309
+ # Sibling: #3
1310
+ # Child: nil
1311
+
1312
+ @zork_memory.force_writeb(addr+0, 0b00000101)
1313
+ @zork_memory.force_writeb(addr+1, 0b00010000)
1314
+ @zork_memory.force_writeb(addr+2, 0b00100000)
1315
+ @zork_memory.force_writeb(addr+3, 0b01000001)
1316
+
1317
+ @zork_memory.force_writeb(addr+4, 1)
1318
+ @zork_memory.force_writeb(addr+5, 3)
1319
+ @zork_memory.force_writeb(addr+6, 0)
1320
+
1321
+ @zork_memory.force_writew(addr+7, 0x300)
1322
+
1323
+ addr += 9
1324
+
1325
+ # Object #3
1326
+
1327
+ # Attributes: 0
1328
+ # Parent: #1
1329
+ # Sibling: #2
1330
+ # Child: nil
1331
+
1332
+ @zork_memory.force_writeb(addr+0, 0b10000000)
1333
+ @zork_memory.force_writeb(addr+1, 0b00000000)
1334
+ @zork_memory.force_writeb(addr+2, 0b00000000)
1335
+ @zork_memory.force_writeb(addr+3, 0b00000000)
1336
+
1337
+ @zork_memory.force_writeb(addr+4, 1)
1338
+ @zork_memory.force_writeb(addr+5, 2)
1339
+ @zork_memory.force_writeb(addr+6, 0)
1340
+
1341
+ @zork_memory.force_writew(addr+7, 0x4290)
1342
+
1343
+ addr += 9
1344
+
1345
+ # Properties Table
1346
+ #
1347
+ # 'grue' is written at 0x4291
1348
+ # So, lets abuse that
1349
+ #
1350
+ # Give the short-name a length of 4
1351
+ @zork_memory.force_writeb(0x4290, 2)
1352
+
1353
+ # Now the property list
1354
+ written_size = (2 - 1) << 5
1355
+ property_number = 15
1356
+ @zork_memory.force_writeb(0x4295, written_size + property_number)
1357
+ @zork_memory.force_writew(0x4296, 34567)
1358
+
1359
+ written_size = (1 - 1) << 5
1360
+ property_number = 20
1361
+ @zork_memory.force_writeb(0x4298, written_size + property_number)
1362
+ @zork_memory.force_writeb(0x4299, 123)
1363
+
1364
+ # End list
1365
+ @zork_memory.force_writeb(0x429a, 0)
1366
+
1367
+ @object_table = Gruesome::Z::ObjectTable.new(@zork_memory)
1368
+
1369
+ # need to reinstantiate the processor
1370
+ @processor = Gruesome::Z::Processor.new(@zork_memory)
1371
+ end
1372
+
1373
+ describe "Output Instruction" do
1374
+ before(:each) do
1375
+ # We take over stdout so that we can see what it prints
1376
+ @stdout = $stdout
1377
+ $stdout = StringIO.new
1378
+ end
1379
+
1380
+ after(:each) do
1381
+ $stdout = @stdout
1382
+
1383
+ # The program counter should not be changed
1384
+ @zork_memory.program_counter.should eql(12345)
1385
+ end
1386
+
1387
+ describe "print_obj" do
1388
+ it "should print to stdout out the short name of the object whose index is given as an operand" do
1389
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PRINT_OBJ,
1390
+ [Gruesome::Z::OperandType::LARGE],
1391
+ [3], nil, nil, nil, 0)
1392
+
1393
+ @processor.execute(i)
1394
+ $stdout.string.should eql("grue")
1395
+ end
1396
+ end
1397
+ end
1398
+
1399
+ describe "Branch Instruction" do
1400
+ describe "get_child" do
1401
+ # Note:
1402
+ # - Object 1 has one child: Object 2
1403
+ # - Object 2 has no children
1404
+
1405
+ it "should not branch if the child object index of the requested object is 0" do
1406
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_CHILD,
1407
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1408
+ [2], 128, 2000, true, 0)
1409
+
1410
+ @processor.execute(i)
1411
+ @zork_memory.program_counter.should eql(12345)
1412
+ end
1413
+
1414
+ it "should branch if the child object index exists for the requested object" do
1415
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_CHILD,
1416
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1417
+ [1], 128, 2000, true, 0)
1418
+ @processor.execute(i)
1419
+ @zork_memory.program_counter.should eql(2000)
1420
+ end
1421
+
1422
+ it "should branch if the child object index of the requested object is 0 and condition is negated" do
1423
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_CHILD,
1424
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1425
+ [2], 128, 2000, false, 0)
1426
+
1427
+ @processor.execute(i)
1428
+ @zork_memory.program_counter.should eql(2000)
1429
+ end
1430
+
1431
+ it "should not branch if the child object index exists for the requested object and condition is negated" do
1432
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_CHILD,
1433
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1434
+ [1], 128, 2000, false, 0)
1435
+ @processor.execute(i)
1436
+ @zork_memory.program_counter.should eql(12345)
1437
+ end
1438
+
1439
+
1440
+ it "should store the child object index to the destination variable" do
1441
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_CHILD,
1442
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1443
+ [1], 128, 2000, true, 0)
1444
+ @processor.execute(i)
1445
+ @zork_memory.readv(128).should eql(2)
1446
+ end
1447
+ end
1448
+
1449
+ describe "get_sibling" do
1450
+ # Note:
1451
+ # - Object 1 has no siblings
1452
+ # - Object 2 has one sibling: Object 3
1453
+
1454
+ it "should not branch if the sibling object index of the requested object is 0" do
1455
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_SIBLING,
1456
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1457
+ [1], 128, 2000, true, 0)
1458
+
1459
+ @processor.execute(i)
1460
+ @zork_memory.program_counter.should eql(12345)
1461
+ end
1462
+
1463
+ it "should branch if the sibling object index exists for the requested object" do
1464
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_SIBLING,
1465
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1466
+ [2], 128, 2000, true, 0)
1467
+ @processor.execute(i)
1468
+ @zork_memory.program_counter.should eql(2000)
1469
+ end
1470
+
1471
+ it "should branch if the sibling object index of the requested object is 0 and condition is negated" do
1472
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_SIBLING,
1473
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1474
+ [1], 128, 2000, false, 0)
1475
+
1476
+ @processor.execute(i)
1477
+ @zork_memory.program_counter.should eql(2000)
1478
+ end
1479
+
1480
+ it "should not branch if the sibling object index exists for the requested object and condition is negated" do
1481
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_SIBLING,
1482
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1483
+ [2], 128, 2000, false, 0)
1484
+ @processor.execute(i)
1485
+ @zork_memory.program_counter.should eql(12345)
1486
+ end
1487
+
1488
+
1489
+ it "should store the sibling object index to the destination variable" do
1490
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_SIBLING,
1491
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1492
+ [2], 128, 2000, true, 0)
1493
+ @processor.execute(i)
1494
+ @zork_memory.readv(128).should eql(3)
1495
+ end
1496
+ end
1497
+
1498
+ describe "get_parent" do
1499
+ # Note:
1500
+ # - Object 1 has no parents
1501
+ # - Object 2 has a parent: Object 1
1502
+
1503
+ it "should store the parent object index to the destination variable" do
1504
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PARENT,
1505
+ [Gruesome::Z::OperandType::LARGE],
1506
+ [2], 128, 2000, true, 0)
1507
+ @processor.execute(i)
1508
+ @zork_memory.readv(128).should eql(1)
1509
+ end
1510
+ end
1511
+
1512
+ describe "jin" do
1513
+ # Note:
1514
+ # - Object 1 has no parents
1515
+ # - Object 2 has a parent: Object 1
1516
+
1517
+ it "should not branch if the given object is not a child of the other" do
1518
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JIN,
1519
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1520
+ [1, 2], 128, 2000, true, 0)
1521
+
1522
+ @processor.execute(i)
1523
+ @zork_memory.program_counter.should eql(12345)
1524
+ end
1525
+
1526
+ it "should branch if the given object is a child of the other" do
1527
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JIN,
1528
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1529
+ [2,1], 128, 2000, true, 0)
1530
+ @processor.execute(i)
1531
+ @zork_memory.program_counter.should eql(2000)
1532
+ end
1533
+
1534
+ it "should branch if the given object is not a child of the other and condition is negated" do
1535
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JIN,
1536
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1537
+ [1, 2], 128, 2000, false, 0)
1538
+
1539
+ @processor.execute(i)
1540
+ @zork_memory.program_counter.should eql(2000)
1541
+ end
1542
+
1543
+ it "should not branch if the given object is a child of the other and condition is negated" do
1544
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::JIN,
1545
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1546
+ [2,1], 128, 2000, false, 0)
1547
+ @processor.execute(i)
1548
+ @zork_memory.program_counter.should eql(12345)
1549
+ end
1550
+ end
1551
+
1552
+ describe "test_attr" do
1553
+ it "should branch if the object has the attribute set" do
1554
+ @object_table.object_has_attribute?(1, 10).should eql(true)
1555
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST_ATTR,
1556
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1557
+ [1, 10], nil, 2000, true, 0)
1558
+
1559
+ @processor.execute(i)
1560
+ @zork_memory.program_counter.should eql(2000)
1561
+ end
1562
+
1563
+ it "should not branch if the object does not have the attribute set" do
1564
+ @object_table.object_has_attribute?(1, 11).should eql(false)
1565
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST_ATTR,
1566
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1567
+ [1, 11], nil, 2000, true, 0)
1568
+
1569
+ @processor.execute(i)
1570
+ @zork_memory.program_counter.should eql(12345)
1571
+ end
1572
+
1573
+ it "should not branch if the first operand has all bits set that the second has set and condition is negated" do
1574
+ @object_table.object_has_attribute?(1, 10).should eql(true)
1575
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST_ATTR,
1576
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1577
+ [1, 10], nil, 2000, false, 0)
1578
+
1579
+ @processor.execute(i)
1580
+ @zork_memory.program_counter.should eql(12345)
1581
+ end
1582
+
1583
+ it "should branch if the first operand does not have all bits set that the second has set and condition is negated" do
1584
+ @object_table.object_has_attribute?(1, 11).should eql(false)
1585
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::TEST_ATTR,
1586
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1587
+ [1, 11], nil, 2000, false, 0)
1588
+
1589
+ @processor.execute(i)
1590
+ @zork_memory.program_counter.should eql(2000)
1591
+ end
1592
+
1593
+ end
1594
+ end
1595
+
1596
+ describe "Instruction" do
1597
+ after(:each) do
1598
+ # The program counter should not be changed
1599
+ @zork_memory.program_counter.should eql(12345)
1600
+ end
1601
+
1602
+ describe "put_prop" do
1603
+ it "should set the property in the object's list when it is a byte" do
1604
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PUT_PROP,
1605
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1606
+ [3, 20, 234], nil, nil, nil, 0)
1607
+ @processor.execute(i)
1608
+ @object_table.object_get_property_word(3, 20).should eql(234)
1609
+ end
1610
+
1611
+ it "should store the least significant part of the value to the property in the object's list when the property is a byte" do
1612
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PUT_PROP,
1613
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1614
+ [3, 20, 0x5432], nil, nil, nil, 0)
1615
+ @processor.execute(i)
1616
+ @object_table.object_get_property_word(3, 20).should eql(0x32)
1617
+ end
1618
+
1619
+ it "should set the property in the object's list when it is a word" do
1620
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PUT_PROP,
1621
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1622
+ [3, 15, 54321], nil, nil, nil, 0)
1623
+ @processor.execute(i)
1624
+ @object_table.object_get_property_word(3, 15).should eql(54321)
1625
+ end
1626
+
1627
+ # XXX: Check for halt when property does not exist
1628
+ end
1629
+
1630
+ describe "get_prop" do
1631
+ it "should retrieve the property in the object's list when it is a byte" do
1632
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP,
1633
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1634
+ [3, 20], 128, nil, nil, 0)
1635
+ @processor.execute(i)
1636
+ @zork_memory.readv(128).should eql(123)
1637
+ end
1638
+
1639
+ it "should retrieve the property in the object's list when it is a word" do
1640
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP,
1641
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1642
+ [3, 15], 128, nil, nil, 0)
1643
+ @processor.execute(i)
1644
+ @zork_memory.readv(128).should eql(34567)
1645
+ end
1646
+
1647
+ it "should retrieve the default property when the object list does not have the property listed" do
1648
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP,
1649
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1650
+ [3, 28], 128, nil, nil, 0)
1651
+ @processor.execute(i)
1652
+ @zork_memory.readv(128).should eql(28)
1653
+ end
1654
+ end
1655
+
1656
+ describe "get_prop_addr" do
1657
+ it "should retrieve the address of the property in the object's list" do
1658
+ @zork_memory.writev(128, 11111)
1659
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP_ADDR,
1660
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1661
+ [3, 20], 128, nil, nil, 0)
1662
+ @processor.execute(i)
1663
+ @zork_memory.readv(128).should eql(0x4299)
1664
+ end
1665
+
1666
+ it "should set the variable given to 0 when the property is not in the object's list" do
1667
+ @zork_memory.writev(128, 11111)
1668
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP_ADDR,
1669
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1670
+ [3, 28], 128, nil, nil, 0)
1671
+ @processor.execute(i)
1672
+ @zork_memory.readv(128).should eql(0)
1673
+ end
1674
+ end
1675
+
1676
+ describe "get_prop_len" do
1677
+ it "should retrieve the property data size for the byte property in the object's list" do
1678
+ @zork_memory.writev(128, 11111)
1679
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP_LEN,
1680
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1681
+ [0x4299], 128, nil, nil, 0)
1682
+ @processor.execute(i)
1683
+ @zork_memory.readv(128).should eql(1)
1684
+ end
1685
+
1686
+ it "should retrieve the property data size for the word property in the object's list" do
1687
+ @zork_memory.writev(128, 11111)
1688
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP_LEN,
1689
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1690
+ [0x4296], 128, nil, nil, 0)
1691
+ @processor.execute(i)
1692
+ @zork_memory.readv(128).should eql(2)
1693
+ end
1694
+
1695
+ it "should return 0 when the property address given is 0" do
1696
+ @zork_memory.writev(128, 11111)
1697
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::GET_PROP_LEN,
1698
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1699
+ [0], 128, nil, nil, 0)
1700
+ @processor.execute(i)
1701
+ @zork_memory.readv(128).should eql(0)
1702
+ end
1703
+ end
1704
+
1705
+ describe "insert_obj" do
1706
+ it "should insert the object given as the child of the other object given" do
1707
+ @object_table.object_get_child(2).should eql(0)
1708
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INSERT_OBJ,
1709
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1710
+ [3, 2], nil, nil, nil, 0)
1711
+ @processor.execute(i)
1712
+
1713
+ @object_table.object_get_child(2).should eql(3)
1714
+ end
1715
+
1716
+ it "should set the sibling of the given object as the previous child of the other object given" do
1717
+ @object_table.object_get_sibling(3).should eql(2)
1718
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INSERT_OBJ,
1719
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1720
+ [3, 2], nil, nil, nil, 0)
1721
+ @processor.execute(i)
1722
+
1723
+ @object_table.object_get_sibling(3).should eql(0)
1724
+ end
1725
+ end
1726
+
1727
+ describe "remove_obj" do
1728
+ it "should clear the parent of the given object" do
1729
+ @object_table.object_get_parent(2).should eql(1)
1730
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::REMOVE_OBJ,
1731
+ [Gruesome::Z::OperandType::LARGE],
1732
+ [2], nil, nil, nil, 0)
1733
+ @processor.execute(i)
1734
+
1735
+ @object_table.object_get_parent(2).should eql(0)
1736
+ end
1737
+ end
1738
+
1739
+
1740
+ describe "clear_attr" do
1741
+ it "should clear the attribute when it exists in the object attribute list without altering other attributes" do
1742
+ @object_table.object_has_attribute?(2, 25).should eql(true)
1743
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CLEAR_ATTR,
1744
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1745
+ [2, 25], nil, nil, nil, 0)
1746
+ @processor.execute(i)
1747
+
1748
+ @object_table.object_has_attribute?(2, 25).should eql(false)
1749
+ @object_table.object_has_attribute?(2, 31).should eql(true)
1750
+ end
1751
+
1752
+ it "should do nothing when the attribute is already cleared" do
1753
+ @object_table.object_has_attribute?(2, 26).should eql(false)
1754
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::CLEAR_ATTR,
1755
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1756
+ [2, 26], nil, nil, nil, 0)
1757
+ @processor.execute(i)
1758
+
1759
+ @object_table.object_has_attribute?(2, 26).should eql(false)
1760
+ @object_table.object_has_attribute?(2, 31).should eql(true)
1761
+ end
1762
+ end
1763
+
1764
+ describe "set_attr" do
1765
+ it "should set the attribute when it exists in the object attribute list without altering other attributes" do
1766
+ @object_table.object_has_attribute?(2, 26).should eql(false)
1767
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::SET_ATTR,
1768
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1769
+ [2, 26], nil, nil, nil, 0)
1770
+ @processor.execute(i)
1771
+
1772
+ @object_table.object_has_attribute?(2, 26).should eql(true)
1773
+ @object_table.object_has_attribute?(2, 31).should eql(true)
1774
+ end
1775
+
1776
+ it "should do nothing when the attribute is already set" do
1777
+ @object_table.object_has_attribute?(2, 25).should eql(true)
1778
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::SET_ATTR,
1779
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1780
+ [2, 25], nil, nil, nil, 0)
1781
+ @processor.execute(i)
1782
+
1783
+ @object_table.object_has_attribute?(2, 25).should eql(true)
1784
+ @object_table.object_has_attribute?(2, 31).should eql(true)
1785
+ end
1786
+ end
1787
+ end
1788
+ end
1789
+
1790
+ describe "Instruction" do
1791
+ after(:each) do
1792
+ # The program counter should not be changed
1793
+ @zork_memory.program_counter.should eql(12345)
1794
+ end
1795
+
1796
+ describe "store" do
1797
+ it "should store the value into the variable referenced by the operand" do
1798
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::STORE,
1799
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
1800
+ [128, -13+65536], nil, nil, nil, 0)
1801
+
1802
+ @processor.execute(i)
1803
+
1804
+ @zork_memory.readv(128).should eql(-13+65536)
1805
+ end
1806
+
1807
+ it "should write to the stack in-place when variable %00 is used" do
1808
+ @zork_memory.writev(0, 11111)
1809
+ @zork_memory.writev(0, 12345)
1810
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::STORE,
1811
+ [Gruesome::Z::OperandType::VARIABLE, Gruesome::Z::OperandType::LARGE],
1812
+ [0, -13+65536], nil, nil, nil, 0)
1813
+
1814
+ @processor.execute(i)
1815
+
1816
+ @zork_memory.readv(0).should eql(-13+65536)
1817
+ @zork_memory.readv(0).should eql(11111)
1818
+ end
1819
+ end
1820
+
1821
+ describe "storeb" do
1822
+ it "should store the byte given into the byte address given by the operands providing base and offset" do
1823
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::STOREB,
1824
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1825
+ [2000, 100, 123], nil, nil, nil, 0)
1826
+
1827
+ @processor.execute(i)
1828
+
1829
+ @zork_memory.force_readb(2000+100).should eql(123)
1830
+ end
1831
+ end
1832
+
1833
+ describe "storew" do
1834
+ it "should store the word given into the byte address given by the operands providing base and offset" do
1835
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::STOREW,
1836
+ [Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE, Gruesome::Z::OperandType::LARGE],
1837
+ [2000, 100, 12345], nil, nil, nil, 0)
1838
+
1839
+ @processor.execute(i)
1840
+
1841
+ @zork_memory.force_readw(2000+200).should eql(12345)
1842
+ end
1843
+ end
1844
+
1845
+ describe "pull" do
1846
+ it "should pop the value from the stack that was most recently pushed" do
1847
+ @zork_memory.writev(0, 23456)
1848
+ @zork_memory.writev(0, 34567)
1849
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PULL,
1850
+ [Gruesome::Z::OperandType::VARIABLE], [128], nil, nil, nil, 0)
1851
+
1852
+ @processor.execute(i)
1853
+ @zork_memory.readv(128).should eql(34567)
1854
+ @zork_memory.readv(0).should eql(23456)
1855
+ end
1856
+
1857
+ it "should not change the stack if the given variable is %00" do
1858
+ @zork_memory.writev(0, 23456)
1859
+ @zork_memory.writev(0, 34567)
1860
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PULL,
1861
+ [Gruesome::Z::OperandType::VARIABLE], [0], nil, nil, nil, 0)
1862
+
1863
+ @processor.execute(i)
1864
+ @zork_memory.readv(0).should eql(34567)
1865
+ @zork_memory.readv(0).should eql(23456)
1866
+ end
1867
+ end
1868
+
1869
+ describe "push" do
1870
+ it "should push the value on the stack such that a read of variable %00 will yield that value" do
1871
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::PUSH,
1872
+ [Gruesome::Z::OperandType::LARGE],
1873
+ [23456], nil, nil, nil, 0)
1874
+
1875
+ @processor.execute(i)
1876
+
1877
+ @zork_memory.readv(0).should eql(23456)
1878
+ end
1879
+ end
1880
+
1881
+ describe "pop" do
1882
+ it "should throw away the most recently pushed item on the stack" do
1883
+ @zork_memory.writev(0, 23456)
1884
+ @zork_memory.writev(0, 34567)
1885
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::POP,
1886
+ [], [], nil, nil, nil, 0)
1887
+
1888
+ @processor.execute(i)
1889
+ @zork_memory.readv(0).should eql(23456)
1890
+ end
1891
+ end
1892
+
1893
+ describe "dec" do
1894
+ it "should decrement the value in the variable given as the operand" do
1895
+ @zork_memory.writev(128, 12345)
1896
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC,
1897
+ [Gruesome::Z::OperandType::VARIABLE],
1898
+ [128], nil, nil, nil, 0)
1899
+ @processor.execute(i)
1900
+ @zork_memory.readv(128).should eql(12344)
1901
+ end
1902
+
1903
+ it "should consider the value signed and decrement 0 to -1" do
1904
+ @zork_memory.writev(128, 0)
1905
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC,
1906
+ [Gruesome::Z::OperandType::VARIABLE],
1907
+ [128], nil, nil, nil, 0)
1908
+ @processor.execute(i)
1909
+ @zork_memory.readv(128).should eql(-1+65536)
1910
+ end
1911
+
1912
+ it "should edit the value on the stack in place" do
1913
+ @zork_memory.writev(0, 11111)
1914
+ @zork_memory.writev(0, 12345)
1915
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::DEC,
1916
+ [Gruesome::Z::OperandType::VARIABLE],
1917
+ [0], nil, nil, nil, 0)
1918
+ @processor.execute(i)
1919
+ @zork_memory.readv(0).should eql(12344)
1920
+ @zork_memory.readv(0).should eql(11111)
1921
+ end
1922
+ end
1923
+
1924
+ describe "inc" do
1925
+ it "should increment the value in the variable given as the operand" do
1926
+ @zork_memory.writev(128, 12345)
1927
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC,
1928
+ [Gruesome::Z::OperandType::VARIABLE],
1929
+ [128], nil, nil, nil, 0)
1930
+ @processor.execute(i)
1931
+ @zork_memory.readv(128).should eql(12346)
1932
+ end
1933
+
1934
+ it "should consider the value signed and increment -1 to 0" do
1935
+ @zork_memory.writev(128, -1+65536)
1936
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC,
1937
+ [Gruesome::Z::OperandType::VARIABLE],
1938
+ [128], nil, nil, nil, 0)
1939
+ @processor.execute(i)
1940
+ @zork_memory.readv(128).should eql(0)
1941
+ end
1942
+
1943
+ it "should edit the value on the stack in place" do
1944
+ @zork_memory.writev(0, 11111)
1945
+ @zork_memory.writev(0, 12345)
1946
+ i = Gruesome::Z::Instruction.new(Gruesome::Z::Opcode::INC,
1947
+ [Gruesome::Z::OperandType::VARIABLE],
1948
+ [0], nil, nil, nil, 0)
1949
+ @processor.execute(i)
1950
+ @zork_memory.readv(0).should eql(12346)
1951
+ @zork_memory.readv(0).should eql(11111)
1952
+ end
1953
+ end
1954
+ end
1955
+ end
1956
+ end