aboutsummaryrefslogtreecommitdiff
path: root/engines/glk
diff options
context:
space:
mode:
authorPaul Gilbert2019-04-14 22:06:49 -0700
committerPaul Gilbert2019-04-17 20:46:06 -0700
commit4ee1b98b86dfa7ccc3b8097a96a883e8d23abe48 (patch)
tree946aca8c823c5a4403d80aacf314f558540f2338 /engines/glk
parent9ceb83972adc62aff28618c196f0a473fb85b1a0 (diff)
downloadscummvm-rg350-4ee1b98b86dfa7ccc3b8097a96a883e8d23abe48.tar.gz
scummvm-rg350-4ee1b98b86dfa7ccc3b8097a96a883e8d23abe48.tar.bz2
scummvm-rg350-4ee1b98b86dfa7ccc3b8097a96a883e8d23abe48.zip
GLK: GLULXE: Add operand methods
Diffstat (limited to 'engines/glk')
-rw-r--r--engines/glk/glulxe/exec.cpp2
-rw-r--r--engines/glk/glulxe/glulxe.h42
-rw-r--r--engines/glk/glulxe/glulxe_types.h6
-rw-r--r--engines/glk/glulxe/operand.cpp617
-rw-r--r--engines/glk/module.mk1
5 files changed, 660 insertions, 8 deletions
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index f3b511f43c..766e8b6042 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -29,7 +29,7 @@ void Glulxe::execute_loop() {
bool done_executing = false;
int ix;
uint opcode;
- operandlist_t *oplist;
+ const operandlist_t *oplist;
oparg_t inst[MAX_OPERANDS];
uint value, addr, val0, val1;
int vals0, vals1;
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index eacc51fd56..97a7c09179 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -108,6 +108,17 @@ public:
heapblock_t *heap_tail = NULL;
/**@}*/
+
+ /**
+ * \defgroup operand fields
+ * @{
+ */
+
+ /**
+ * This is a handy array in which to look up operandlists quickly. It stores the operandlists
+ * for the first 128 opcodes, which are the ones used most frequently.
+ */
+ const operandlist_t *fast_operandlist[0x80];
protected:
/**
* \defgroup glkop fields
@@ -330,11 +341,34 @@ public:
/**
* \defgroup Operand access methods
* @{
- */ operandlist_t *fast_operandlist[0x80];
- void init_operands(void);
- operandlist_t *lookup_operandlist(uint opcode);
- void parse_operands(oparg_t *opargs, operandlist_t *oplist);
+ */
+
+ /**
+ * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up.
+ */
+ void init_operands();
+
+ /**
+ * Return the operandlist for a given opcode. For opcodes in the range 00..7F, it's faster
+ * to use the array fast_operandlist[].
+ */
+ const operandlist_t *lookup_operandlist(uint opcode);
+
+ /**
+ * Read the list of operands of an instruction, and put the values in args. This assumes
+ * that the PC is at the beginning of the operand mode list (right after an opcode number.)
+ * Upon return, the PC will be at the beginning of the next instruction.
+ *
+ * This also assumes that args points at an allocated array of MAX_OPERANDS oparg_t structures.
+ */
+ void parse_operands(oparg_t *opargs, const operandlist_t *oplist);
+
+ /**
+ * Store a result value, according to the desttype and destaddress given. This is usually used to store
+ * the result of an opcode, but it's also used by any code that pulls a call-stub off the stack.
+ */
void store_operand(uint desttype, uint destaddr, uint storeval);
+
void store_operand_s(uint desttype, uint destaddr, uint storeval);
void store_operand_b(uint desttype, uint destaddr, uint storeval);
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 96943d2f2a..8c99d9047d 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -313,9 +313,9 @@ typedef classtable_struct classtable_t;
* Represents the operand structure of an opcode.
*/
struct operandlist_struct {
- int num_ops; /* Number of operands for this opcode */
- int arg_size; /* Usually 4, but can be 1 or 2 */
- int *formlist; /* Array of values, either modeform_Load or modeform_Store */
+ int num_ops; ///< Number of operands for this opcode
+ int arg_size; ///< Usually 4, but can be 1 or 2
+ const int *formlist; ///< Array of values, either modeform_Load or modeform_Store
};
typedef operandlist_struct operandlist_t;
diff --git a/engines/glk/glulxe/operand.cpp b/engines/glk/glulxe/operand.cpp
new file mode 100644
index 0000000000..a48d68e0b9
--- /dev/null
+++ b/engines/glk/glulxe/operand.cpp
@@ -0,0 +1,617 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+/**
+ * The actual immutable structures which lookup_operandlist() returns.
+ */
+static const operandlist_t list_none = { 0, 4, NULL };
+
+static const int array_S[1] = { modeform_Store };
+static const operandlist_t list_S = { 1, 4, &array_S[0] };
+static const int array_LS[2] = { modeform_Load, modeform_Store };
+static const operandlist_t list_LS = { 2, 4, &array_LS[0] };
+static const int array_LLS[3] = { modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLS = { 3, 4, &array_LLS[0] };
+static const int array_LLLS[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLS = { 4, 4, &array_LLLS[0] };
+static const int array_LLLLS[5] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLS = { 5, 4, &array_LLLLS[0] };
+/* static const int array_LLLLLS[6] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLLS = { 6, 4, &array_LLLLLS }; */ /* not currently used */
+static const int array_LLLLLLS[7] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLLLS = { 7, 4, &array_LLLLLLS[0] };
+static const int array_LLLLLLLS[8] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLLLLS = { 8, 4, &array_LLLLLLLS[0] };
+
+static const int array_L[1] = { modeform_Load };
+static const operandlist_t list_L = { 1, 4, &array_L[0] };
+static const int array_LL[2] = { modeform_Load, modeform_Load };
+static const operandlist_t list_LL = { 2, 4, &array_LL[0] };
+static const int array_LLL[3] = { modeform_Load, modeform_Load, modeform_Load };
+static const operandlist_t list_LLL = { 3, 4, &array_LLL[0] };
+static const operandlist_t list_2LS = { 2, 2, &array_LS[0] };
+static const operandlist_t list_1LS = { 2, 1, &array_LS[0] };
+static const int array_LLLL[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load };
+static const operandlist_t list_LLLL = { 4, 4, &array_LLLL[0] };
+static const int array_SL[2] = { modeform_Store, modeform_Load };
+static const operandlist_t list_SL = { 2, 4, &array_SL[0] };
+static const int array_SS[2] = { modeform_Store, modeform_Store };
+static const operandlist_t list_SS = { 2, 4, &array_SS[0] };
+static const int array_LLSS[4] = { modeform_Load, modeform_Load, modeform_Store, modeform_Store };
+static const operandlist_t list_LLSS = { 4, 4, &array_LLSS[0] };
+
+void Glulxe::init_operands() {
+ for (int ix=0; ix<0x80; ix++)
+ fast_operandlist[ix] = lookup_operandlist(ix);
+}
+
+const operandlist_t *Glulxe::lookup_operandlist(uint opcode) {
+ switch (opcode) {
+ case op_nop:
+ return &list_none;
+
+ case op_add:
+ case op_sub:
+ case op_mul:
+ case op_div:
+ case op_mod:
+ case op_bitand:
+ case op_bitor:
+ case op_bitxor:
+ case op_shiftl:
+ case op_sshiftr:
+ case op_ushiftr:
+ return &list_LLS;
+
+ case op_neg:
+ case op_bitnot:
+ return &list_LS;
+
+ case op_jump:
+ case op_jumpabs:
+ return &list_L;
+ case op_jz:
+ case op_jnz:
+ return &list_LL;
+ case op_jeq:
+ case op_jne:
+ case op_jlt:
+ case op_jge:
+ case op_jgt:
+ case op_jle:
+ case op_jltu:
+ case op_jgeu:
+ case op_jgtu:
+ case op_jleu:
+ return &list_LLL;
+
+ case op_call:
+ return &list_LLS;
+ case op_return:
+ return &list_L;
+ case op_catch:
+ return &list_SL;
+ case op_throw:
+ return &list_LL;
+ case op_tailcall:
+ return &list_LL;
+
+ case op_sexb:
+ case op_sexs:
+ return &list_LS;
+
+ case op_copy:
+ return &list_LS;
+ case op_copys:
+ return &list_2LS;
+ case op_copyb:
+ return &list_1LS;
+ case op_aload:
+ case op_aloads:
+ case op_aloadb:
+ case op_aloadbit:
+ return &list_LLS;
+ case op_astore:
+ case op_astores:
+ case op_astoreb:
+ case op_astorebit:
+ return &list_LLL;
+
+ case op_stkcount:
+ return &list_S;
+ case op_stkpeek:
+ return &list_LS;
+ case op_stkswap:
+ return &list_none;
+ case op_stkroll:
+ return &list_LL;
+ case op_stkcopy:
+ return &list_L;
+
+ case op_streamchar:
+ case op_streamunichar:
+ case op_streamnum:
+ case op_streamstr:
+ return &list_L;
+ case op_getstringtbl:
+ return &list_S;
+ case op_setstringtbl:
+ return &list_L;
+ case op_getiosys:
+ return &list_SS;
+ case op_setiosys:
+ return &list_LL;
+
+ case op_random:
+ return &list_LS;
+ case op_setrandom:
+ return &list_L;
+
+ case op_verify:
+ return &list_S;
+ case op_restart:
+ return &list_none;
+ case op_save:
+ case op_restore:
+ return &list_LS;
+ case op_saveundo:
+ case op_restoreundo:
+ return &list_S;
+ case op_protect:
+ return &list_LL;
+
+ case op_quit:
+ return &list_none;
+
+ case op_gestalt:
+ return &list_LLS;
+
+ case op_debugtrap:
+ return &list_L;
+
+ case op_getmemsize:
+ return &list_S;
+ case op_setmemsize:
+ return &list_LS;
+
+ case op_linearsearch:
+ return &list_LLLLLLLS;
+ case op_binarysearch:
+ return &list_LLLLLLLS;
+ case op_linkedsearch:
+ return &list_LLLLLLS;
+
+ case op_glk:
+ return &list_LLS;
+
+ case op_callf:
+ return &list_LS;
+ case op_callfi:
+ return &list_LLS;
+ case op_callfii:
+ return &list_LLLS;
+ case op_callfiii:
+ return &list_LLLLS;
+
+ case op_mzero:
+ return &list_LL;
+ case op_mcopy:
+ return &list_LLL;
+ case op_malloc:
+ return &list_LS;
+ case op_mfree:
+ return &list_L;
+
+ case op_accelfunc:
+ case op_accelparam:
+ return &list_LL;
+
+#ifdef FLOAT_SUPPORT
+
+ case op_numtof:
+ case op_ftonumz:
+ case op_ftonumn:
+ case op_ceil:
+ case op_floor:
+ case op_sqrt:
+ case op_exp:
+ case op_log:
+ return &list_LS;
+ case op_fadd:
+ case op_fsub:
+ case op_fmul:
+ case op_fdiv:
+ case op_pow:
+ case op_atan2:
+ return &list_LLS;
+ case op_fmod:
+ return &list_LLSS;
+ case op_sin:
+ case op_cos:
+ case op_tan:
+ case op_asin:
+ case op_acos:
+ case op_atan:
+ return &list_LS;
+ case op_jfeq:
+ case op_jfne:
+ return &list_LLLL;
+ case op_jflt:
+ case op_jfle:
+ case op_jfgt:
+ case op_jfge:
+ return &list_LLL;
+ case op_jisnan:
+ case op_jisinf:
+ return &list_LL;
+
+#endif /* FLOAT_SUPPORT */
+
+#ifdef GLULX_EXTEND_OPERANDS
+ GLULX_EXTEND_OPERANDS
+#endif /* GLULX_EXTEND_OPERANDS */
+
+ default:
+ return NULL;
+ }
+}
+
+void Glulxe::parse_operands(oparg_t *args, const operandlist_t *oplist) {
+ int ix;
+ oparg_t *curarg;
+ int numops = oplist->num_ops;
+ int argsize = oplist->arg_size;
+ uint modeaddr = pc;
+ int modeval = 0;
+
+ pc += (numops+1) / 2;
+
+ for (ix=0, curarg=args; ix<numops; ix++, curarg++) {
+ int mode;
+ uint value;
+ uint addr;
+
+ curarg->desttype = 0;
+
+ if ((ix & 1) == 0) {
+ modeval = Mem1(modeaddr);
+ mode = (modeval & 0x0F);
+ }
+ else {
+ mode = ((modeval >> 4) & 0x0F);
+ modeaddr++;
+ }
+
+ if (oplist->formlist[ix] == modeform_Load) {
+
+ switch (mode) {
+
+ case 8: /* pop off stack */
+ if (stackptr < valstackbase+4) {
+ fatal_error("Stack underflow in operand.");
+ }
+ stackptr -= 4;
+ value = Stk4(stackptr);
+ break;
+
+ case 0: /* constant zero */
+ value = 0;
+ break;
+
+ case 1: /* one-byte constant */
+ /* Sign-extend from 8 bits to 32 */
+ value = (int)(signed char)(Mem1(pc));
+ pc++;
+ break;
+
+ case 2: /* two-byte constant */
+ /* Sign-extend the first byte from 8 bits to 32; the subsequent
+ byte must not be sign-extended. */
+ value = (int)(signed char)(Mem1(pc));
+ pc++;
+ value = (value << 8) | (uint)(Mem1(pc));
+ pc++;
+ break;
+
+ case 3: /* four-byte constant */
+ /* Bytes must not be sign-extended. */
+ value = Mem4(pc);
+ pc += 4;
+ break;
+
+ case 15: /* main memory RAM, four-byte address */
+ addr = Mem4(pc);
+ addr += ramstart;
+ pc += 4;
+ goto MainMemAddr;
+
+ case 14: /* main memory RAM, two-byte address */
+ addr = (uint)Mem2(pc);
+ addr += ramstart;
+ pc += 2;
+ goto MainMemAddr;
+
+ case 13: /* main memory RAM, one-byte address */
+ addr = (uint)(Mem1(pc));
+ addr += ramstart;
+ pc++;
+ goto MainMemAddr;
+
+ case 7: /* main memory, four-byte address */
+ addr = Mem4(pc);
+ pc += 4;
+ goto MainMemAddr;
+
+ case 6: /* main memory, two-byte address */
+ addr = (uint)Mem2(pc);
+ pc += 2;
+ goto MainMemAddr;
+
+ case 5: /* main memory, one-byte address */
+ addr = (uint)(Mem1(pc));
+ pc++;
+ /* fall through */
+
+ MainMemAddr:
+ /* cases 5, 6, 7, 13, 14, 15 all wind up here. */
+ if (argsize == 4) {
+ value = Mem4(addr);
+ }
+ else if (argsize == 2) {
+ value = Mem2(addr);
+ }
+ else {
+ value = Mem1(addr);
+ }
+ break;
+
+ case 11: /* locals, four-byte address */
+ addr = Mem4(pc);
+ pc += 4;
+ goto LocalsAddr;
+
+ case 10: /* locals, two-byte address */
+ addr = (uint)Mem2(pc);
+ pc += 2;
+ goto LocalsAddr;
+
+ case 9: /* locals, one-byte address */
+ addr = (uint)(Mem1(pc));
+ pc++;
+ /* fall through */
+
+ LocalsAddr:
+ /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
+ be four-byte aligned, but we don't check this explicitly.
+ A "strict mode" interpreter probably should. It's also illegal
+ for addr to be less than zero or greater than the size of
+ the locals segment. */
+ addr += localsbase;
+ if (argsize == 4) {
+ value = Stk4(addr);
+ }
+ else if (argsize == 2) {
+ value = Stk2(addr);
+ }
+ else {
+ value = Stk1(addr);
+ }
+ break;
+
+ default:
+ value = 0;
+ fatal_error("Unknown addressing mode in load operand.");
+ }
+
+ curarg->value = value;
+
+ }
+ else { /* modeform_Store */
+ switch (mode) {
+
+ case 0: /* discard value */
+ curarg->desttype = 0;
+ curarg->value = 0;
+ break;
+
+ case 8: /* push on stack */
+ curarg->desttype = 3;
+ curarg->value = 0;
+ break;
+
+ case 15: /* main memory RAM, four-byte address */
+ addr = Mem4(pc);
+ addr += ramstart;
+ pc += 4;
+ goto WrMainMemAddr;
+
+ case 14: /* main memory RAM, two-byte address */
+ addr = (uint)Mem2(pc);
+ addr += ramstart;
+ pc += 2;
+ goto WrMainMemAddr;
+
+ case 13: /* main memory RAM, one-byte address */
+ addr = (uint)(Mem1(pc));
+ addr += ramstart;
+ pc++;
+ goto WrMainMemAddr;
+
+ case 7: /* main memory, four-byte address */
+ addr = Mem4(pc);
+ pc += 4;
+ goto WrMainMemAddr;
+
+ case 6: /* main memory, two-byte address */
+ addr = (uint)Mem2(pc);
+ pc += 2;
+ goto WrMainMemAddr;
+
+ case 5: /* main memory, one-byte address */
+ addr = (uint)(Mem1(pc));
+ pc++;
+ /* fall through */
+
+ WrMainMemAddr:
+ /* cases 5, 6, 7 all wind up here. */
+ curarg->desttype = 1;
+ curarg->value = addr;
+ break;
+
+ case 11: /* locals, four-byte address */
+ addr = Mem4(pc);
+ pc += 4;
+ goto WrLocalsAddr;
+
+ case 10: /* locals, two-byte address */
+ addr = (uint)Mem2(pc);
+ pc += 2;
+ goto WrLocalsAddr;
+
+ case 9: /* locals, one-byte address */
+ addr = (uint)(Mem1(pc));
+ pc++;
+ /* fall through */
+
+ WrLocalsAddr:
+ /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
+ be four-byte aligned, but we don't check this explicitly.
+ A "strict mode" interpreter probably should. It's also illegal
+ for addr to be less than zero or greater than the size of
+ the locals segment. */
+ curarg->desttype = 2;
+ /* We don't add localsbase here; the store address for desttype 2
+ is relative to the current locals segment, not an absolute
+ stack position. */
+ curarg->value = addr;
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ fatal_error("Constant addressing mode in store operand.");
+
+ default:
+ fatal_error("Unknown addressing mode in store operand.");
+ }
+ }
+ }
+}
+
+void Glulxe::store_operand(uint desttype, uint destaddr, uint storeval) {
+ switch (desttype) {
+
+ case 0: /* do nothing; discard the value. */
+ return;
+
+ case 1: /* main memory. */
+ MemW4(destaddr, storeval);
+ return;
+
+ case 2: /* locals. */
+ destaddr += localsbase;
+ StkW4(destaddr, storeval);
+ return;
+
+ case 3: /* push on stack. */
+ if (stackptr+4 > stacksize) {
+ fatal_error("Stack overflow in store operand.");
+ }
+ StkW4(stackptr, storeval);
+ stackptr += 4;
+ return;
+
+ default:
+ fatal_error("Unknown destination type in store operand.");
+
+ }
+}
+
+void Glulxe::store_operand_s(uint desttype, uint destaddr, uint storeval) {
+ storeval &= 0xFFFF;
+
+ switch (desttype) {
+
+ case 0: /* do nothing; discard the value. */
+ return;
+
+ case 1: /* main memory. */
+ MemW2(destaddr, storeval);
+ return;
+
+ case 2: /* locals. */
+ destaddr += localsbase;
+ StkW2(destaddr, storeval);
+ return;
+
+ case 3: /* push on stack. A four-byte value is actually pushed. */
+ if (stackptr+4 > stacksize) {
+ fatal_error("Stack overflow in store operand.");
+ }
+ StkW4(stackptr, storeval);
+ stackptr += 4;
+ return;
+
+ default:
+ fatal_error("Unknown destination type in store operand.");
+
+ }
+}
+
+void Glulxe::store_operand_b(uint desttype, uint destaddr, uint storeval) {
+ storeval &= 0xFF;
+
+ switch (desttype) {
+
+ case 0: /* do nothing; discard the value. */
+ return;
+
+ case 1: /* main memory. */
+ MemW1(destaddr, storeval);
+ return;
+
+ case 2: /* locals. */
+ destaddr += localsbase;
+ StkW1(destaddr, storeval);
+ return;
+
+ case 3: /* push on stack. A four-byte value is actually pushed. */
+ if (stackptr+4 > stacksize) {
+ fatal_error("Stack overflow in store operand.");
+ }
+ StkW4(stackptr, storeval);
+ stackptr += 4;
+ return;
+
+ default:
+ fatal_error("Unknown destination type in store operand.");
+
+ }
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 669f81168e..6addf7efa2 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -66,6 +66,7 @@ MODULE_OBJS := \
glulxe/glkop.o \
glulxe/glulxe.o \
glulxe/heap.o \
+ glulxe/operand.o \
magnetic/detection.o \
magnetic/magnetic.o \
scott/detection.o \