1# ZCode operations file
2# This file is 'partially evaluated' to generate a (long!) C source file
3# that should provide a performance boost to the execution of instructions
4
5# YMMV, depending on your compilers optimisation abilities
6
7####                           ----// 888 \\----                           ####
8# 2OPs
9
10# je is weird and is actually treated as a VAR op instead of a 2OP
11OPCODE "je"         2OP:0x01 BRANCH REALLYVAR VERSION all
12%{
13  result = 1;
14  for (x=1; x<argblock.n_args; x++)
15    {
16      if (argblock.arg[0] == argblock.arg[x])
17	{
18	  goto je_true;
19	}
20    }
21  result = 0;
22je_true:
23  dobranch;
24%}
25
26OPCODE "jl"         2OP:0x02 BRANCH VERSION all
27%{
28  result = arg1<arg2;
29  dobranch;
30%}
31
32OPCODE "jg"         2OP:0x03 BRANCH VERSION all
33%{
34  result = arg1>arg2;
35  dobranch;
36%}
37
38OPCODE "dec_chk"    2OP:0x04 BRANCH VERSION all
39%{
40  ZWord var;
41
42  var = GetVarNoPop(arg1);
43  result = (--var) < arg2;
44  store_nopush(stack, arg1, var);
45  dobranch;
46%}
47
48OPCODE "inc_chk"    2OP:0x05 BRANCH VERSION all
49%{
50  ZWord var;
51
52  var = GetVarNoPop(arg1);
53  result = (++var) > arg2;
54  store_nopush(stack, arg1, var);
55  dobranch;
56%}
57
58OPCODE "jin"        2OP:0x06 BRANCH VERSION 1,2,3
59%{
60  ZByte* obj;
61
62  obj = Obj3(uarg1);
63  result = obj[parent_3] == arg2;
64  dobranch;
65%}
66
67OPCODE "jin"        2OP:0x06 BRANCH VERSION 4,5,6,7,8
68%{
69  ZByte* obj;
70
71  obj = Obj4(uarg1);
72  result = GetParent4(obj) == arg2;
73  dobranch;
74%}
75
76OPCODE "test"       2OP:0x07 BRANCH VERSION all
77%{
78  result = ((ZUWord)arg1&(ZUWord)arg2)==(ZUWord)arg2;
79  dobranch;
80%}
81
82OPCODE "test_attr"     2OP:0x0a BRANCH VERSION 1,2,3
83%{
84  ZByte* obj;
85  int byte, bit;
86
87  if (uarg1>255) /* || uarg1 == 0) HHGG bug */
88    zmachine_fatal("Object %i out of range", uarg1);
89  if (uarg2>31)
90    zmachine_fatal("Attribute out of range");
91
92  byte = uarg2>>3;
93  bit  = uarg2&7;
94
95  if (uarg1 != 0) {
96    obj = Obj3(uarg1);
97
98#ifdef TRACKING
99    if (machine.track_attributes)
100      tracking_print("Testing attribute %i of object \"%s\" (%s)",
101	  	     attr, tracking_object(uarg1),
102		     (obj[byte]&(0x80>>bit))?"Set":"Unset");
103#endif
104
105    result = ((obj[byte]&(0x80>>bit)) != 0);
106  } else {
107    result = 0;
108  }
109  dobranch;
110%}
111
112OPCODE "test_attr"     2OP:0x0a BRANCH VERSION 4,5,6,7,8
113%{
114  ZByte* obj;
115  int byte, bit;
116
117  if (uarg1 == 0)
118    {
119      zmachine_warning("Object 0 has no attributes");
120      result = 0;
121      dobranch;
122      goto loop;
123    }
124  if (uarg2 == 48)
125    {
126      zmachine_warning("Attempt to test attribute 48");
127      goto loop;
128    }
129  if (uarg2>47)
130    zmachine_fatal("Attribute out of range");
131
132  byte = uarg2>>3;
133  bit  = uarg2&7;
134
135  obj = Obj4(uarg1);
136
137#ifdef TRACKING
138  if (machine.track_attributes)
139    tracking_print("Testing attribute %i of object \"%s\" (%s)",
140		   arg2, tracking_object(arg1),
141		   (obj[byte]&(0x80>>bit))?"Set":"Unset");
142#endif
143
144  result = (obj[byte]&(0x80>>bit)) != 0;
145  dobranch;
146%}
147
148OPCODE "set_attr"      2OP:0x0b        VERSION 1,2,3
149%{
150  ZByte* obj;
151  int byte, bit;
152
153  if (uarg1 == 0) {
154	/* Technically this is a bug in the game, but The Witness accidentally does this so we allow it in v3 games */
155	zmachine_warning("Cannot set attributes of object 0");
156  } else {
157	  if (uarg1>255 || uarg1 == 0)
158	    zmachine_fatal("Object %i out of range", uarg1);
159	  if (uarg2>31)
160	    zmachine_fatal("Attribute out of range");
161
162#ifdef TRACKING
163	  if (machine.track_attributes)
164	    tracking_print("Setting attribute %i of object \"%s\"",
165			   uarg2, tracking_object(uarg1));
166#endif
167
168	  byte = uarg2>>3;
169	  bit  = uarg2&7;
170
171	  obj = Obj3(uarg1);
172	  obj[byte] |= 0x80>>bit;
173  }
174%}
175
176OPCODE "set_attr"      2OP:0x0b        VERSION 4,5,6,7,8
177%{
178  ZByte* obj;
179  int byte, bit;
180
181  if (uarg1 == 0)
182    zmachine_fatal("Object 0 cannot be altered");
183  if (uarg2>47)
184    zmachine_fatal("Attribute out of range");
185
186#ifdef DEBUG
187  printf_debug("Setting attribute %x of object #%x\n", uarg2, uarg1);
188#endif
189
190#ifdef TRACKING
191  if (machine.track_attributes)
192    tracking_print("Setting attribute %i of object \"%s\"",
193		   uarg2, tracking_object(uarg1));
194#endif
195
196  byte = uarg2>>3;
197  bit  = uarg2&7;
198
199  obj = Obj4(uarg1);
200  obj[byte] |= (0x80>>bit);
201%}
202
203OPCODE "clear_attr"    2OP:0x0c        VERSION 1,2,3
204%{
205  ZByte* obj;
206  int byte, bit;
207
208  if (uarg1 == 0) {
209	/* Technically this is a bug in the game, but The Witness accidentally does this so we allow it in v3 games */
210	zmachine_warning("Cannot clear the attributs of object 0");
211  } else {
212	  if (uarg1>255 || uarg1 == 0)
213	    zmachine_fatal("Object %i out of range", uarg1);
214	  if (uarg2>31)
215	    zmachine_fatal("Attribute out of range");
216
217#ifdef TRACKING
218	  if (machine.track_attributes)
219	    tracking_print("Setting attribute %i of object \"%s\"",
220			   uarg2, tracking_object(uarg1));
221#endif
222
223	  byte = uarg2>>3;
224	  bit  = uarg2&7;
225
226	  obj = Obj3(uarg1);
227	  obj[byte] &= ~(0x80>>bit);
228  }
229%}
230
231OPCODE "clear_attr"    2OP:0x0c        VERSION 4,5,6,7,8
232%{
233  ZByte* obj;
234  int byte, bit;
235
236  if (uarg1 == 0)
237    zmachine_fatal("Object 0 cannot be altered");
238  if (uarg2>47)
239    zmachine_fatal("Attribute out of range");
240
241#ifdef DEBUG
242  printf_debug("Setting attribute %x of object #%x\n", uarg2, uarg1);
243#endif
244
245#ifdef TRACKING
246  if (machine.track_attributes)
247    tracking_print("Setting attribute %i of object \"%s\"",
248		   uarg2, tracking_object(uarg1));
249#endif
250
251  byte = uarg2>>3;
252  bit  = uarg2&7;
253
254  obj = Obj4(uarg1);
255  obj[byte] &= ~(0x80>>bit);
256%}
257
258OPCODE "insert_obj"    2OP:0x0e        VERSION 1,2,3
259%{
260  ZByte* src_obj;
261  ZByte* dest_obj;
262  ZByte* tmp;
263
264  if (uarg1>255 || uarg1 == 0)
265    zmachine_fatal("Object %i out of range", uarg1);
266  if (uarg2>255)
267    zmachine_fatal("Object %i out of range", uarg2);
268
269  /* Get the address of the object */
270  src_obj = Obj3(uarg1);
271
272  if (src_obj[parent_3] != 0)
273    {
274      /* Get the address of its parent */
275      tmp = Obj3(src_obj[parent_3]);
276
277      /*
278       * If the object is a direct child of its parent, set the new child
279       * to the object's sibling
280       */
281      if (tmp[child_3] == uarg1)
282	{
283	  tmp[child_3] = src_obj[sibling_3];
284	}
285      else
286	{
287	  /* Find the object which is a sibling with this object */
288	  tmp = Obj3(tmp[child_3]);
289
290	  while (tmp[sibling_3] != uarg1 && tmp[sibling_3] != 0)
291	    {
292	      tmp = Obj3(tmp[sibling_3]);
293	    }
294
295	  if (tmp[sibling_3] == 0)
296	    zmachine_fatal("Corrupt object tree (object is not a child of its parent)");
297
298	  /* Set its sibling to the sibling of the object */
299	  tmp[sibling_3] = src_obj[sibling_3];
300	}
301    }
302
303  if (uarg2 != 0)
304    {
305      /* Set the new parent's child to be this object */
306      dest_obj = Obj3(uarg2);
307      src_obj[sibling_3] = dest_obj[child_3];
308      dest_obj[child_3]  = uarg1;
309    }
310  else
311    src_obj[sibling_3] = 0;
312
313  src_obj[parent_3] = uarg2;
314%}
315
316OPCODE "insert_obj"    2OP:0x0e        VERSION 4,5,6,7,8
317%{
318  ZByte* src_obj;
319  ZByte* dest_obj;
320  ZByte* tmp;
321
322#ifdef DEBUG
323  printf_debug("Inserting object %i into %i\n", uarg1, uarg2);
324#endif
325
326  if (uarg1 == 0)
327    zmachine_fatal("Object 0 cannot be inserted");
328
329  src_obj = Obj4(uarg1);
330
331  if (GetParent4(src_obj) != 0)
332    {
333      ZUWord sibling;
334
335      /* Get the address of the old parent */
336      tmp = Obj4(GetParent4(src_obj));
337
338      if (GetChild4(tmp) == uarg1)
339	{
340	  sibling = GetSibling4(src_obj);
341	  /*
342	   * Object is a direct child, so we make the new child this
343	   * object's sibling
344	   */
345	  tmp[child_4] = sibling>>8;
346	  tmp[child_4+1] = sibling;
347	}
348      else
349	{
350	  ZUWord our_sibling;
351
352	  /* Find the object which is a sibling with this object */
353	  tmp = Obj4(GetChild4(tmp));
354
355	  sibling = GetSibling4(tmp);
356	  while (sibling != uarg1 && sibling != 0)
357	    {
358	      tmp = Obj4(sibling);
359	      sibling = GetSibling4(tmp);
360	    }
361
362	  if (sibling == 0)
363	    zmachine_fatal("Corrupt object tree (object is not a child of its parent)");
364
365	  /* Set its sibling to our sibling */
366	  our_sibling = GetSibling4(src_obj);
367	  tmp[sibling_4] = our_sibling>>8;
368	  tmp[sibling_4+1] = our_sibling;
369	}
370    }
371
372  if (uarg2 != 0)
373    {
374      ZUWord kid;
375
376      /* Set the new parent's child to be this object */
377      dest_obj = Obj4(uarg2);
378      kid = GetChild4(dest_obj);
379      src_obj[sibling_4] = kid>>8;
380      src_obj[sibling_4+1] = kid;
381      dest_obj[child_4] = uarg1>>8;
382      dest_obj[child_4+1] = uarg1;
383    }
384  else
385    {
386      src_obj[sibling_4] = 0;
387      src_obj[sibling_4+1] = 0;
388    }
389
390  src_obj[parent_4] = uarg2>>8;
391  src_obj[parent_4+1] = uarg2;
392%}
393
394OPCODE "get_prop"      2OP:0x11 STORE  VERSION 1,2,3
395%{
396  struct prop* p;
397
398  if (uarg1>255 || uarg1 == 0)
399    zmachine_fatal("Object %i out of range", uarg1);
400  arg2 &= 0x1f;
401  if (uarg2>31 || uarg2 == 0)
402    zmachine_fatal("Property %i out of range", uarg2);
403
404  p = get_object_prop_3(uarg1, uarg2);
405
406  switch (p->size)
407    {
408    case 1:
409      store(stack, st, p->prop[0]);
410      break;
411
412    case 2:
413      store(stack, st, (p->prop[0]<<8)|p->prop[1]);
414      break;
415
416    default:
417      zmachine_fatal("Size %i is invalid for get_prop\n", p->size);
418    }
419%}
420
421OPCODE "get_prop"      2OP:0x11 STORE  VERSION 4,5,6,7,8
422%{
423  struct prop* p;
424
425  if (uarg1 == 0)
426    zmachine_warning("Object 0 has no properties");
427  if (uarg2>63 || uarg2 == 0)
428    zmachine_fatal("Property %i out of range", uarg2);
429
430#ifdef DEBUG
431  printf_debug("Get_prop: object %i prop %i\n", uarg1, uarg2);
432#endif
433
434  p = get_object_prop_4(uarg1, uarg2);
435
436  switch (p->size)
437    {
438    case 1:
439      store(stack, st, p->prop[0]);
440      break;
441
442      /*
443       * Hmm, this is against spec, but some Inform games
444       * (Christminster?) seem to use get_prop on properties with
445       * lengths > 2. So we'll let 'em off with a warning
446       */
447    default:
448      zmachine_warning("get_prop used on a property with length %i",
449		       p->size);
450      store(stack, st, (p->prop[-1]<<8)|p->prop[0]); /* Frotz's behaviour */
451      break;
452
453    case 2:
454      store(stack, st, (p->prop[0]<<8)|p->prop[1]);
455      break;
456
457      /* default:
458	 zmachine_fatal("Size %i is invalid for get_prop\n", p->size); */
459    }
460%}
461
462OPCODE "get_prop_addr" 2OP:0x12 STORE  VERSION 1,2,3
463%{
464  ZByte* obj_adr;
465  ZUWord prop_adr;
466  ZByte  size;
467
468  if (uarg1>255)
469    zmachine_fatal("Object %i out of range", uarg1);
470
471  if (uarg1 == 0)
472    {
473      /* Zork 2 has an occasional bug that causes a crash here if we make this error fatal */
474      zmachine_warning("Attempt to get the address of a property for invalid object number 0");
475      store(stack, st, 0);
476      goto loop;
477    }
478
479  arg2 &= 0x1f;
480  if (uarg2 > 31 || uarg2 == 0)
481    {
482      zmachine_warning("Attempt to get address of out of range property %i", uarg2);
483      store(stack, st, 0);
484      goto loop;
485    }
486
487  obj_adr = Obj3(uarg1);
488
489  prop_adr  = (obj_adr[7]<<8)|obj_adr[8];
490  prop_adr += ReadByte(prop_adr)*2 + 1;
491
492  while ((size = ReadByte(prop_adr)) != 0)
493    {
494      if ((size&0x1f) == uarg2)
495	{
496	  store(stack, st, prop_adr + 1);
497	  goto loop;
498	}
499
500      prop_adr += (size>>5) + 2;
501    }
502
503  store(stack, st, 0);
504%}
505
506OPCODE "get_prop_addr" 2OP:0x12 STORE  VERSION 4,5,6,7,8
507%{
508  ZByte* obj_adr;
509  ZUWord prop_adr;
510  struct propinfo* pinfo;
511
512  if (uarg1 == 0)
513    {
514      zmachine_warning("Object 0 has no properties");
515      store(stack, st, 0);
516    }
517  if (uarg2 > 63 || uarg2 == 0)
518    {
519      zmachine_warning("Attempt to get address of out of range property %i", uarg2);
520      store(stack, st, 0);
521    }
522
523  obj_adr = Obj4(uarg1);
524
525  prop_adr = GetPropAddr4(obj_adr);
526  prop_adr += (ReadByte(prop_adr)*2)+1;
527
528  do
529    {
530      pinfo = get_object_propinfo_4(Address(prop_adr));
531
532      if (pinfo->number == uarg2)
533	{
534	  store(stack, st, (ZUWord)(prop_adr+pinfo->header));
535	  goto loop;
536	}
537
538      prop_adr += pinfo->datasize + pinfo->header;
539    }
540  while (pinfo->number != 0);
541
542  store(stack, st, 0);
543%}
544
545OPCODE "get_next_prop" 2OP:0x13 STORE  VERSION 1,2,3
546%{
547  ZByte* obj_adr;
548  ZWord  prop_adr;
549  ZByte  size;
550  int state;
551
552  obj_adr = Obj3(uarg1);
553
554  prop_adr  = (obj_adr[7]<<8)|obj_adr[8];
555  prop_adr += ReadByte(prop_adr)*2 + 1;
556
557  if (uarg2 == 0)
558    {
559      store(stack, st, ReadByte(prop_adr)&0x1f);
560      goto loop;
561    }
562
563  state = 0;
564  while ((size = ReadByte(prop_adr)) != 0)
565    {
566      if (state)
567	{
568	  store(stack, st, size&0x1f);
569	  goto loop;
570	}
571      if ((size&0x1f) == uarg2)
572	{
573	  state = 1;
574	}
575
576      prop_adr += (size>>5) + 2;
577    }
578
579  store(stack, st, 0);
580%}
581
582OPCODE "get_next_prop" 2OP:0x13 STORE  VERSION 4,5,6,7,8
583%{
584  struct prop*     property;
585  struct propinfo* inf;
586
587  if (uarg1 == 0)
588    zmachine_fatal("Object 0 has no properties");
589  if (uarg2 > 63)
590    zmachine_fatal("Property %i out of range", uarg2);
591
592  if (uarg2 == 0)
593    {
594      ZByte* obj_adr;
595      ZUWord prop_adr;
596
597      obj_adr = Obj4(uarg1);
598
599      prop_adr = GetPropAddr4(obj_adr);
600      prop_adr += (ReadByte(prop_adr)*2)+1;
601
602      inf = get_object_propinfo_4(Address(prop_adr));
603      store(stack, st, inf->number);
604      goto loop;
605    }
606
607  property = get_object_prop_4(uarg1, uarg2);
608
609  if (property->isdefault)
610    zmachine_fatal("Can't get next property of a default");
611
612  inf = get_object_propinfo_4(property->prop - property->pad);
613  if (inf->number != 0)
614    {
615      ZByte *next_prop;
616
617      next_prop = property->prop + inf->datasize;
618      inf = get_object_propinfo_4(next_prop);
619
620      store(stack, st, inf->number);
621    }
622  else /* Huh? We retrieved property 0? */
623    zmachine_fatal("Programmer is a spoon");
624%}
625
626OPCODE "store"        2OP:0x0d        VERSION all
627%{
628  store_nopush(stack, arg1, arg2);
629%}
630
631OPCODE "loadw"        2OP:0x0f STORE  VERSION all
632%{
633#ifdef DEBUG
634  printf_debug("Loading word at #%x\n", (ZUWord)arg1+((ZWord)arg2*2));
635#endif
636  store(stack, st, Word((ZUWord)((ZUWord)arg1+((ZWord)arg2*2))));
637%}
638OPCODE "loadb"        2OP:0x10 STORE  VERSION all
639%{
640#ifdef DEBUG
641  printf_debug("Loading byte at #%x\n", (ZUWord)arg1+(ZWord)arg2);
642#endif
643  store(stack, st, ReadByte((ZUWord)((ZUWord)arg1+(ZWord)arg2)));
644%}
645
646OPCODE "or"           2OP:0x08 STORE  VERSION all
647%{ store(stack, st, arg1|arg2); %}
648
649OPCODE "and"          2OP:0x09 STORE  VERSION all
650%{ store(stack, st, arg1&arg2); %}
651
652OPCODE "add"          2OP:0x14 STORE  VERSION all
653%{ store(stack, st, arg1+arg2); %}
654
655OPCODE "sub"          2OP:0x15 STORE  VERSION all
656%{ store(stack, st, arg1-arg2); %}
657
658OPCODE "mul"          2OP:0x16 STORE  VERSION all
659%{ store(stack, st, arg1*arg2); %}
660
661OPCODE "div"          2OP:0x17 STORE  VERSION all
662%{
663  if (arg2 == 0)
664    zmachine_fatal("Division by 0");
665  store(stack, st, arg1/arg2);
666%}
667
668OPCODE "mod"          2OP:0x18 STORE  VERSION all
669%{
670  if (arg2 == 0)
671    zmachine_fatal("Modulo by 0");
672  store(stack, st, arg1%arg2);
673%}
674
675OPCODE "call_2s"      2OP:0x19 CANJUMP STORE VERSION 4,5,6,7,8
676%{
677  ZDWord  new_routine;
678  ZFrame* newframe;
679
680  if (arg1 == 0)
681    {
682      store(stack, st, 0);
683      goto loop;
684    }
685
686  new_routine = UnpackR(uarg1);
687  newframe = call_routine(&pc, stack, new_routine);
688
689  newframe->local[1] = arg2;
690  newframe->flags |= 1;
691
692  newframe->storevar = st;
693%}
694
695OPCODE "call_2n"      2OP:0x1a CANJUMP       VERSION 5,6,7,8
696%{
697  ZDWord  new_routine;
698  ZFrame* newframe;
699
700  if (uarg1 == 0)
701    {
702      goto loop;
703    }
704
705  new_routine = UnpackR(uarg1);
706  newframe = call_routine(&pc, stack, new_routine);
707
708  newframe->local[1] = arg2;
709  newframe->flags |= 1;
710
711  newframe->discard  = 1;
712%}
713
714OPCODE "set_colour"   2OP:0x1b               VERSION 5,7,8
715%{
716#ifdef DEBUG
717  printf_debug("Setting colours to %i, %i\n", arg1, arg2);
718#endif
719  stream_flush_buffer();
720  display_set_colour(convert_colour(arg1), convert_colour(arg2));
721%}
722
723OPCODE "throw"        2OP:0x1c CANJUMP       VERSION 5,6,7,8
724%{
725  if (stack->current_frame == NULL)
726    zmachine_fatal("Throw attempted after function with catch has returned");
727  if (stack->current_frame->frame_num < arg1)
728    zmachine_fatal("Throw attempted after function with catch has returned");
729
730  /* Unroll the stack to the appropriate frame */
731  while (stack->current_frame->frame_num != arg2)
732    {
733      ZFrame* oldframe;
734
735      oldframe = stack->current_frame;
736      stack->current_frame = oldframe->last_frame;
737      stack->stack_size += oldframe->frame_size;
738      stack->stack_top  -= oldframe->frame_size;
739
740      free(oldframe);
741    }
742
743  /* Do a return */
744  goto op_ret;
745%}
746
747####                           ----// 888 \\----                           ####
748# 1OPs
749
750OPCODE "jz"           1OP:0x00 BRANCH       VERSION all
751%{
752  result = arg1==0;
753  dobranch;
754%}
755
756OPCODE "get_sibling"  1OP:0x01 BRANCH STORE VERSION 1,2,3
757%{
758  ZByte* obj;
759
760  obj = Obj3(uarg1);
761  store(stack, st, obj[sibling_3]);
762  result = obj[sibling_3] != 0;
763  dobranch;
764%}
765
766OPCODE "get_child"    1OP:0x02 BRANCH STORE VERSION 1,2,3
767%{
768  ZByte* obj;
769
770  obj = Obj3(uarg1);
771  store(stack, st, obj[child_3]);
772  result = obj[child_3] != 0;
773  dobranch;
774%}
775
776OPCODE "get_parent"   1OP:0x03        STORE VERSION 1,2,3
777%{
778  ZByte* obj;
779
780  obj = Obj3(uarg1);
781  store(stack, st, obj[parent_3]);
782%}
783
784OPCODE "get_prop_len" 1OP:0x04        STORE VERSION 1,2,3
785%{
786  if (arg1 == 0)
787    store(stack, st, 0);
788  else
789    store(stack, st, (machine.memory[uarg1-1]>>5)+1);
790%}
791
792OPCODE "get_sibling"  1OP:0x01 BRANCH STORE VERSION 4,5,6,7,8
793%{
794  ZByte* obj_adr;
795  ZUWord sibling;
796
797  if (uarg1 == 0)
798    {
799      zmachine_warning("Object 0 has no siblings");
800      store(stack, st, 0);
801      goto loop;
802    }
803
804  obj_adr = Obj4(uarg1);
805  sibling = GetSibling4(obj_adr);
806
807  store(stack, st, sibling);
808
809  result = sibling != 0;
810  dobranch;
811%}
812
813OPCODE "get_child"    1OP:0x02 BRANCH STORE VERSION 4,5,6,7,8
814%{
815  ZByte* obj_adr;
816  ZUWord child;
817
818  if (uarg1 == 0)
819    {
820      zmachine_warning("Object 0 has no siblings");
821      store(stack, st, 0);
822      goto loop;
823    }
824
825  obj_adr = Obj4(uarg1);
826  child = GetChild4(obj_adr);
827
828  store(stack, st, child);
829
830  result = child != 0;
831  dobranch;
832%}
833
834OPCODE "get_parent"   1OP:0x03        STORE VERSION 4,5,6,7,8
835%{
836  ZByte* obj_adr;
837  ZUWord parent;
838
839  if (uarg1 == 0)
840    {
841      zmachine_warning("Object 0 has no siblings");
842      store(stack, st, 0);
843      goto loop;
844    }
845
846  obj_adr = Obj4(uarg1);
847  parent = GetParent4(obj_adr);
848
849  store(stack, st, parent);
850%}
851
852OPCODE "get_prop_len" 1OP:0x04        STORE VERSION 4,5,6,7,8
853%{
854  ZByte* p;
855  struct propinfo* inf;
856
857  if (arg1 == 0)
858    store(stack, st, 0);
859  else
860    {
861      p = machine.memory + uarg1;
862      if (p[-1]&0x80)
863        p -= 2;
864      else
865        p -= 1;
866
867      inf = get_object_propinfo_4(p);
868
869      store(stack, st, inf->datasize);
870    }
871%}
872
873OPCODE "inc"          1OP:0x05              VERSION all
874%{
875  ZWord var;
876
877#ifdef DEBUG
878  printf_debug("Inc: increasing variable %i by 1\n", arg1);
879#endif
880
881  var = GetVarNoPop(arg1);
882  var++;
883  store_nopush(stack, arg1, var);
884%}
885
886OPCODE "dec"          1OP:0x06              VERSION all
887%{
888  ZWord var;
889
890#ifdef DEBUG
891  printf_debug("Dec: decreasing variable %i by 1\n", arg1);
892#endif
893
894  var = GetVarNoPop(arg1);
895  var--;
896  store_nopush(stack, arg1, var);
897%}
898
899OPCODE "print_addr"   1OP:0x07              VERSION all
900%{
901  int len;
902
903#ifdef DEBUG
904  printf_debug(">%s<\n", zscii_to_ascii(machine.memory + uarg1, &len));
905#endif
906
907  stream_prints(zscii_to_unicode(machine.memory + uarg1, &len));
908%}
909
910OPCODE "call_1s"      1OP:0x08 CANJUMP STORE VERSION 4,5,6,7,8
911%{
912  ZDWord  new_routine;
913  ZFrame* newframe;
914
915  if (arg1 == 0)
916    {
917      store(stack, st, 0);
918      goto loop;
919    }
920
921  new_routine = UnpackR(arg1);
922  newframe = call_routine(&pc, stack, new_routine);
923
924  newframe->storevar = st;
925%}
926
927OPCODE "remove_obj"   1OP:0x09              VERSION 1,2,3
928%{
929  arg2 = 0;
930  goto op_insert_obj_123;
931%}
932
933OPCODE "remove_obj"   1OP:0x09              VERSION 4,5,6,7,8
934%{
935  arg2 = 0;
936  goto op_insert_obj_45678;
937%}
938
939OPCODE "print_obj"    1OP:0x0a              VERSION 1,2,3
940%{
941  ZByte* obj;
942  ZByte* prop;
943  int len;
944
945  obj = Obj3(uarg1);
946  prop = machine.memory + ((obj[7]<<8)|obj[8]) + 1;
947
948#ifdef DEBUG
949  printf_debug(">%s<\n", zscii_to_ascii(prop, &len));
950#endif
951
952  stream_prints(zscii_to_unicode(prop, &len));
953%}
954
955OPCODE "print_obj"    1OP:0x0a              VERSION 4,5,6,7,8
956%{
957  ZByte* obj;
958  ZByte* prop;
959  int len;
960
961  obj = Obj4(uarg1);
962  prop = Address((ZUWord)GetPropAddr4(obj)+1);
963
964#ifdef DEBUG
965  printf_debug(">%s<\n", zscii_to_ascii(prop, &len));
966#endif
967
968  stream_prints(zscii_to_unicode(prop, &len));
969%}
970
971OPCODE "ret"          1OP:0x0b CANJUMP      VERSION all
972%{
973  ZFrame* oldframe;
974  int     end_func;
975
976  oldframe = stack->current_frame;
977  stack->current_frame = oldframe->last_frame;
978  stack->stack_top  -= oldframe->frame_size;
979  stack->stack_size += oldframe->frame_size;
980
981  pc = oldframe->ret;
982
983  if (oldframe->discard == 0)
984    store(stack, oldframe->storevar, arg1);
985
986  if (oldframe->v4read != NULL)
987    (oldframe->v4read)(&pc, stack, &oldframe->readblock);
988  if (oldframe->v5read != NULL)
989    {
990#ifdef DEBUG
991      printf_debug("Stack: Calling V5 callback routine\n");
992#endif
993      (oldframe->v5read)(&pc, stack, &oldframe->readblock, oldframe->readstore);
994      end_func = 1;
995    }
996
997#ifdef DEBUG
998  if (oldframe->discard == 0)
999    printf_debug("Returned %i into V%x\n", arg1, oldframe->storevar);
1000  if (stack->current_frame != NULL)
1001    printf_debug("Stack: returned, discarded %i outstanding items (stack top now #%x, size %i, frame usage %i)\n",
1002	   oldframe->frame_size, stack->stack_top, stack->stack_size,
1003	   stack->current_frame!=NULL?stack->current_frame->frame_size:-1);
1004#endif
1005
1006  end_func = oldframe->end_func;
1007
1008  free(oldframe);
1009
1010  if (stack->current_frame && stack->current_frame->break_on_return)
1011    {
1012	  /* Treat as a breakpoint */
1013	  debug_run_breakpoint(pc);
1014	}
1015
1016  if (end_func)
1017    {
1018#ifdef DEBUG
1019      printf_debug("Stack: Exit from interpreter loop\n");
1020#endif
1021      return;
1022    }
1023%}
1024
1025OPCODE "jump"         1OP:0x0c CANJUMP      VERSION all
1026%{ pc += arg1-2; %}
1027
1028OPCODE "print_paddr"  1OP:0x0d              VERSION 1,2,3
1029%{
1030  int len;
1031
1032#ifdef DEBUG
1033  printf_debug(">%s<\n", zscii_to_ascii(machine.memory + (((ZDWord)uarg1)<<1), &len));
1034#endif
1035
1036  stream_prints(zscii_to_unicode(machine.memory + (((ZDWord)uarg1)<<1), &len));
1037%}
1038
1039OPCODE "print_paddr"  1OP:0x0d              VERSION 4,5,6,7,8
1040%{
1041  int len;
1042
1043#ifdef DEBUG
1044  printf_debug(">%s<\n", zscii_to_ascii(machine.memory + UnpackS(uarg1), &len));
1045#endif
1046
1047  stream_prints(zscii_to_unicode(machine.memory + UnpackS(uarg1), &len));
1048%}
1049
1050OPCODE "load"         1OP:0x0e        STORE VERSION all
1051%{
1052  store(stack, st, GetVarNoPop(uarg1));
1053%}
1054
1055OPCODE "not"          1OP:0x0f        STORE VERSION 1,2,3,4
1056%{
1057  store(stack, st, ~GetVar(arg1));
1058%}
1059
1060OPCODE "call_1n"      1OP:0x0f CANJUMP      VERSION 5,6,7,8
1061%{
1062  ZDWord  new_routine;
1063  ZFrame* newframe;
1064
1065  if (arg1 == 0)
1066    {
1067      store(stack, st, 0);
1068      goto loop;
1069    }
1070
1071  new_routine = UnpackR(arg1);
1072  newframe = call_routine(&pc, stack, new_routine);
1073
1074  newframe->discard = 1;
1075%}
1076
1077####                           ----// 888 \\----                           ####
1078# 0OPs
1079
1080OPCODE "rtrue"       0OP:0x00 CANJUMP VERSION all
1081%{
1082  arg1 = 1; goto op_ret;
1083%}
1084
1085OPCODE "rfalse"      0OP:0x01 CANJUMP VERSION all
1086%{
1087  arg1 = 0; goto op_ret;
1088%}
1089
1090OPCODE "print"       0OP:0x02 STRING  VERSION all
1091%{
1092#ifdef DEBUG
1093  printf_debug(">%s<\n", string);
1094#endif
1095  stream_prints((int*)string);
1096%}
1097
1098OPCODE "print_ret"   0OP:0x03 CANJUMP STRING  VERSION all
1099%{
1100#ifdef DEBUG
1101  printf_debug(">%s<\n", string);
1102#endif
1103  stream_prints((int*)string);
1104  stream_printf("\n");
1105  stream_flush_buffer();
1106  goto op_rtrue;
1107%}
1108
1109OPCODE "nop"         0OP:0x04         VERSION all
1110%{ /* zzz */ %}
1111
1112OPCODE "save"        0OP:0x05 BRANCH  VERSION 1,2,3
1113%{
1114  ZDWord newpc;
1115
1116  if (branch == 0 || branch == 1)
1117    {
1118      zmachine_warning("This interpreter does not support returning from v3 save statements correctly");
1119      newpc = pc;
1120    }
1121  else
1122    {
1123      newpc = pc;
1124      if (negate)
1125        newpc += branch-2;
1126    }
1127
1128  result = save_1234(newpc, stack, -1);
1129  dobranch;
1130%}
1131
1132OPCODE "save"        0OP:0x05 STORE CANJUMP VERSION 4
1133%{
1134  if (save_1234(pc, stack, st))
1135    store(stack, st, 1);
1136  else
1137    store(stack, st, 0);
1138%}
1139
1140OPCODE "restore"     0OP:0x06 BRANCH  VERSION 1,2,3
1141%{
1142  if (!restore_1234(&pc, stack))
1143    {
1144      result = 0;
1145      dobranch;
1146    }
1147%}
1148
1149OPCODE "restore"     0OP:0x06 STORE CANJUMP VERSION 4
1150%{
1151  if (!restore_1234(&pc, stack))
1152    {
1153      store(stack, st, 0);
1154    }
1155%}
1156
1157OPCODE "restart"     0OP:0x07 CANJUMP VERSION 3,4,5,7,8
1158%{
1159  /* Unwind stack */
1160  while (stack->current_frame->last_frame != NULL)
1161    {
1162      ZFrame* oldframe;
1163
1164      oldframe = stack->current_frame;
1165      stack->current_frame = oldframe->last_frame;
1166
1167      stack->stack_size += oldframe->frame_size;
1168      stack->stack_top  -= oldframe->frame_size;
1169
1170      free(oldframe);
1171    }
1172
1173  display_join(0, 2);
1174  display_set_window(0);
1175  display_erase_window();
1176  if (ReadByte(0) > 4)
1177    display_set_cursor(0,0);
1178
1179  read_block2(machine.memory, machine.file,
1180	machine.story_offset, machine.story_offset+machine.dynamic_ceiling);
1181  pc = Word(ZH_initpc);
1182
1183  restart_machine();
1184%}
1185
1186OPCODE "ret_popped"  0OP:0x08 CANJUMP VERSION all
1187%{
1188  arg1 = pop(stack);
1189  goto op_ret;
1190%}
1191
1192OPCODE "pop"         0OP:0x09         VERSION 1,2,3,4
1193%{ pop(stack); %}
1194
1195OPCODE "catch"       0OP:0x09 STORE   VERSION 5,6,7,8
1196%{
1197  if (stack->current_frame == NULL)
1198    zmachine_fatal("Catch attempted while no frame is current");
1199  store(stack, st, stack->current_frame->frame_num);
1200%}
1201
1202OPCODE "quit"        0OP:0x0a CANJUMP VERSION all
1203%{
1204  pc = -1;
1205  return;
1206%}
1207
1208OPCODE "new_line"    0OP:0x0b         VERSION all
1209%{
1210  stream_printf("\n");
1211%}
1212
1213OPCODE "show_status" 0OP:0x0c         VERSION 3
1214%{
1215  draw_statusbar_123(stack);
1216%}
1217
1218OPCODE "status_nop"  0OP:0x0c         VERSION 4,5,6,7,8
1219%{
1220  debug_breakpoint* bp;
1221
1222  /* Zzzz */
1223
1224  if ((bp=debug_get_breakpoint(pc-1)) != NULL)
1225    {
1226      pc--;
1227      instr = bp->original;
1228      debug_run_breakpoint(pc);
1229      goto execute_instr;
1230    }
1231%}
1232
1233OPCODE "verify"      0OP:0x0d BRANCH  VERSION all # 3,4,5,6,7,8
1234%{
1235  result = 1;
1236  dobranch;
1237%}
1238
1239OPCODE "piracy"      0OP:0x0f BRANCH  VERSION 5,6,7,8
1240%{
1241  result = 1;
1242  dobranch;
1243%}
1244
1245####                           ----// 888 \\----                           ####
1246# VAR ops
1247
1248OPCODE "call"           VAR:0x00        STORE CANJUMP VERSION 1,2,3
1249%{
1250  ZDWord  new_routine;
1251  ZFrame* newframe;
1252  int     x;
1253
1254  if (argblock.n_args < 1)
1255    zmachine_fatal("call must have 1 argument");
1256
1257  if (argblock.arg[0] == 0)
1258    {
1259      store(stack, st, 0);
1260      goto loop;
1261    }
1262
1263  new_routine = 2*(ZUWord)argblock.arg[0];
1264
1265#ifdef DEBUG
1266  printf_debug("CALL $%x -> V%03i\n", new_routine, st);
1267#endif
1268
1269  newframe = call_routine(&pc, stack, new_routine);
1270
1271  for (x=1; x<argblock.n_args; x++)
1272    {
1273      newframe->flags |= 1<<(x-1);
1274      newframe->local[x] = argblock.arg[x];
1275    }
1276
1277  newframe->storevar = st;
1278%}
1279
1280OPCODE "call_vs"        VAR:0x00        STORE CANJUMP VERSION 4,5,6,7,8
1281%{
1282  ZDWord  new_routine;
1283  ZFrame* newframe;
1284  int     x;
1285
1286  if (argblock.n_args < 1)
1287    zmachine_fatal("call must have 1 argument");
1288
1289  if (argblock.arg[0] == 0)
1290    {
1291      store(stack, st, 0);
1292      goto loop;
1293    }
1294
1295  new_routine = UnpackR((ZUWord)argblock.arg[0]);
1296
1297#ifdef DEBUG
1298  printf_debug("CALL $%x -> V%03i\n", new_routine, st);
1299#endif
1300
1301  newframe = call_routine(&pc, stack, new_routine);
1302
1303  for (x=1; x<argblock.n_args; x++)
1304    {
1305      newframe->flags |= 1<<(x-1);
1306      newframe->local[x] = argblock.arg[x];
1307    }
1308
1309  newframe->storevar = st;
1310%}
1311
1312OPCODE "storew"         VAR:0x01 ARGS:3               VERSION all
1313%{
1314  ZByte* mem;
1315
1316#ifdef DEBUG
1317  printf_debug("Storing word value %i at #%x\n",
1318	 argblock.arg[2],
1319	 ((ZUWord) argblock.arg[0] + (ZUWord) (argblock.arg[1]*2))&0xffff);
1320#endif
1321#ifdef SAFE
1322  if (((ZUWord) argblock.arg[0] + ((ZWord) argblock.arg[1]*2)+1) >
1323      machine.dynamic_ceiling)
1324    zmachine_fatal("Out of range storew (tried to store at $%x, but ceiling is at $%x)",
1325		   ((ZUWord) argblock.arg[0] + ((ZUWord) argblock.arg[1]*2)),
1326		   machine.dynamic_ceiling);
1327#endif
1328
1329  if ((ZUWord) argblock.arg[0] + ((ZWord) argblock.arg[1]*2) == ZH_flags2)
1330    {
1331      ZArgblock a;
1332
1333      stream_flush_buffer();
1334
1335      a.n_args = 1;
1336      a.arg[0] = (argblock.arg[2]&1)?2:-2;
1337
1338      zcode_op_output_stream(stack, &a);
1339
1340      argblock.arg[2] &= ~1;
1341      argblock.arg[2] |= Word(ZH_flags2)&1;
1342    }
1343
1344  mem = Address(((ZUWord) argblock.arg[0] + ((ZWord) argblock.arg[1]*2))&0xffff);
1345  mem[0] = argblock.arg[2]>>8;
1346  mem[1] = argblock.arg[2];
1347%}
1348
1349OPCODE "storeb"         VAR:0x02 ARGS:3               VERSION all
1350%{
1351  ZByte* mem;
1352
1353#ifdef DEBUG
1354  printf_debug("Storing byte value %i at #%x\n",
1355	 argblock.arg[2],
1356	 ((ZUWord) argblock.arg[0] + (ZWord) argblock.arg[1])&0xffff);
1357#endif
1358#ifdef SAFE
1359  if (((ZUWord) argblock.arg[0] + (ZWord) argblock.arg[1]) >
1360      machine.dynamic_ceiling)
1361    zmachine_fatal("Out of range storeb (store to $%x, ceiling at $%x)", ((ZUWord) argblock.arg[0] + (ZUWord) argblock.arg[1]), machine.dynamic_ceiling);
1362#endif
1363
1364  mem = Address(((ZUWord) argblock.arg[0] + (ZWord) argblock.arg[1])&0xffff);
1365  mem[0] = argblock.arg[2];
1366%}
1367
1368OPCODE "put_prop"       VAR:0x03 ARGS:3               VERSION 1,2,3
1369%{
1370  struct prop* p;
1371
1372  if (argblock.arg[0]>255 || argblock.arg[0] == 0)
1373    zmachine_fatal("Object %i out of range", argblock.arg[0]);
1374  argblock.arg[1] &= 0x1f;
1375  if (argblock.arg[1]>31)
1376    zmachine_fatal("Property %i out of range", argblock.arg[1]);
1377
1378  p = get_object_prop_3(argblock.arg[0], argblock.arg[1]);
1379  if (p->isdefault)
1380    zmachine_fatal("No such property %i for object %i", argblock.arg[1], argblock.arg[0]);
1381
1382  switch (p->size)
1383    {
1384    case 1:
1385      p->prop[0] = argblock.arg[2];
1386      break;
1387
1388    case 2:
1389      p->prop[0] = argblock.arg[2]>>8;
1390      p->prop[1] = argblock.arg[2];
1391      break;
1392
1393    default:
1394      zmachine_fatal("%i is an invalid size for put_prop", p->size);
1395    }
1396%}
1397
1398OPCODE "put_prop"       VAR:0x03 ARGS:3               VERSION 4,5,6,7,8
1399%{
1400  struct prop* p;
1401
1402  if (argblock.arg[0] == 0)
1403    {
1404      zmachine_warning("Object 0 has no properties");
1405      goto loop;
1406    }
1407  if (argblock.arg[1]>63)
1408    zmachine_fatal("Property %i out of range", argblock.arg[1]);
1409
1410  p = get_object_prop_4(argblock.arg[0], argblock.arg[1]);
1411  if (p->isdefault)
1412    {
1413      zmachine_warning("No such property %i for object %i", argblock.arg[1], argblock.arg[0]);
1414      goto loop;
1415    }
1416
1417  switch (p->size)
1418    {
1419    case 1:
1420      p->prop[0] = argblock.arg[2];
1421      break;
1422
1423    case 2:
1424      p->prop[0] = argblock.arg[2]>>8;
1425      p->prop[1] = argblock.arg[2];
1426      break;
1427
1428    default:
1429      zmachine_fatal("%i is an invalid size for put_prop", p->size);
1430    }
1431%}
1432
1433OPCODE "sread"          VAR:0x04 ARGS:2               VERSION 1,2,3
1434%{
1435  ZByte* mem;
1436  int* buf;
1437  int x;
1438
1439  //pc -= (2+padding); /* HACK: allow autosave */
1440  machine.autosave_pc = pc - (2+padding);
1441
1442  stream_flush_buffer();
1443
1444  mem = machine.memory + (ZUWord) argblock.arg[0];
1445  buf = malloc(sizeof(int)*(mem[0]+1));
1446
1447  buf[0] = 0;
1448  draw_statusbar_123(stack);
1449  stream_readline(buf, mem[0], 0);
1450
1451  for (x=0; buf[x] != 0; x++)
1452    {
1453      buf[x] = unicode_to_lower(buf[x]);
1454      mem[x+1] = zscii_get_char(buf[x]);
1455    }
1456  mem[x+1] = 0;
1457
1458  if (argblock.n_args > 1)
1459    {
1460      tokenise_string((int*)buf,
1461		      Word(ZH_dict),
1462		      machine.memory + (ZUWord) argblock.arg[1],
1463		      0,
1464		      1);
1465
1466#ifdef DEBUG
1467      {
1468	ZByte* tokbuf;
1469	tokbuf = machine.memory + (ZUWord) argblock.arg[1];
1470	for (x=0; x<tokbuf[1]; x++)
1471	  {
1472	    printf_debug("Token $%x%x word at %i, length %i\n",
1473			   tokbuf[2+x*4],
1474			   tokbuf[3+x*4],
1475			   tokbuf[5+x*4],
1476			   tokbuf[4+x*4]);
1477	  }
1478      }
1479#endif
1480    }
1481
1482  free(buf);
1483
1484  machine.autosave_pc = 0;
1485  //pc += (2+padding); /* HACK: allow autosave */
1486%}
1487
1488OPCODE "sread"          VAR:0x04 ARGS:4       CANJUMP VERSION 4
1489%{
1490  //pc -= (2+padding); /* HACK: allow autosave */
1491  machine.autosave_pc = pc - (2+padding);
1492  zcode_op_sread_4(&pc, stack, &argblock);
1493  machine.autosave_pc = 0;
1494  //pc += (2+padding); /* HACK: allow autosave */
1495%}
1496
1497OPCODE "aread"          VAR:0x04 ARGS:4 STORE CANJUMP VERSION 5,7,8
1498%{
1499  //pc -= (3+padding); /* HACK: allow autosave */
1500  machine.autosave_pc = pc - (3+padding);
1501  zcode_op_aread_5678(&pc, stack, &argblock, st);
1502  machine.autosave_pc = 0;
1503  //pc += (3+padding); /* HACK: allow autosave */
1504%}
1505
1506OPCODE "print_char"     VAR:0x05 ARGS:1               VERSION all
1507%{
1508  int ch[2];
1509
1510#ifdef DEBUG
1511  printf_debug(">%c<\n", argblock.arg[0]);
1512#endif
1513  if (argblock.arg[0] >= 256)
1514    argblock.arg[0] = '?';
1515  if (argblock.arg[0] == 0)
1516    argblock.arg[0] = 32;
1517  ch[0] = zscii_unicode[argblock.arg[0]];
1518  ch[1] = 0;
1519  stream_prints((int*)ch);
1520%}
1521
1522OPCODE "print_num"      VAR:0x06 ARGS:1               VERSION all
1523%{
1524#ifdef DEBUG
1525  printf_debug(">%i<\n", argblock.arg[0]);
1526#endif
1527  stream_printf("%i", argblock.arg[0]);
1528%}
1529
1530OPCODE "random"         VAR:0x07 ARGS:1 STORE         VERSION all
1531%{
1532  if (argblock.arg[0] > 0)
1533    {
1534      unsigned int random = random_number()>>8;
1535      random = random&0xffff;
1536      random = (random*argblock.arg[0])/0x10000;
1537      store(stack, st, random+1);
1538
1539      #ifdef DEBUG
1540      printf_debug("Random(%i) = %i\n", argblock.arg[0], random+1);
1541      #endif
1542    }
1543  else if (argblock.arg[0] < 0)
1544    {
1545      random_seed(argblock.arg[0]);
1546      store(stack, st, 0);
1547    }
1548  else
1549    {
1550#ifdef HAVE_GETTIMEOFDAY
1551      struct timeval tv;
1552
1553      /* Reseed RNG */
1554      gettimeofday(&tv, NULL);
1555      random_seed(tv.tv_sec^tv.tv_usec);
1556#else
1557      random_seed((unsigned int) time(NULL));
1558#endif
1559
1560      store(stack, st, 0);
1561    }
1562%}
1563
1564OPCODE "push"           VAR:0x08 ARGS:1               VERSION all
1565%{
1566  push(stack, argblock.arg[0]);
1567%}
1568
1569OPCODE "pull"           VAR:0x09 ARGS:1               VERSION 3,4,5,7,8
1570%{
1571  ZUWord val;
1572
1573  val = pop(stack);
1574  store_nopush(stack, argblock.arg[0], val);
1575%}
1576
1577OPCODE "split_window"   VAR:0x0a ARGS:1               VERSION 3,4,5,7,8
1578%{
1579  stream_flush_buffer();
1580
1581  if (argblock.arg[0] != 0)
1582    {
1583      int win;
1584      int ver;
1585
1586      ver = ReadByte(0);
1587
1588#ifdef DEBUG
1589      printf_debug("Top window bottom is now %i\n", argblock.arg[0]);
1590#endif
1591
1592      win = display_get_window();
1593
1594      if (ver == 3)
1595	{
1596	  display_set_window(2);
1597	  display_erase_window();
1598	}
1599
1600      display_set_window(0);
1601      display_join(0, 2);
1602      display_split(argblock.arg[0], 2);
1603      display_set_window(2);
1604      if (ver == 3)
1605	{
1606	  display_set_cursor(0,1);
1607	  display_erase_window();
1608	}
1609      display_force_fixed(2, 1);
1610
1611      display_set_window(win);
1612    }
1613  else
1614    {
1615      if (ReadByte(0) == 3)
1616	{
1617	  display_set_window(2);
1618	  display_erase_window();
1619	}
1620
1621      display_join(0, 2);
1622      display_set_window(0);
1623    }
1624
1625  if (machine.transcript_on == 2 && display_get_window() == 0)
1626    machine.transcript_on = 1;
1627%}
1628
1629OPCODE "set_window"     VAR:0x0b ARGS:1               VERSION 3,4,5,7,8
1630%{
1631  stream_flush_buffer();
1632
1633  switch (argblock.arg[0])
1634    {
1635    case 0:
1636      stream_buffering(machine.buffering);
1637      display_set_window(0);
1638
1639      if (machine.transcript_on == 2)
1640	machine.transcript_on = 1;
1641      break;
1642    case 1:
1643      stream_buffering(0);
1644      display_set_window(2);
1645      if (ReadByte(0) == 3)
1646	display_set_cursor(0,1);
1647
1648      if (machine.transcript_on == 1)
1649	machine.transcript_on = 2;
1650      break;
1651    default:
1652      zmachine_warning("Window %i not available", argblock.arg[0]);
1653    }
1654%}
1655
1656
1657OPCODE "call_vs2"       VAR:0x0c   STORE LONG CANJUMP VERSION 4,5,6,7,8
1658%{
1659  goto op_call_vs_45678;
1660%}
1661
1662OPCODE "erase_window"   VAR:0x0d ARGS:1               VERSION 4,5,7,8
1663%{
1664  int old_win;
1665
1666  stream_flush_buffer();
1667  old_win = display_get_window();
1668
1669  switch (argblock.arg[0])
1670    {
1671    case 0:
1672      display_set_window(0);
1673      display_erase_window();
1674      if (ReadByte(0) != 4)
1675	display_set_cursor(0,0);
1676      display_set_window(old_win);
1677      break;
1678
1679    case 1:
1680      display_set_window(2);
1681      display_erase_window();
1682      display_set_cursor(0,0);
1683      display_set_window(old_win);
1684      break;
1685
1686    case -1:
1687      display_join(0, 2);
1688      display_set_window(0);
1689      display_erase_window();
1690      if (ReadByte(0) != 4)
1691	display_set_cursor(0,0);
1692      break;
1693
1694    case -2:
1695      old_win = display_get_window();
1696      display_set_window(0);
1697      display_erase_window();
1698      display_set_window(2);
1699      display_erase_window();
1700      display_set_window(old_win);
1701      break;
1702    }
1703%}
1704
1705OPCODE "erase_line"     VAR:0x0e ARGS:1               VERSION 4,5,7,8
1706%{
1707  if (arg1 == 1)
1708    {
1709      display_erase_line(1);
1710    }
1711%}
1712
1713OPCODE "set_cursor"     VAR:0x0f ARGS:2               VERSION 4,5,7,8
1714%{
1715  stream_flush_buffer();
1716#ifdef DEBUG
1717  printf_debug("Cursor moved to %i, %i\n", argblock.arg[1]-1, argblock.arg[0]-1);
1718#endif
1719  if (display_get_window() == 2)
1720    {
1721      display_set_cursor(argblock.arg[1]-1, argblock.arg[0]-1);
1722    }
1723%}
1724
1725OPCODE "get_cursor"     VAR:0x10 ARGS:1               VERSION 4,5,7,8
1726%{
1727  ZByte* dest;
1728  int x, y;
1729
1730  dest = Address((ZUWord)argblock.arg[0]);
1731  x = display_get_cur_x()+1;
1732  y = display_get_cur_y()+1;
1733
1734  dest[0] = y>>8;
1735  dest[1] = y;
1736  dest[2] = x>>8;
1737  dest[3] = x;
1738%}
1739
1740OPCODE "set_text_style" VAR:0x11 ARGS:1               VERSION 4,5,7,8
1741%{
1742  stream_flush_buffer();
1743#ifndef SPEC_11
1744  if (argblock.arg[0] != 0 &&
1745      argblock.arg[0] != 1 &&
1746      argblock.arg[0] != 2 &&
1747      argblock.arg[0] != 4 &&
1748      argblock.arg[0] != 8 &&
1749      argblock.arg[0] != 16)
1750    {
1751      zmachine_warning("Multiple styles not (officially) supported in standard 1.0", argblock.arg[0]);
1752    }
1753#endif
1754  display_set_style(argblock.arg[0]);
1755%}
1756
1757OPCODE "buffer_mode"    VAR:0x12 ARGS:1               VERSION 4,5,6,7,8
1758%{
1759  machine.buffering = argblock.arg[0];
1760  stream_buffering(argblock.arg[0]);
1761%}
1762
1763OPCODE "output_stream"  VAR:0x13 ARGS:1               VERSION 3
1764%{
1765  zcode_op_output_stream(stack, &argblock);
1766%}
1767
1768OPCODE "output_stream"  VAR:0x13 ARGS:2               VERSION 4,5,7,8
1769%{
1770  zcode_op_output_stream(stack, &argblock);
1771%}
1772
1773OPCODE "input_stream"   VAR:0x14 ARGS:1               VERSION all # 3,4,5,7,8
1774%{
1775  switch (argblock.arg[0])
1776    {
1777    case 0:
1778      machine.script_on = 0;
1779      break;
1780
1781    case 1:
1782      machine.script_on = 0;
1783      if (!machine.script_file)
1784        {
1785	  int sz;
1786	  machine.script_file = get_file_read(&sz, script_fname, ZFile_transcript);
1787	}
1788      machine.script_on = 1;
1789      if (!machine.script_file)
1790        {
1791	  stream_printf("Couldn't open %s for reading.\n", script_fname);
1792          machine.script_on = 0;
1793	}
1794      if (machine.script_file && end_of_file(machine.script_file))
1795        {
1796	  close_file(machine.script_file);
1797	  stream_printf("Couldn't read any data from %s\n", script_fname);
1798	  machine.script_file = NULL;
1799	  machine.script_on = 0;
1800	}
1801      break;
1802
1803    default:
1804      zmachine_warning("Input stream %i not supported", argblock.arg[0]);
1805    }
1806%}
1807
1808OPCODE "sound_effect"   VAR:0x15                      VERSION all # 3,4,5,6,7,8
1809%{
1810  display_beep();
1811%}
1812
1813OPCODE "read_char"      VAR:0x16 ARGS:3 STORE CANJUMP VERSION 4,5,7,8
1814%{
1815  //pc -= (3+padding); /* HACK: allow autosave */
1816  machine.autosave_pc = pc - (3+padding);
1817
1818  stream_flush_buffer();
1819
1820  if (argblock.n_args < 2)
1821    {
1822      zcode_op_readchar(&pc, stack, &argblock, st);
1823    }
1824  else
1825    {
1826      zcode_op_readchar(&pc, stack, &argblock, st);
1827    }
1828
1829  machine.autosave_pc = 0;
1830  //pc += (3+padding); /* HACK: allow autosave */
1831%}
1832
1833OPCODE "scan_table"     VAR:0x17        BRANCH STORE  VERSION 4,5,6,7,8
1834%{
1835  ZDWord adr;
1836
1837  if (argblock.n_args < 4)
1838    argblock.arg[3] = 0x82;
1839
1840  adr = scan_table(argblock.arg[0],
1841		   argblock.arg[1],
1842		   argblock.arg[2],
1843		   argblock.arg[3]);
1844
1845  if (adr > 0)
1846    {
1847      store(stack, st, adr);
1848      result = 1;
1849    }
1850  else
1851    {
1852      store(stack, st, 0);
1853      result = 0;
1854    }
1855  dobranch;
1856%}
1857
1858OPCODE "not"            VAR:0x18 ARGS:1 STORE         VERSION 5,6,7,8
1859%{
1860  store(stack, st, ~argblock.arg[0]);
1861%}
1862
1863OPCODE "call_vn"        VAR:0x19 CANJUMP              VERSION 5,6,7,8
1864%{
1865  ZDWord  new_routine;
1866  ZFrame* newframe;
1867  int     x;
1868
1869  if (argblock.n_args < 1)
1870    zmachine_fatal("call must have 1 argument");
1871
1872  if (argblock.arg[0] == 0)
1873    {
1874      store(stack, st, 0);
1875      goto loop;
1876    }
1877
1878  new_routine = UnpackR((ZUWord)argblock.arg[0]);
1879
1880#ifdef DEBUG
1881  printf_debug("CALL $%x -> V%03i\n", new_routine, st);
1882#endif
1883
1884  newframe = call_routine(&pc, stack, new_routine);
1885
1886  for (x=1; x<argblock.n_args; x++)
1887    {
1888      newframe->flags |= 1<<(x-1);
1889      newframe->local[x] = argblock.arg[x];
1890    }
1891
1892  newframe->discard = 1;
1893%}
1894
1895OPCODE "call_vn2"       VAR:0x1a CANJUMP LONG         VERSION 5,6,7,8
1896%{
1897  goto op_call_vn_5678;
1898%}
1899
1900OPCODE "tokenise"       VAR:0x1b ARGS:4               VERSION 5,6,7,8
1901%{
1902  ZByte* text;
1903  int*  buf;
1904  int x;
1905
1906  text = Address((ZUWord)argblock.arg[0]);
1907  buf = malloc(sizeof(int)*(text[1]+1));
1908  for (x=0; x<text[1]; x++)
1909    buf[x] = zscii_unicode[text[x+2]];
1910  buf[x] = 0;
1911
1912  tokenise_string(buf,
1913		  argblock.arg[2]==0?Word(ZH_dict):argblock.arg[2],
1914		  Address((ZUWord)argblock.arg[1]),
1915		  argblock.arg[3] != 0,
1916		  2);
1917
1918#ifdef DEBUG
1919      {
1920	ZByte* tokbuf;
1921	int x;
1922
1923	tokbuf = machine.memory + (ZUWord) argblock.arg[1];
1924	for (x=0; x<tokbuf[1]; x++)
1925	  {
1926	    printf_debug("Token $%x%x word at %i, length %i\n",
1927			   tokbuf[2+x*4],
1928			   tokbuf[3+x*4],
1929			   tokbuf[5+x*4],
1930			   tokbuf[4+x*4]);
1931	  }
1932      }
1933#endif
1934
1935  free(buf);
1936%}
1937
1938OPCODE "encode_text"    VAR:0x1c ARGS:4               VERSION 5,6,7,8
1939%{
1940  int* buf;
1941  int x;
1942  char* d;
1943
1944  buf = malloc(sizeof(int)*(argblock.arg[1]+1));
1945  d = Address((ZUWord)argblock.arg[0]) + argblock.arg[2];
1946
1947  for (x=0; x<argblock.arg[1]; x++)
1948    buf[x] = zscii_get_char(d[x]);
1949  buf[x] = 0;
1950
1951  /* Note: I haven't tested this yet */
1952  pack_zscii(buf,
1953	     argblock.arg[1],
1954	     Address((ZUWord)argblock.arg[3]),
1955	     9);
1956
1957  free(buf);
1958%}
1959
1960OPCODE "copy_table"     VAR:0x1d ARGS:3               VERSION 5,6,7,8
1961%{
1962  if (argblock.arg[1] != 0)
1963    {
1964#ifdef DEBUG
1965      printf_debug("Copying #%x bytes from #%x to #%x\n", argblock.arg[2],
1966	     (ZUWord)argblock.arg[0], (ZUWord)argblock.arg[1]);
1967#endif
1968
1969      if (argblock.arg[2] >= 0) /* Move memory */
1970	memmove(Address((ZUWord)argblock.arg[1]),
1971		Address((ZUWord)argblock.arg[0]),
1972		(ZUWord)argblock.arg[2]);
1973      else /* Copy forwards */
1974	{
1975	  ZUWord x;
1976	  ZByte* src;
1977	  ZByte* dest;
1978
1979	  src = Address((ZUWord)argblock.arg[0]);
1980	  dest = Address((ZUWord)argblock.arg[1]);
1981
1982	  for (x = 0; x<-argblock.arg[2]; x++)
1983	    {
1984	      dest[x] = src[x];
1985	    }
1986	}
1987    }
1988  else
1989    {
1990      ZUWord x;
1991      ZByte* mem;
1992
1993#ifdef DEBUG
1994      printf_debug("Blanking %i bytes from #%x\n", argblock.arg[2], (ZUWord)argblock.arg[0]);
1995#endif
1996
1997      mem = Address((ZUWord)argblock.arg[0]);
1998
1999      for (x=0; x<argblock.arg[2]; x++)
2000	{
2001	  mem[x] = 0;
2002	}
2003    }
2004%}
2005
2006OPCODE "print_table"    VAR:0x1e                      VERSION 5,7,8
2007%{
2008  ZByte* table;
2009  int x,y;
2010  int xpos, ypos;
2011
2012  stream_flush_buffer();
2013  xpos = display_get_cur_x();
2014  ypos = display_get_cur_y();
2015
2016  if (argblock.arg[2] == 0)
2017    argblock.arg[2] = 1;
2018
2019#ifdef DEBUG
2020  printf_debug("Printing table #%x (%ix%i), offset %i\n", argblock.arg[0], argblock.arg[1], argblock.arg[2], argblock.arg[3]);
2021#endif
2022
2023  table = Address((ZUWord) argblock.arg[0]);
2024
2025  for (y=0; y<argblock.arg[2]; y++)
2026    {
2027      if (y != 0)
2028	{
2029	  stream_flush_buffer();
2030	  if (machine.screen_on)
2031            display_set_cursor(xpos, ypos+y);
2032	}
2033
2034      for (x=0; x<argblock.arg[1]; x++)
2035	{
2036	  unsigned char c;
2037
2038	  c = (table++)[0];
2039	  if (c == 0)
2040	    c = 32;
2041	  if (c>31)
2042	    stream_printf("%c", c);
2043	}
2044
2045      table += argblock.arg[3];
2046    }
2047  stream_flush_buffer();
2048%}
2049
2050OPCODE "check_argcount" VAR:0x1f ARGS:1 BRANCH        VERSION 5,6,7,8
2051%{
2052  result = (stack->current_frame->flags&(1<<(argblock.arg[0]-1)))!=0;
2053  dobranch;
2054%}
2055
2056####                           ----// 888 \\----                           ####
2057# EXT ops
2058
2059OPCODE "save"          EXT:0x00 ARGS:3 STORE CANJUMP VERSION 5,6,7,8
2060%{
2061  stream_flush_buffer();
2062
2063  if (argblock.n_args == 0)
2064    {
2065      ZWord tmp;
2066      ZFile* f;
2067
2068      stream_printf("\nPlease supply a filename for save\n");
2069      f = get_file_write(NULL, save_fname, ZFile_save);
2070
2071      store(stack, st, 2);
2072      if (state_save(f, stack, pc))
2073	{
2074	  tmp = GetVar(st); /* Pop the variable if it was on the stack */
2075	  store(stack, st, 1);
2076	}
2077      else
2078	{
2079	  tmp = GetVar(st);
2080	  store(stack, st, 0);
2081
2082	  if (state_fail())
2083	    stream_printf("(Save failed, reason: %s)\n", state_fail());
2084	  else
2085	    stream_printf("(Save failed, reason unknown)\n");
2086	}
2087    }
2088  else
2089    {
2090      char fname[256];
2091      ZFile* file;
2092      int    prompt = 1;
2093
2094#ifdef SPEC_11
2095      if (argblock.n_args > 3)
2096        prompt = argblock.arg[3];
2097#endif
2098
2099      if (argblock.arg[2] != 0)
2100        {
2101	  strncpy(fname,
2102	          Address(argblock.arg[2]+1),
2103		  ReadByte(argblock.arg[2]));
2104          fname[ReadByte(argblock.arg[2])] = 0;
2105	}
2106      else
2107	strcpy(fname, "table.dat");
2108
2109      if (prompt)
2110	{
2111      	  stream_printf("\nPlease supply a filename for data save\n");
2112      	  file = get_file_write(NULL, fname, ZFile_data);
2113	}
2114      else
2115        {
2116	  char* newname;
2117
2118	  newname = malloc(strlen(rc_get_savedir()) +
2119	                   strlen(fname) + 3);
2120
2121	  sprintf(newname, "%s/%s", rc_get_savedir(), fname); /* Safe: see above */
2122
2123	  file = open_file_write(newname);
2124
2125	  free(newname);
2126        }
2127
2128      if (!(file))
2129	{
2130	  store(stack, st, 0);
2131	  goto loop;
2132	}
2133
2134      write_block(file, Address(argblock.arg[0]), (ZUWord)argblock.arg[1]);
2135
2136      close_file(file);
2137
2138      store(stack, st, argblock.arg[1]);
2139    }
2140%}
2141
2142OPCODE "restore"       EXT:0x01 ARGS:3 STORE CANJUMP VERSION 5,6,7,8
2143%{
2144  if (argblock.n_args == 0)
2145    {
2146      ZFile* f;
2147      ZDWord sz;
2148
2149      stream_printf("\nPlease supply a filename for restore\n");
2150      f = get_file_read(&sz, save_fname, ZFile_save);
2151
2152      if (state_load(f, sz, stack, &pc))
2153	{
2154	  zmachine_setup_header();
2155	  goto loop;
2156	}
2157
2158      if (state_fail())
2159	stream_printf("(Restore failed, reason: %s)\n", state_fail());
2160      else
2161	stream_printf("(Restore failed, reason unknown)\n");
2162
2163      store(stack, st, 0);
2164    }
2165  else
2166    {
2167      char fname[256];
2168      ZFile* file;
2169      int sz;
2170      int    prompt = 1;
2171
2172#ifdef SPEC_11
2173      if (argblock.n_args > 3)
2174        prompt = argblock.arg[3];
2175#endif
2176
2177      if (argblock.arg[2] != 0)
2178        {
2179	  strncpy(fname,
2180	          Address(argblock.arg[2]+1),
2181		  ReadByte(argblock.arg[2]));
2182	  fname[ReadByte(argblock.arg[2])] = 0;
2183	}
2184      else
2185	strcpy(fname, "table.dat");
2186
2187      if (prompt)
2188        {
2189          stream_printf("\nPlease supply a filename for data restore\n");
2190          file = get_file_read(&sz, fname, ZFile_data);
2191        }
2192      else
2193        {
2194	  char* newname = NULL;
2195
2196	  newname = malloc(strlen(rc_get_savedir()) +
2197	                   strlen(fname) + 3);
2198
2199	  sprintf(newname, "%s/%s", rc_get_savedir(), fname); /* Safe: see above */
2200
2201	  sz = get_file_size(newname);
2202	  file = open_file(newname);
2203
2204	  free(newname);
2205        }
2206
2207      if (!(file))
2208	{
2209	  store(stack, st, 0);
2210	  goto loop;
2211	}
2212
2213      if (sz < (ZUWord)argblock.arg[1])
2214	argblock.arg[1] = (ZUWord) sz;
2215      read_block2(Address(argblock.arg[0]),
2216		  file, 0, (ZUWord)argblock.arg[1]);
2217
2218      close_file(file);
2219
2220      store(stack, st, argblock.arg[1]);
2221    }
2222%}
2223
2224OPCODE "log_shift"     EXT:0x02 ARGS:2 STORE  VERSION 5,6,7,8
2225%{
2226  if (argblock.arg[1] >= 0)
2227    {
2228      store(stack, st, argblock.arg[0]<<argblock.arg[1]);
2229    }
2230  else
2231    {
2232      ZUWord result;
2233
2234      result = argblock.arg[0];
2235      result >>= -argblock.arg[1];
2236      store(stack, st, result);
2237    }
2238%}
2239
2240OPCODE "art_shift"     EXT:0x03 ARGS:2 STORE  VERSION 5,6,7,8
2241%{
2242  if (argblock.arg[1] >= 0)
2243    {
2244      store(stack, st, argblock.arg[0]<<argblock.arg[1]);
2245    }
2246  else
2247    {
2248      store(stack, st, argblock.arg[0]>>-argblock.arg[1]);
2249    }
2250%}
2251
2252OPCODE "set_font"      EXT:0x04 ARGS:1 STORE  VERSION 5,7,8
2253%{
2254  stream_flush_buffer();
2255
2256  store(stack, st, 1);
2257
2258  switch (argblock.arg[0])
2259    {
2260    case 1:
2261      display_set_style(-16);
2262      display_set_style(-8);
2263      break;
2264
2265    case 3:
2266      display_set_style(16);
2267      break;
2268
2269    case 4:
2270      display_set_style(-16);
2271      display_set_style(8);
2272      break;
2273
2274    default:
2275      zmachine_warning("Font %i not supported", argblock.arg[0]);
2276      store(stack, st, 0);
2277   }
2278%}
2279
2280OPCODE "save_undo"     EXT:0x09 ARGS:0 STORE CANJUMP VERSION 5,6,7,8
2281%{
2282#ifdef CAN_UNDO
2283  ZWord tmp;
2284  int x;
2285
2286  if (machine.undo[UNDO_LEVEL-1])
2287    free(machine.undo[UNDO_LEVEL-1]);
2288
2289  for (x=UNDO_LEVEL-2; x>=0; x--)
2290    {
2291      machine.undo[x+1] = machine.undo[x];
2292      machine.undo_len[x+1] = machine.undo_len[x];
2293    }
2294
2295  store(stack, st, 2);
2296  machine.undo[0] = state_compile(stack, pc, &machine.undo_len[0],
2297#ifdef SQUEEZE_UNDO
2298			       1
2299#else
2300			       0
2301#endif
2302			       );
2303  tmp = GetVar(st); /* (Pop the value again if it's on the stack) */
2304
2305  if (machine.undo)
2306    store(stack, st, 1);
2307  else
2308    store(stack, st, 0);
2309#else
2310  store(stack, st, -1);
2311#endif
2312%}
2313
2314OPCODE "restore_undo"  EXT:0x0a ARGS:0 STORE CANJUMP VERSION 5,6,7,8
2315%{
2316#ifdef CAN_UNDO
2317  if (machine.undo[0])
2318    {
2319      if (state_decompile(machine.undo[0], stack, &pc, machine.undo_len[0]))
2320	{
2321	  int x;
2322
2323	  free(machine.undo[0]);
2324	  for (x=1; x<UNDO_LEVEL; x++)
2325	    {
2326	      machine.undo[x-1] = machine.undo[x];
2327	      machine.undo_len[x-1] = machine.undo_len[x];
2328	    }
2329	  machine.undo[UNDO_LEVEL-1] = NULL;
2330
2331	  goto loop;
2332	}
2333      if (state_fail())
2334        {
2335	  stream_printf("[ Undo failed, %s ]\n", state_fail());
2336	}
2337      store(stack, st, 0);
2338    }
2339#endif
2340  store(stack, st, 0);
2341%}
2342
2343OPCODE "print_unicode" EXT:0x0b ARGS:1        VERSION 5,7,8
2344%{
2345  stream_printc(argblock.arg[0]);
2346%}
2347
2348OPCODE "check_unicode" EXT:0x0c ARGS:1 STORE  VERSION 5,7,8
2349%{
2350  store(stack, st, 1);
2351%}
2352
2353####                           ----// 888 \\----                           ####
2354# Version 6 opcodes
2355
2356OPCODE "aread"          VAR:0x04 ARGS:4 STORE CANJUMP VERSION 6
2357%{
2358  //pc -= (3+padding); /* HACK: allow autosave */
2359  machine.autosave_pc = pc - (3+padding);
2360
2361  stream_flush_buffer();
2362  v6_set_caret();
2363  zcode_op_aread_5678(&pc, stack, &argblock, st);
2364
2365  machine.autosave_pc = 0;
2366  //pc += (3+padding);
2367%}
2368
2369OPCODE "split_window"   VAR:0x0a ARGS:1               VERSION 6
2370%{
2371  int owin;
2372  int oldx0, oldy0;
2373  int oldx1, oldy1;
2374
2375  stream_flush_buffer();
2376
2377  owin = v6_get_window();
2378
2379  v6_set_window(0);
2380  oldx0 = v6_get_cursor_x();
2381  oldy0 = v6_get_cursor_y();
2382
2383  v6_set_window(1);
2384  oldx1 = v6_get_cursor_x();
2385  oldy1 = v6_get_cursor_y();
2386
2387  windows[0].x      = 0;
2388  windows[0].y      = argblock.arg[0]+1;
2389  windows[0].xsize  = machine.dinfo->width;
2390  windows[0].ysize  = machine.dinfo->height-argblock.arg[0]-1;
2391  windows[1].scrolling = 0;
2392  windows[1].x      = 0;
2393  windows[1].y      = 0;
2394  windows[1].xsize  = machine.dinfo->width;
2395  windows[1].ysize  = argblock.arg[0];
2396  zcode_setup_window(0);
2397  zcode_setup_window(1);
2398
2399  v6_set_window(0);
2400  v6_set_cursor(oldx0, oldy0);
2401
2402  v6_set_window(1);
2403  v6_set_cursor(oldx1, oldy1);
2404
2405#ifdef DEBUG
2406  printf_debug("Defining windows as a result of split_window\n");
2407#endif
2408
2409  v6_set_window(owin);
2410%}
2411
2412OPCODE "set_text_style" VAR:0x11 ARGS:1               VERSION 6
2413%{
2414  int cwin;
2415
2416  stream_flush_buffer();
2417
2418  cwin = v6_get_window();
2419
2420  if (argblock.arg[0] > 0)
2421    windows[cwin].style |= argblock.arg[0];
2422  else
2423    windows[cwin].style |= ~(-argblock.arg[0]);
2424
2425  if (argblock.arg[0] == 0)
2426    windows[cwin].style = 0;
2427
2428  v6_set_style(argblock.arg[0]);
2429  if (windows[cwin].font_num == 4 && argblock.arg[0] == 0)
2430    v6_set_style(8);
2431  if (windows[cwin].font_num == 3 && argblock.arg[0] == 0)
2432    v6_set_style(16);
2433%}
2434
2435OPCODE "read_char"      VAR:0x16 ARGS:3 STORE CANJUMP VERSION 6
2436%{
2437  //pc -= (3+padding); /* HACK: allow autosave */
2438  machine.autosave_pc = pc - (3+padding);
2439
2440  stream_flush_buffer();
2441  v6_set_caret();
2442
2443  if (argblock.n_args < 2)
2444    {
2445      zcode_op_readchar(&pc, stack, &argblock, st);
2446    }
2447  else
2448    {
2449      zcode_op_readchar(&pc, stack, &argblock, st);
2450    }
2451
2452  machine.autosave_pc = 0;
2453  //pc += (3+padding); /* HACK: allow autosave */
2454%}
2455
2456OPCODE "print_table"    VAR:0x1e                      VERSION 6
2457%{
2458  ZByte* table;
2459  int x,y;
2460  int xpos;
2461
2462  stream_flush_buffer();
2463  xpos = v6_get_cursor_x();
2464
2465  if (argblock.arg[2] == 0)
2466    argblock.arg[2] = 1;
2467
2468#ifdef DEBUG
2469  printf_debug("Printing table #%x (%ix%i), offset %i\n", argblock.arg[0], argblock.arg[1], argblock.arg[2], argblock.arg[3]);
2470#endif
2471
2472  table = Address((ZUWord) argblock.arg[0]);
2473
2474  for (y=0; y<argblock.arg[2]; y++)
2475    {
2476      if (y != 0)
2477	{
2478	  stream_printf("\n");
2479	  stream_flush_buffer();
2480	  if (machine.screen_on)
2481	    v6_set_cursor(xpos, v6_get_cursor_y());
2482	}
2483
2484      for (x=0; x<argblock.arg[1]; x++)
2485	{
2486	  unsigned char c;
2487
2488	  c = (table++)[0];
2489	  if (c == 0)
2490	    c = 32;
2491	  if (c>31)
2492	    stream_printf("%c", c);
2493	}
2494
2495      table += argblock.arg[3];
2496    }
2497  stream_flush_buffer();
2498%}
2499
2500OPCODE "restart"     0OP:0x07 CANJUMP VERSION 6
2501%{
2502  int x;
2503
2504  stream_flush_buffer();
2505
2506  /* Unwind stack */
2507  while (stack->current_frame->last_frame != NULL)
2508    {
2509      ZFrame* oldframe;
2510
2511      oldframe = stack->current_frame;
2512      stack->current_frame = oldframe->last_frame;
2513
2514      stack->stack_size += oldframe->frame_size;
2515      stack->stack_top  -= oldframe->frame_size;
2516
2517      free(oldframe);
2518    }
2519
2520  read_block2(machine.memory, machine.file,
2521	machine.story_offset, machine.story_offset + machine.dynamic_ceiling);
2522
2523  v6_reset();
2524
2525  for (x=0; x<8; x++)
2526    {
2527      windows[x].wrapping   = 0;
2528      windows[x].scrolling  = 1;
2529      windows[x].buffering  = 1;
2530      windows[x].transcript = 0;
2531      windows[x].x = windows[x].y = 0;
2532      windows[x].xsize = machine.dinfo->width;
2533      windows[x].ysize = machine.dinfo->height;
2534    }
2535
2536  call_routine(&pc, stack,
2537	       (4*GetWord(machine.header, ZH_initpc)) +
2538	       machine.routine_offset);
2539  zcode_v6_initialise();
2540
2541  restart_machine();
2542%}
2543
2544OPCODE "set_colour"    2OP:0x1b REALLYVAR     VERSION 6
2545%{
2546  int win = 0;
2547  int working;
2548
2549  int fg, bg;
2550
2551  stream_flush_buffer();
2552
2553  win = working = v6_get_window();
2554  if (argblock.n_args > 2)
2555    {
2556      win = v6_get_window();
2557      v6_set_window(WinNum(argblock.arg[2]));
2558      working = WinNum(argblock.arg[2]);
2559    }
2560
2561  fg = convert_colour(arg1);
2562  bg = convert_colour(arg2);
2563
2564  v6_set_colours(fg, bg);
2565
2566  fg = v6_get_fg_colour();
2567  bg = v6_get_bg_colour();
2568
2569  if (fg >= 0)
2570    {
2571      windows[working].colour = (windows[working].colour&~0xff)|((fg+2)&0xff);
2572      if (fg <= 16)
2573	    windows[working].fg_true = true_colour(fg);
2574	  else
2575	    windows[working].fg_true = fg - 16;
2576    }
2577  if (bg >= 0)
2578    {
2579      windows[working].colour = (windows[working].colour&~0xff00)|(((bg+2)&0xff)<<8);
2580      if (bg <= 16)
2581	    windows[working].bg_true = true_colour(bg);
2582	  else
2583	    windows[working].bg_true = bg - 16;
2584    }
2585
2586  v6_set_window(win);
2587%}
2588
2589OPCODE "set_true_colour" EXT:0x0d VERSION 6
2590%{
2591#ifndef SPEC_11
2592  zmachine_warning("set_true_colour has no effect before standard 1.1");
2593#else
2594  int fore, back;
2595  int win, working;
2596
2597  stream_flush_buffer();
2598
2599  win = working = v6_get_window();
2600  if (argblock.n_args > 2)
2601    {
2602      win = v6_get_window();
2603      v6_set_window(WinNum(argblock.arg[2]));
2604      working = WinNum(argblock.arg[2]);
2605    }
2606
2607  fore = argblock.arg[0];
2608  back = argblock.arg[1];
2609
2610  if (fore >= 0)
2611    fore += 16;
2612  if (back >= 0)
2613    back += 16;
2614
2615  v6_set_colours(fore, back);
2616
2617  windows[working].colour = 0x4040;
2618
2619  fore = v6_get_fg_colour();
2620  back = v6_get_bg_colour();
2621
2622  if (fore >= 16)
2623    fore -= 16;
2624  if (back >= 16)
2625    back -= 16;
2626
2627  windows[working].fg_true = fore;
2628  windows[working].bg_true = back;
2629
2630  v6_set_window(win);
2631#endif
2632%}
2633
2634OPCODE "pull"          VAR:0x09 ARGS:1 STORE  VERSION 6
2635%{
2636  if (argblock.arg[0] != 0)
2637    {
2638      ZByte* us;
2639      ZByte* val;
2640      ZUWord len;
2641
2642      /* User stack */
2643      us = Address(argblock.arg[0]);
2644      len = (us[0]<<8)|us[1];
2645      len++;
2646      us[0] = len>>8; us[1] = len;
2647      val = us + len*2;
2648
2649      store(stack, st, (val[0]<<8)|val[1]);
2650    }
2651  else
2652    {
2653      /* Game stack */
2654      store(stack, st, pop(stack));
2655    }
2656%}
2657
2658OPCODE "erase_window"   VAR:0x0d ARGS:1               VERSION 6
2659%{
2660  int old_win, ewin;
2661
2662  stream_flush_buffer();
2663  old_win = v6_get_window();
2664
2665  if (argblock.n_args == 0)
2666    argblock.arg[0] = -3;
2667
2668  ewin = WinNum(argblock.arg[0]);
2669
2670  if (argblock.arg[0] == -1)
2671    {
2672      old_win = 0;
2673      ewin = 0;
2674      v6_reset_windows();
2675
2676#ifdef DEBUG
2677      printf_debug("Defining windows as a result of erase_window\n");
2678#endif
2679
2680      for (x=0; x<8; x++)
2681        {
2682	  v6_set_window(x);
2683
2684          windows[x].wrapping   = 1;
2685          windows[x].scrolling  = 1;
2686          windows[x].buffering  = 1;
2687          windows[x].transcript = 0;
2688          windows[x].x = windows[x].y = 1;
2689          windows[x].xsize = machine.dinfo->width;
2690	  windows[x].ysize = machine.dinfo->height;
2691	  windows[x].font_num = 0;
2692
2693	  /* windows[x].colour = ((machine.dinfo->back+2)<<8)|(machine.dinfo->fore+2);
2694	  v6_set_colours(machine.dinfo->fore, machine.dinfo->back); */
2695	  zcode_setup_window(x);
2696       }
2697    }
2698
2699  if (ewin < 0)
2700    zmachine_fatal("Attempt to erase invalid window %i", ewin);
2701
2702  v6_set_window(ewin);
2703  v6_erase_window();
2704  v6_set_cursor(1,1);
2705  v6_set_window(old_win);
2706%}
2707
2708OPCODE "erase_line"    VAR:0x0e ARGS:1        VERSION 6
2709%{
2710  stream_flush_buffer();
2711  v6_erase_line(argblock.arg[0]);
2712%}
2713
2714OPCODE "set_window"    VAR:0x0b ARGS:1        VERSION 6
2715%{
2716  stream_flush_buffer();
2717
2718  v6_set_window(WinNum(argblock.arg[0]));
2719  machine.transcript_on = (machine.transcript_file!=NULL&&
2720    (windows[WinNum(argblock.arg[0])].transcript != 0));
2721  stream_buffering(windows[WinNum(argblock.arg[0])].buffering);
2722%}
2723
2724OPCODE "set_cursor"    VAR:0x0f ARGS:3        VERSION 6
2725%{
2726  int owin;
2727
2728  stream_flush_buffer();
2729
2730  if (argblock.n_args < 3)
2731    argblock.arg[2] = v6_get_window();
2732
2733  if (argblock.arg[0] > 0)
2734    {
2735      owin = v6_get_window();
2736      v6_set_window(WinNum(argblock.arg[2]));
2737      v6_set_cursor(argblock.arg[1],
2738	  	    argblock.arg[0]);
2739      v6_set_window(owin);
2740    }
2741  else
2742    {
2743      /* FIXME: implement cursor hiding */
2744    }
2745%}
2746
2747OPCODE "get_cursor"    VAR:0x10 ARGS:1        VERSION 6
2748%{
2749  ZByte* dest;
2750  int x, y;
2751  int win;
2752
2753  win = v6_get_window();
2754
2755  dest = Address((ZUWord)argblock.arg[0]);
2756  x = v6_get_cursor_x();
2757  y = v6_get_cursor_y();
2758
2759  dest[0] = y>>8;
2760  dest[1] = y;
2761  dest[2] = x>>8;
2762  dest[3] = x;
2763%}
2764
2765OPCODE "output_stream" VAR:0x13 ARGS:3        VERSION 6
2766%{
2767  stream_flush_buffer();
2768  zcode_op_output_stream(stack, &argblock);
2769%}
2770
2771OPCODE "set_font"      EXT:0x04 ARGS:1 STORE  VERSION 6
2772%{
2773  int win = 0;
2774  int working;
2775
2776  stream_flush_buffer();
2777
2778  win = working = v6_get_window();
2779  if (argblock.n_args > 2)
2780    {
2781      win = v6_get_window();
2782      v6_set_window(WinNum(argblock.arg[2]));
2783      working = WinNum(argblock.arg[2]);
2784    }
2785
2786  switch (argblock.arg[0])
2787    {
2788    case 1:
2789      v6_set_style(-16);
2790      v6_set_style(-8);
2791
2792      windows[working].font_num = argblock.arg[0];
2793
2794      windows[working].style &= ~(16|8);
2795      store(stack, st, 1);
2796      break;
2797
2798    case 3:
2799      windows[working].font_num = argblock.arg[0];
2800      v6_set_style(16);
2801      windows[working].style |= 16;
2802      store(stack, st, 1);
2803      break;
2804
2805    case 4:
2806      windows[working].font_num = argblock.arg[0];
2807      v6_set_style(-16);
2808      v6_set_style(8);
2809      windows[working].style &= ~16;
2810      windows[working].style |= 8;
2811      store(stack, st, 1);
2812      break;
2813
2814    default:
2815      zmachine_warning("Font %i not supported", argblock.arg[0]);
2816      store(stack, st, 0);
2817   }
2818
2819  v6_set_window(win);
2820%}
2821
2822OPCODE "draw_picture"  EXT:0x05 ARGS:3        VERSION 6
2823%{
2824  BlorbImage* img;
2825  int win;
2826
2827  stream_flush_buffer();
2828
2829  win = v6_get_window();
2830
2831  if (machine.blorb != NULL)
2832    {
2833      img = blorb_findimage(machine.blorb, argblock.arg[0]);
2834      // img = blorb_findimage(machine.blorb, 1);
2835    }
2836  else
2837    img = NULL;
2838
2839  if (img != NULL && img->loaded != NULL)
2840    {
2841      int x,y;
2842
2843      x = argblock.arg[2];
2844      y = argblock.arg[1];
2845      if (argblock.n_args < 3)
2846	x = v6_get_cursor_x();
2847      if (argblock.n_args < 2)
2848        y = v6_get_cursor_y();
2849
2850#ifdef DEBUG
2851      printf_debug("Drawing image %i @ (%i, %i)\n", argblock.arg[0],
2852	x, y);
2853#endif
2854
2855      display_plot_image(img, x+windows[win].x-1, y+windows[win].y-1);
2856    }
2857%}
2858
2859OPCODE "picture_data"  EXT:0x06 BRANCH        VERSION 6
2860%{
2861  unsigned char* d;
2862  int width, height;
2863  BlorbImage* img;
2864
2865  if (argblock.arg[0] == 0)
2866    {
2867      if (machine.blorb != NULL)
2868	{
2869          result = 1;
2870	  if (argblock.n_args > 1)
2871	    {
2872	      unsigned char* mem;
2873
2874              mem = Address(argblock.arg[1]);
2875	      mem[0] = machine.blorb->index.npictures>>8;
2876	      mem[1] = machine.blorb->index.npictures;
2877
2878	      mem[2] = machine.blorb->release>>8;
2879              mem[3] = machine.blorb->release;
2880	    }
2881 	}
2882      else
2883        result = 0;
2884
2885      goto draw_pict_branch;
2886    }
2887
2888  d = Address((ZUWord)argblock.arg[1]);
2889
2890  if (machine.blorb != NULL)
2891    {
2892      img = blorb_findimage(machine.blorb, argblock.arg[0]);
2893    }
2894  else
2895    img = NULL;
2896
2897  if (img != NULL)
2898    {
2899      int sc_n, sc_d;
2900
2901      width = img->width;
2902      height = img->height;
2903
2904      v6_scale_image(img, &sc_n, &sc_d);
2905
2906#ifdef DEBUG
2907      printf_debug("Image data for %i: %ix%i (scaled %i, %i)\n",
2908	argblock.arg[0], width, height, sc_n, sc_d);
2909#endif
2910
2911      width = (width*sc_n)/sc_d;
2912      height = (height*sc_n)/sc_d;
2913
2914      d[0] = height>>8;
2915      d[1] = height;
2916      d[2] = width>>8;
2917      d[3] = width;
2918
2919      result = 1;
2920    }
2921  else
2922    {
2923      result = 0;
2924    }
2925
2926 draw_pict_branch:
2927  dobranch;
2928%}
2929
2930OPCODE "erase_picture" EXT:0x07               VERSION 6
2931%{
2932  int width, height;
2933  BlorbImage* img;
2934
2935  stream_flush_buffer();
2936
2937  if (machine.blorb != NULL)
2938    {
2939      img = blorb_findimage(machine.blorb, argblock.arg[0]);
2940    }
2941  else
2942    img = NULL;
2943
2944  if (img != NULL)
2945    {
2946      int sc_n, sc_d;
2947
2948      width = img->width;
2949      height = img->height;
2950
2951      v6_scale_image(img, &sc_n, &sc_d);
2952
2953      width = (width*sc_n)/sc_d;
2954      height = (height*sc_n)/sc_d;
2955
2956      display_pixmap_cols(v6_get_bg_colour(), 0);
2957      display_plot_rect(windows[v6_get_window()].x + argblock.arg[2],
2958	                windows[v6_get_window()].y + argblock.arg[1],
2959	                width, height);
2960    }
2961%}
2962
2963OPCODE "set_margins"   EXT:0x08 ARGS:3        VERSION 6
2964%{
2965  int win, yp;
2966
2967  stream_flush_buffer();
2968
2969  if (argblock.n_args < 3)
2970    argblock.arg[2] = v6_get_window();
2971  win = v6_get_window();
2972  v6_set_window(WinNum(argblock.arg[2]));
2973
2974#ifdef DEBUG
2975  printf_debug("Margins set to %i, %i\n", argblock.arg[0], argblock.arg[1]);
2976#endif
2977
2978  windows[argblock.arg[2]].leftmar  = argblock.arg[0];
2979  windows[argblock.arg[2]].rightmar = argblock.arg[1];
2980
2981  zcode_setup_window(argblock.arg[2]);
2982  if (v6_get_cursor_x() < windows[argblock.arg[2]].leftmar)
2983    {
2984      yp = v6_get_cursor_y();
2985
2986      v6_set_cursor(windows[argblock.arg[2]].leftmar,
2987                    yp);
2988    }
2989  v6_set_window(win);
2990%}
2991
2992OPCODE "move_window"   EXT:0x10               VERSION 6
2993%{
2994  int xmove, ymove;
2995  int win;
2996
2997  stream_flush_buffer();
2998
2999  win = v6_get_window();
3000  v6_set_window(WinNum(argblock.arg[0]));
3001
3002  xmove = argblock.arg[2]-windows[argblock.arg[0]].x;
3003  ymove = argblock.arg[1]-windows[argblock.arg[0]].y;
3004
3005  v6_set_cursor(v6_get_cursor_x()+xmove,
3006                v6_get_cursor_y()+ymove);
3007
3008  windows[argblock.arg[0]].x = argblock.arg[2];
3009  windows[argblock.arg[0]].y = argblock.arg[1];
3010  windows[argblock.arg[0]].leftmar = 0;
3011  windows[argblock.arg[0]].rightmar = 0;
3012
3013#ifdef DEBUG
3014  printf_debug("Window %i position is now %i, %i\n",
3015	 argblock.arg[0],
3016	 windows[argblock.arg[0]].x,
3017	 windows[argblock.arg[0]].y);
3018#endif
3019
3020  zcode_setup_window(argblock.arg[0]);
3021  v6_set_window(win);
3022%}
3023
3024OPCODE "window_size"   EXT:0x11               VERSION 6
3025%{
3026  stream_flush_buffer();
3027
3028  windows[WinNum(argblock.arg[0])].xsize = argblock.arg[2];
3029  windows[WinNum(argblock.arg[0])].ysize = argblock.arg[1];
3030  windows[WinNum(argblock.arg[0])].leftmar = 0;
3031  windows[WinNum(argblock.arg[0])].rightmar = 0;
3032
3033#ifdef DEBUG
3034  printf_debug("Window %i size is now %i, %i\n",
3035	 argblock.arg[0],
3036	 windows[argblock.arg[0]].xsize,
3037	 windows[argblock.arg[0]].ysize);
3038#endif
3039
3040  {
3041    int win;
3042    win = v6_get_window();
3043    zcode_setup_window(argblock.arg[0]);
3044    v6_set_window(win);
3045  }
3046%}
3047
3048OPCODE "window_style"  EXT:0x12               VERSION 6
3049%{
3050  int win, owin;
3051
3052  win = WinNum(argblock.arg[0]);
3053
3054  stream_flush_buffer();
3055
3056#ifdef DEBUG
3057  printf_debug("Window_style: %i %i %i\n", argblock.arg[0], argblock.arg[1], argblock.arg[2]);
3058#endif
3059
3060  StyleSet(windows[win].wrapping,   argblock.arg[1]&1);
3061  StyleSet(windows[win].scrolling,  argblock.arg[1]&2);
3062  StyleSet(windows[win].transcript, argblock.arg[1]&4);
3063  StyleSet(windows[win].buffering,  argblock.arg[1]&8);
3064
3065  owin = v6_get_window();
3066  zcode_setup_window(win);
3067  v6_set_window(owin);
3068%}
3069
3070OPCODE "get_wind_prop" EXT:0x13        STORE  VERSION 6
3071%{
3072  int win;
3073
3074  stream_flush_buffer();
3075  win = v6_get_window();
3076
3077  argblock.arg[0] = v6_window(argblock.arg[0]);
3078  if (argblock.arg[0] < 0)
3079    zmachine_fatal("Bad window for get_wind_prop: %i", argblock.arg[0]);
3080  v6_set_window(WinNum(argblock.arg[0]));
3081
3082  switch(argblock.arg[1])
3083    {
3084    case 0:
3085      store(stack, st, windows[argblock.arg[0]].y);
3086      break;
3087    case 1:
3088      store(stack, st, windows[argblock.arg[0]].x);
3089      break;
3090    case 2:
3091      store(stack, st, windows[argblock.arg[0]].ysize);
3092      break;
3093    case 3:
3094      store(stack, st, windows[argblock.arg[0]].xsize);
3095      break;
3096    case 4:
3097      store(stack, st, v6_get_cursor_y());
3098      break;
3099    case 5:
3100      store(stack, st, v6_get_cursor_x());
3101      break;
3102    case 6:
3103      store(stack, st, windows[argblock.arg[0]].leftmar);
3104      break;
3105    case 7:
3106      store(stack, st, windows[argblock.arg[0]].rightmar);
3107      break;
3108    case 8:
3109      store(stack, st, windows[argblock.arg[0]].newline_routine);
3110      break;
3111    case 9:
3112      store(stack, st, windows[argblock.arg[0]].countdown);
3113      break;
3114    case 10:
3115      store(stack, st, windows[argblock.arg[0]].style);
3116      break;
3117    case 11:
3118      store(stack, st, windows[argblock.arg[0]].colour);
3119      break;
3120    case 12:
3121      store(stack, st, windows[argblock.arg[0]].font_num);
3122      break;
3123    case 13:
3124      store(stack, st, ((int)(display_get_font_height(windows[argblock.arg[0]].style)+0.5)<<8)|
3125                        (int)(display_get_font_width(windows[argblock.arg[0]].style)+0.5));
3126      break;
3127    case 14:
3128      store(stack, st,
3129	    windows[argblock.arg[0]].wrapping|
3130	    (windows[argblock.arg[0]].scrolling<<1)|
3131	    (windows[argblock.arg[0]].transcript<<2)|
3132	    (windows[argblock.arg[0]].buffering<<3));
3133      break;
3134    case 15:
3135      store(stack, st,
3136	    windows[argblock.arg[0]].line_count);
3137      break;
3138    case 16:
3139      store(stack, st,
3140            windows[argblock.arg[0]].fg_true);
3141      break;
3142    case 17:
3143      store(stack, st,
3144            windows[argblock.arg[0]].bg_true);
3145      break;
3146
3147    default:
3148      zmachine_fatal("Attempt to access out of range window property %i", argblock.arg[1]);
3149   }
3150  v6_set_window(win);
3151%}
3152
3153OPCODE "scroll_window" EXT:0x14               VERSION 6
3154%{
3155  int win;
3156
3157  win = WinNum(argblock.arg[0]);
3158
3159  if (win == v6_get_window())
3160    stream_flush_buffer();
3161
3162  v6_scroll_window(win, argblock.arg[1]);
3163%}
3164
3165OPCODE "pop_stack"     EXT:0x15               VERSION 6
3166%{
3167  if (argblock.arg[1] == 0)
3168    {
3169      int x;
3170
3171      for (x=0; x<(ZUWord) argblock.arg[0]; x++)
3172	pop(stack);
3173    }
3174  else
3175    {
3176      ZByte* s;
3177      ZUWord len;
3178
3179      s = Address(argblock.arg[1]);
3180      len = (s[0]<<8)|s[1];
3181      len += argblock.arg[0];
3182      s[0] = len>>8;
3183      s[1] = len;
3184    }
3185%}
3186
3187OPCODE "read_mouse"    EXT:0x16               VERSION 6
3188%{
3189  unsigned char* d;
3190
3191  d = Address(argblock.arg[0]);
3192  display_read_mouse();
3193
3194  d[0] = (unsigned)display_get_pix_mouse_y()>>8;
3195  d[1] = (unsigned)display_get_pix_mouse_y();
3196  d[2] = (unsigned)display_get_pix_mouse_x()>>8;
3197  d[3] = (unsigned)display_get_pix_mouse_x();
3198  d[4] = (unsigned)display_get_pix_mouse_b()>>8;
3199  d[5] = (unsigned)display_get_pix_mouse_b();
3200  d[6] = 0;
3201  d[7] = 0;
3202%}
3203
3204OPCODE "mouse_window"  EXT:0x17               VERSION 6
3205%{
3206  v6_set_mouse_win(WinNum(argblock.arg[0]));
3207%}
3208
3209OPCODE "push_stack"    EXT:0x18        BRANCH VERSION 6
3210%{
3211  result = zcode_v6_push_stack(stack, argblock.arg[1], argblock.arg[0]);
3212  dobranch;
3213%}
3214
3215OPCODE "put_wind_prop" EXT:0x19               VERSION 6
3216%{
3217  int win;
3218
3219  stream_flush_buffer();
3220
3221  win = v6_get_window();
3222  v6_set_window(WinNum(argblock.arg[0]));
3223  switch(argblock.arg[1])
3224    {
3225    case 0:
3226      zmachine_warning("Bad put_wind_prop: should use move_window instead");
3227      windows[argblock.arg[0]].y = argblock.arg[2];
3228      break;
3229    case 1:
3230      zmachine_warning("Bad put_wind_prop: should use move_window instead");
3231      windows[argblock.arg[0]].x = argblock.arg[2];
3232      break;
3233    case 2:
3234      zmachine_warning("Bad put_wind_prop: should use window_size instead");
3235      windows[argblock.arg[0]].ysize = argblock.arg[2];
3236      break;
3237    case 3:
3238      zmachine_warning("Bad put_wind_prop: should use window_size instead");
3239      windows[argblock.arg[0]].xsize = argblock.arg[2];
3240      break;
3241    case 4:
3242      zmachine_warning("Bad put_wind_prop: should use set_cursor instead");
3243      v6_set_cursor(v6_get_cursor_x(), argblock.arg[2]);
3244      break;
3245    case 5:
3246      zmachine_warning("Bad put_wind_prop: should use set_cursor instead");
3247      v6_set_cursor(argblock.arg[2], v6_get_cursor_y());
3248      break;
3249    case 6:
3250      windows[argblock.arg[0]].leftmar = argblock.arg[2];
3251      break;
3252    case 7:
3253      windows[argblock.arg[0]].rightmar = argblock.arg[2];
3254      break;
3255    case 8:
3256      windows[argblock.arg[0]].newline_routine = argblock.arg[2];
3257      break;
3258    case 9:
3259      windows[argblock.arg[0]].countdown = argblock.arg[2];
3260      break;
3261    case 10:
3262      zmachine_warning("Bad put_wind_prop: should use set_text_style instead");
3263      break;
3264    case 11:
3265      zmachine_warning("Bad put_wind_prop: should use set_colour instead");
3266      break;
3267    case 12:
3268      zmachine_warning("Bad put_wind_prop: should use set_font instead");
3269      break;
3270    case 13:
3271      zmachine_warning("Bad put_wind_prop: should use set_font instead");
3272      break;
3273    case 14:
3274      zmachine_warning("Bad put_wind_prop: should use window_style instead");
3275      break;
3276    case 15:
3277      windows[argblock.arg[0]].line_count = argblock.arg[2];
3278      break;
3279
3280    default:
3281      zmachine_fatal("Attempt to access out of range window property %i", argblock.arg[1]);
3282   }
3283  v6_set_window(win);
3284%}
3285
3286OPCODE "print_form"    EXT:0x1a               VERSION 6
3287%{
3288  ZByte* table;
3289  int len;
3290  int first;
3291
3292  table = Address((ZUWord) argblock.arg[0]);
3293  first = 1;
3294
3295  do
3296    {
3297      int x;
3298
3299      len = (table[0]<<8)|(table[1]);
3300      if (!first && len > 0)
3301	stream_printf("\n");
3302      first = 0;
3303
3304      for (x=0; x<len; x++)
3305	{
3306	  stream_printf("%c", table[x+2]);
3307	}
3308
3309      table += len+2;
3310    }
3311  while (len != 0);
3312%}
3313
3314OPCODE "make_menu"     EXT:0x1b        BRANCH VERSION 6
3315%{
3316  /* Hum, the spec is unclear on what the branch does... */
3317  /* printf_debug("make_menu not implemented\n"); */
3318
3319  result = 0;
3320  dobranch;
3321%}
3322
3323OPCODE "picture_table" EXT:0x1c               VERSION 6
3324%{
3325  /* Does nothing */
3326%}
3327
3328OPCODE "buffer_screen" EXT:0x1d STORE		  VERSION 6
3329%{
3330  /* Not really supported */
3331  /* Zoom really has no choice with screen buffering, especially in the cocoa version */
3332  if (arg1 == 0 || arg1 == 1) {
3333	/* Exactly the same for zoom */
3334  } else if (arg1 == -1) {
3335	/* Implement me! */
3336	display_flush();
3337  } else {
3338	zmachine_warning("buffer_screen opcode cannot take values other than 0, 1 or -1");
3339  }
3340
3341  store(stack, st, 0);
3342%}
3343
3344####                           ----// 888 \\----                           ####
3345# Standard 1.1 opcodes
3346
3347OPCODE "set_true_colour" EXT:0x0d VERSION 5,7,8
3348%{
3349  int fore, back;
3350
3351#ifndef SPEC_11
3352  zmachine_warning("set_true_colour has no effect before standard 1.1");
3353#else
3354  stream_flush_buffer();
3355
3356  fore = arg1+16;
3357  back = arg2+16;
3358
3359  if (arg1 < 0)
3360    fore = arg1;
3361  if (arg2 < 0)
3362    back = arg2;
3363
3364  display_set_colour(fore, back);
3365#endif
3366%}
3367
3368OPCODE "sound_data" EXT:0x0e BRANCH VERSION 5,6,7,8
3369%{
3370#ifndef SPEC_11
3371  zmachine_warning("sound_data has no effect before standard 1.1");
3372#else
3373#endif
3374
3375  result = 0;
3376  dobranch;
3377%}
3378
3379####                           ----// 888 \\----                           ####
3380# Our own extensions - benchmarking/profiling operations
3381OPCODE "start_timer"   EXT:0x80               VERSION 4,5,6,7,8
3382%{
3383  start_clock = clock();
3384%}
3385
3386OPCODE "stop_timer"    EXT:0x81               VERSION 4,5,6,7,8
3387%{
3388  end_clock = clock();
3389%}
3390
3391OPCODE "read_timer"    EXT:0x82 STORE         VERSION 4,5,6,7,8
3392%{
3393  clock_t now;
3394
3395  now = end_clock - start_clock;
3396
3397  store(stack, st, (ZUWord) (now*100)/CLOCKS_PER_SEC);
3398%}
3399
3400OPCODE "print_timer"   EXT:0x83      	      VERSION 4,5,6,7,8
3401%{
3402  clock_t now;
3403
3404  now = end_clock - start_clock;
3405
3406  stream_printf("%i.%02i secs", (signed int) (now/CLOCKS_PER_SEC),
3407		(signed int) ((now*100)/CLOCKS_PER_SEC)%100);
3408%}
3409
3410OPCODE "print_stack"   EXT:0x84      	      VERSION 5,7,8
3411%{
3412    zmachine_dump_stack(stack);
3413%}
3414