Improved Glulxercise test program
[projects/chimara/chimara.git] / interpreters / glulxe / operand.c
1 /* operand.c: Glulxe code for instruction operands, reading and writing.
2     Designed by Andrew Plotkin <erkyrath@eblong.com>
3     http://eblong.com/zarf/glulx/index.html
4 */
5
6 #include "glk.h"
7 #include "glulxe.h"
8 #include "opcodes.h"
9
10 /* ### We could save a few cycles per operand by generating a function for
11    each operandlist type. */
12
13 /* fast_operandlist[]:
14    This is a handy array in which to look up operandlists quickly.
15    It stores the operandlists for the first 128 opcodes, which are
16    the ones used most frequently.
17 */
18 operandlist_t *fast_operandlist[0x80];
19
20 /* The actual immutable structures which lookup_operandlist()
21    returns. */
22 static operandlist_t list_none = { 0, 4, NULL };
23
24 static int array_S[1] = { modeform_Store };
25 static operandlist_t list_S = { 1, 4, array_S };
26 static int array_LS[2] = { modeform_Load, modeform_Store };
27 static operandlist_t list_LS = { 2, 4, array_LS };
28 static int array_LLS[3] = { modeform_Load, modeform_Load, modeform_Store };
29 static operandlist_t list_LLS = { 3, 4, array_LLS };
30 static int array_LLLS[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Store };
31 static operandlist_t list_LLLS = { 4, 4, array_LLLS };
32 static int array_LLLLS[5] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
33 static operandlist_t list_LLLLS = { 5, 4, array_LLLLS };
34 static int array_LLLLLS[6] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
35 static operandlist_t list_LLLLLS = { 6, 4, array_LLLLLS };
36 static int array_LLLLLLS[7] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
37 static operandlist_t list_LLLLLLS = { 7, 4, array_LLLLLLS };
38 static int array_LLLLLLLS[8] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
39 static operandlist_t list_LLLLLLLS = { 8, 4, array_LLLLLLLS };
40
41 static int array_L[1] = { modeform_Load };
42 static operandlist_t list_L = { 1, 4, array_L };
43 static int array_LL[2] = { modeform_Load, modeform_Load };
44 static operandlist_t list_LL = { 2, 4, array_LL };
45 static int array_LLL[3] = { modeform_Load, modeform_Load, modeform_Load };
46 static operandlist_t list_LLL = { 3, 4, array_LLL };
47 static operandlist_t list_2LS = { 2, 2, array_LS };
48 static operandlist_t list_1LS = { 2, 1, array_LS };
49 static int array_SL[2] = { modeform_Store, modeform_Load };
50 static operandlist_t list_SL = { 2, 4, array_SL };
51 static int array_SS[2] = { modeform_Store, modeform_Store };
52 static operandlist_t list_SS = { 2, 4, array_SS };
53
54 /* init_operands():
55    Set up the fast-lookup array of operandlists. This is called just
56    once, when the terp starts up. 
57 */
58 void init_operands()
59 {
60   int ix;
61   for (ix=0; ix<0x80; ix++)
62     fast_operandlist[ix] = lookup_operandlist(ix);
63 }
64
65 /* lookup_operandlist():
66    Return the operandlist for a given opcode. For opcodes in the range
67    00..7F, it's faster to use the array fast_operandlist[]. 
68 */
69 operandlist_t *lookup_operandlist(glui32 opcode)
70 {
71   switch (opcode) {
72   case op_nop: 
73     return &list_none;
74
75   case op_add:
76   case op_sub:
77   case op_mul:
78   case op_div:
79   case op_mod:
80   case op_bitand:
81   case op_bitor:
82   case op_bitxor:
83   case op_shiftl:
84   case op_sshiftr:
85   case op_ushiftr:
86     return &list_LLS;
87
88   case op_neg:
89   case op_bitnot:
90     return &list_LS;
91
92   case op_jump:
93   case op_jumpabs:
94     return &list_L;
95   case op_jz:
96   case op_jnz:
97     return &list_LL;
98   case op_jeq:
99   case op_jne:
100   case op_jlt:
101   case op_jge:
102   case op_jgt:
103   case op_jle:
104   case op_jltu:
105   case op_jgeu:
106   case op_jgtu:
107   case op_jleu:
108     return &list_LLL;
109
110   case op_call:
111     return &list_LLS;
112   case op_return:
113     return &list_L;
114   case op_catch:
115     return &list_SL;
116   case op_throw:
117     return &list_LL;
118   case op_tailcall:
119     return &list_LL;
120
121   case op_sexb:
122   case op_sexs:
123     return &list_LS;
124
125   case op_copy:
126     return &list_LS;
127   case op_copys:
128     return &list_2LS;
129   case op_copyb:
130     return &list_1LS;
131   case op_aload:
132   case op_aloads:
133   case op_aloadb:
134   case op_aloadbit:
135     return &list_LLS;
136   case op_astore:
137   case op_astores:
138   case op_astoreb:
139   case op_astorebit:
140     return &list_LLL;
141
142   case op_stkcount:
143     return &list_S;
144   case op_stkpeek:
145     return &list_LS;
146   case op_stkswap: 
147     return &list_none;
148   case op_stkroll:
149     return &list_LL;
150   case op_stkcopy:
151     return &list_L;
152
153   case op_streamchar:
154   case op_streamunichar:
155   case op_streamnum:
156   case op_streamstr:
157     return &list_L;
158   case op_getstringtbl:
159     return &list_S;
160   case op_setstringtbl:
161     return &list_L;
162   case op_getiosys:
163     return &list_SS;
164   case op_setiosys:
165     return &list_LL;
166
167   case op_random:
168     return &list_LS;
169   case op_setrandom:
170     return &list_L;
171
172   case op_verify:
173     return &list_S;
174   case op_restart:
175     return &list_none;
176   case op_save:
177   case op_restore:
178     return &list_LS;
179   case op_saveundo:
180   case op_restoreundo:
181     return &list_S;
182   case op_protect:
183     return &list_LL;
184
185   case op_quit:
186     return &list_none;
187
188   case op_gestalt:
189     return &list_LLS;
190
191   case op_debugtrap: 
192     return &list_L;
193
194   case op_getmemsize:
195     return &list_S;
196   case op_setmemsize:
197     return &list_LS;
198
199   case op_linearsearch:
200     return &list_LLLLLLLS;
201   case op_binarysearch:
202     return &list_LLLLLLLS;
203   case op_linkedsearch:
204     return &list_LLLLLLS;
205
206   case op_glk:
207     return &list_LLS;
208
209   case op_callf:
210     return &list_LS;
211   case op_callfi:
212     return &list_LLS;
213   case op_callfii:
214     return &list_LLLS;
215   case op_callfiii:
216     return &list_LLLLS;
217
218   case op_mzero:
219     return &list_LL;
220   case op_mcopy:
221     return &list_LLL;
222   case op_malloc:
223     return &list_LS;
224   case op_mfree:
225     return &list_L;
226
227   case op_accelfunc:
228   case op_accelparam:
229     return &list_LL;
230
231   default: 
232     return NULL;
233   }
234 }
235
236 /* parse_operands():
237    Read the list of operands of an instruction, and put the values
238    in inst. This assumes that the PC is at the beginning of the
239    operand mode list (right after an opcode number.) Upon return,
240    the PC will be at the beginning of the next instruction.
241 */
242 void parse_operands(instruction_t *inst, operandlist_t *oplist)
243 {
244   int ix;
245   int numops = oplist->num_ops;
246   int argsize = oplist->arg_size;
247   glui32 modeaddr = pc;
248   int modeval;
249
250   inst->desttype = 0;
251
252   pc += (numops+1) / 2;
253
254   for (ix=0; ix<numops; ix++) {
255     int mode;
256     glui32 value;
257     glui32 addr;
258
259     if ((ix & 1) == 0) {
260       modeval = Mem1(modeaddr);
261       mode = (modeval & 0x0F);
262     }
263     else {
264       mode = ((modeval >> 4) & 0x0F);
265       modeaddr++;
266     }
267
268     if (oplist->formlist[ix] == modeform_Load) {
269
270       switch (mode) {
271
272       case 8: /* pop off stack */
273         if (stackptr < valstackbase+4) {
274           fatal_error("Stack underflow in operand.");
275         }
276         stackptr -= 4;
277         value = Stk4(stackptr);
278         break;
279
280       case 0: /* constant zero */
281         value = 0;
282         break;
283
284       case 1: /* one-byte constant */
285         /* Sign-extend from 8 bits to 32 */
286         value = (glsi32)(signed char)(Mem1(pc));
287         pc++;
288         break;
289
290       case 2: /* two-byte constant */
291         /* Sign-extend the first byte from 8 bits to 32; the subsequent
292            byte must not be sign-extended. */
293         value = (glsi32)(signed char)(Mem1(pc));
294         pc++;
295         value = (value << 8) | (glui32)(Mem1(pc));
296         pc++;
297         break;
298
299       case 3: /* four-byte constant */
300         /* Bytes must not be sign-extended. */
301         value = Mem4(pc);
302         pc += 4;
303         break;
304
305       case 15: /* main memory RAM, four-byte address */
306         addr = Mem4(pc);
307         addr += ramstart;
308         pc += 4;
309         goto MainMemAddr; 
310
311       case 14: /* main memory RAM, two-byte address */
312         addr = (glui32)Mem2(pc);
313         addr += ramstart;
314         pc += 2;
315         goto MainMemAddr; 
316
317       case 13: /* main memory RAM, one-byte address */
318         addr = (glui32)(Mem1(pc));
319         addr += ramstart;
320         pc++;
321         goto MainMemAddr; 
322         
323       case 7: /* main memory, four-byte address */
324         addr = Mem4(pc);
325         pc += 4;
326         goto MainMemAddr;
327
328       case 6: /* main memory, two-byte address */
329         addr = (glui32)Mem2(pc);
330         pc += 2;
331         goto MainMemAddr;
332
333       case 5: /* main memory, one-byte address */
334         addr = (glui32)(Mem1(pc));
335         pc++;
336         /* fall through */
337
338       MainMemAddr:
339         /* cases 5, 6, 7, 13, 14, 15 all wind up here. */
340         if (argsize == 4) {
341           value = Mem4(addr);
342         }
343         else if (argsize == 2) {
344           value = Mem2(addr);
345         }
346         else {
347           value = Mem1(addr);
348         }
349         break;
350
351       case 11: /* locals, four-byte address */
352         addr = Mem4(pc);
353         pc += 4;
354         goto LocalsAddr;
355
356       case 10: /* locals, two-byte address */
357         addr = (glui32)Mem2(pc);
358         pc += 2;
359         goto LocalsAddr; 
360
361       case 9: /* locals, one-byte address */
362         addr = (glui32)(Mem1(pc));
363         pc++;
364         /* fall through */
365
366       LocalsAddr:
367         /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
368            be four-byte aligned, but we don't check this explicitly. 
369            A "strict mode" interpreter probably should. It's also illegal
370            for addr to be less than zero or greater than the size of
371            the locals segment. */
372         addr += localsbase;
373         if (argsize == 4) {
374           value = Stk4(addr);
375         }
376         else if (argsize == 2) {
377           value = Stk2(addr);
378         }
379         else {
380           value = Stk1(addr);
381         }
382         break;
383
384       default:
385         fatal_error("Unknown addressing mode in load operand.");
386       }
387
388       inst->value[ix] = value;
389
390     }
391     else {  /* modeform_Store */
392       switch (mode) {
393
394       case 0: /* discard value */
395         inst->desttype = 0;
396         inst->value[ix] = 0;
397         break;
398
399       case 8: /* push on stack */
400         inst->desttype = 3;
401         inst->value[ix] = 0;
402         break;
403
404       case 15: /* main memory RAM, four-byte address */
405         addr = Mem4(pc);
406         addr += ramstart;
407         pc += 4;
408         goto WrMainMemAddr; 
409
410       case 14: /* main memory RAM, two-byte address */
411         addr = (glui32)Mem2(pc);
412         addr += ramstart;
413         pc += 2;
414         goto WrMainMemAddr; 
415
416       case 13: /* main memory RAM, one-byte address */
417         addr = (glui32)(Mem1(pc));
418         addr += ramstart;
419         pc++;
420         goto WrMainMemAddr; 
421
422       case 7: /* main memory, four-byte address */
423         addr = Mem4(pc);
424         pc += 4;
425         goto WrMainMemAddr;
426
427       case 6: /* main memory, two-byte address */
428         addr = (glui32)Mem2(pc);
429         pc += 2;
430         goto WrMainMemAddr;
431
432       case 5: /* main memory, one-byte address */
433         addr = (glui32)(Mem1(pc));
434         pc++;
435         /* fall through */
436
437       WrMainMemAddr:
438         /* cases 5, 6, 7 all wind up here. */
439         inst->desttype = 1;
440         inst->value[ix] = addr;
441         break;
442
443       case 11: /* locals, four-byte address */
444         addr = Mem4(pc);
445         pc += 4;
446         goto WrLocalsAddr;
447
448       case 10: /* locals, two-byte address */
449         addr = (glui32)Mem2(pc);
450         pc += 2;
451         goto WrLocalsAddr; 
452
453       case 9: /* locals, one-byte address */
454         addr = (glui32)(Mem1(pc));
455         pc++;
456         /* fall through */
457
458       WrLocalsAddr:
459         /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
460            be four-byte aligned, but we don't check this explicitly. 
461            A "strict mode" interpreter probably should. It's also illegal
462            for addr to be less than zero or greater than the size of
463            the locals segment. */
464         inst->desttype = 2;
465         /* We don't add localsbase here; the store address for desttype 2
466            is relative to the current locals segment, not an absolute
467            stack position. */
468         inst->value[ix] = addr;
469         break;
470
471       case 1:
472       case 2:
473       case 3:
474         fatal_error("Constant addressing mode in store operand.");
475
476       default:
477         fatal_error("Unknown addressing mode in store operand.");
478       }
479     }
480   }
481 }
482
483 /* store_operand():
484    Store a result value, according to the desttype and destaddress given.
485    This is usually used to store the result of an opcode, but it's also
486    used by any code that pulls a call-stub off the stack.
487 */
488 void store_operand(glui32 desttype, glui32 destaddr, glui32 storeval)
489 {
490   switch (desttype) {
491
492   case 0: /* do nothing; discard the value. */
493     return;
494
495   case 1: /* main memory. */
496     MemW4(destaddr, storeval);
497     return;
498
499   case 2: /* locals. */
500     destaddr += localsbase;
501     StkW4(destaddr, storeval);
502     return;
503
504   case 3: /* push on stack. */
505     if (stackptr+4 > stacksize) {
506       fatal_error("Stack overflow in store operand.");
507     }
508     StkW4(stackptr, storeval);
509     stackptr += 4;
510     return;
511
512   default:
513     fatal_error("Unknown destination type in store operand.");
514
515   }
516 }
517
518 void store_operand_s(glui32 desttype, glui32 destaddr, glui32 storeval)
519 {
520   storeval &= 0xFFFF;
521
522   switch (desttype) {
523
524   case 0: /* do nothing; discard the value. */
525     return;
526
527   case 1: /* main memory. */
528     MemW2(destaddr, storeval);
529     return;
530
531   case 2: /* locals. */
532     destaddr += localsbase;
533     StkW2(destaddr, storeval);
534     return;
535
536   case 3: /* push on stack. A four-byte value is actually pushed. */
537     if (stackptr+4 > stacksize) {
538       fatal_error("Stack overflow in store operand.");
539     }
540     StkW4(stackptr, storeval);
541     stackptr += 4;
542     return;
543
544   default:
545     fatal_error("Unknown destination type in store operand.");
546
547   }
548 }
549
550 void store_operand_b(glui32 desttype, glui32 destaddr, glui32 storeval)
551 {
552   storeval &= 0xFF;
553
554   switch (desttype) {
555
556   case 0: /* do nothing; discard the value. */
557     return;
558
559   case 1: /* main memory. */
560     MemW1(destaddr, storeval);
561     return;
562
563   case 2: /* locals. */
564     destaddr += localsbase;
565     StkW1(destaddr, storeval);
566     return;
567
568   case 3: /* push on stack. A four-byte value is actually pushed. */
569     if (stackptr+4 > stacksize) {
570       fatal_error("Stack overflow in store operand.");
571     }
572     StkW4(stackptr, storeval);
573     stackptr += 4;
574     return;
575
576   default:
577     fatal_error("Unknown destination type in store operand.");
578
579   }
580 }