scummvm/engines/glk/glulx/accel.cpp
2021-12-26 18:48:43 +01:00

663 lines
13 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/glulx/glulx.h"
namespace Glk {
namespace Glulx {
/**
* Git passes along function arguments in reverse order. To make our lives more interesting
*/
#ifdef ARGS_REVERSED
#define ARG(argv, argc, ix) (argv[(argc-1)-ix])
#else
#define ARG(argv, argc, ix) (argv[ix])
#endif
/**
* Any function can be called with any number of arguments. This macro lets us snarf a given argument,
* or zero if it wasn't supplied.
*/
#define ARG_IF_GIVEN(argv, argc, ix) ((argc > ix) ? (ARG(argv, argc, ix)) : 0)
acceleration_func Glulx::accel_find_func(uint index) {
switch (index) {
case 0:
return nullptr; // 0 always means no acceleration
case 1:
return &Glulx::func_1_z__region;
case 2:
return &Glulx::func_2_cp__tab;
case 3:
return &Glulx::func_3_ra__pr;
case 4:
return &Glulx::func_4_rl__pr;
case 5:
return &Glulx::func_5_oc__cl;
case 6:
return &Glulx::func_6_rv__pr;
case 7:
return &Glulx::func_7_op__pr;
case 8:
return &Glulx::func_8_cp__tab;
case 9:
return &Glulx::func_9_ra__pr;
case 10:
return &Glulx::func_10_rl__pr;
case 11:
return &Glulx::func_11_oc__cl;
case 12:
return &Glulx::func_12_rv__pr;
case 13:
return &Glulx::func_13_op__pr;
}
return nullptr;
}
acceleration_func Glulx::accel_get_func(uint addr) {
int bucknum;
accelentry_t *ptr;
if (!accelentries)
return nullptr;
bucknum = (addr % ACCEL_HASH_SIZE);
for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
if (ptr->addr == addr)
return ptr->func;
}
return nullptr;
}
void Glulx::accel_iterate_funcs(void (*func)(uint index, uint addr)) {
int bucknum;
accelentry_t *ptr;
if (!accelentries)
return;
for (bucknum = 0; bucknum < ACCEL_HASH_SIZE; bucknum++) {
for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
if (ptr->func) {
func(ptr->index, ptr->addr);
}
}
}
}
void Glulx::accel_set_func(uint index, uint addr) {
int bucknum;
accelentry_t *ptr;
int functype;
acceleration_func new_func = nullptr;
/* Check the Glulx type identifier byte. */
functype = Mem1(addr);
if (functype != 0xC0 && functype != 0xC1) {
fatal_error_i("Attempt to accelerate non-function.", addr);
}
if (!accelentries) {
accelentries = (accelentry_t **)glulx_malloc(ACCEL_HASH_SIZE
* sizeof(accelentry_t *));
if (!accelentries)
fatal_error("Cannot malloc acceleration table.");
for (bucknum = 0; bucknum < ACCEL_HASH_SIZE; bucknum++)
accelentries[bucknum] = nullptr;
}
new_func = accel_find_func(index);
/* Might be nullptr, if the index is zero or not recognized. */
bucknum = (addr % ACCEL_HASH_SIZE);
for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
if (ptr->addr == addr)
break;
}
if (!ptr) {
if (!new_func) {
return; /* no need for a new entry */
}
ptr = (accelentry_t *)glulx_malloc(sizeof(accelentry_t));
if (!ptr)
fatal_error("Cannot malloc acceleration entry.");
ptr->addr = addr;
ptr->index = 0;
ptr->func = nullptr;
ptr->next = accelentries[bucknum];
accelentries[bucknum] = ptr;
}
ptr->index = index;
ptr->func = new_func;
}
void Glulx::accel_set_param(uint index, uint val) {
switch (index) {
case 0:
classes_table = val;
break;
case 1:
indiv_prop_start = val;
break;
case 2:
class_metaclass = val;
break;
case 3:
object_metaclass = val;
break;
case 4:
routine_metaclass = val;
break;
case 5:
string_metaclass = val;
break;
case 6:
self = val;
break;
case 7:
num_attr_bytes = val;
break;
case 8:
cpv__start = val;
break;
}
}
uint Glulx::accel_get_param_count() const {
return 9;
}
uint Glulx::accel_get_param(uint index) const {
switch (index) {
case 0:
return classes_table;
case 1:
return indiv_prop_start;
case 2:
return class_metaclass;
case 3:
return object_metaclass;
case 4:
return routine_metaclass;
case 5:
return string_metaclass;
case 6:
return self;
case 7:
return num_attr_bytes;
case 8:
return cpv__start;
default:
return 0;
}
}
void Glulx::accel_error(const char *msg) {
glk_put_char('\n');
glk_put_string(msg);
glk_put_char('\n');
}
int Glulx::obj_in_class(uint obj) {
// This checks whether obj is contained in Class, not whether it is a member of Class
return (Mem4(obj + 13 + num_attr_bytes) == class_metaclass);
}
uint Glulx::get_prop(uint obj, uint id) {
uint cla = 0;
uint prop;
uint call_argv[2];
if (id & 0xFFFF0000) {
cla = Mem4(classes_table + ((id & 0xFFFF) * 4));
ARG(call_argv, 2, 0) = obj;
ARG(call_argv, 2, 1) = cla;
if (func_5_oc__cl(2, call_argv) == 0)
return 0;
id >>= 16;
obj = cla;
}
ARG(call_argv, 2, 0) = obj;
ARG(call_argv, 2, 1) = id;
prop = func_2_cp__tab(2, call_argv);
if (prop == 0)
return 0;
if (obj_in_class(obj) && (cla == 0)) {
if ((id < indiv_prop_start) || (id >= indiv_prop_start + 8))
return 0;
}
if (Mem4(self) != obj) {
if (Mem1(prop + 9) & 1)
return 0;
}
return prop;
}
uint Glulx::get_prop_new(uint obj, uint id) {
uint cla = 0;
uint prop;
uint call_argv[2];
if (id & 0xFFFF0000) {
cla = Mem4(classes_table + ((id & 0xFFFF) * 4));
ARG(call_argv, 2, 0) = obj;
ARG(call_argv, 2, 1) = cla;
if (func_11_oc__cl(2, call_argv) == 0)
return 0;
id >>= 16;
obj = cla;
}
ARG(call_argv, 2, 0) = obj;
ARG(call_argv, 2, 1) = id;
prop = func_8_cp__tab(2, call_argv);
if (prop == 0)
return 0;
if (obj_in_class(obj) && (cla == 0)) {
if ((id < indiv_prop_start) || (id >= indiv_prop_start + 8))
return 0;
}
if (Mem4(self) != obj) {
if (Mem1(prop + 9) & 1)
return 0;
}
return prop;
}
uint Glulx::func_1_z__region(uint argc, uint *argv) {
uint addr;
uint tb;
if (argc < 1)
return 0;
addr = ARG(argv, argc, 0);
if (addr < 36)
return 0;
if (addr >= endmem)
return 0;
tb = Mem1(addr);
if (tb >= 0xE0) {
return 3;
}
if (tb >= 0xC0) {
return 2;
}
if (tb >= 0x70 && tb <= 0x7F && addr >= ramstart) {
return 1;
}
return 0;
}
uint Glulx::func_2_cp__tab(uint argc, uint *argv) {
uint obj;
uint id;
uint otab, max;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
if (func_1_z__region(1, &obj) != 1) {
accel_error("[** Programming error: tried to find the \".\" of (something) **]");
return 0;
}
otab = Mem4(obj + 16);
if (!otab)
return 0;
max = Mem4(otab);
otab += 4;
/* @binarysearch id 2 otab 10 max 0 0 res; */
return binary_search(id, 2, otab, 10, max, 0, 0);
}
uint Glulx::func_3_ra__pr(uint argc, uint *argv) {
uint obj;
uint id;
uint prop;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
prop = get_prop(obj, id);
if (prop == 0)
return 0;
return Mem4(prop + 4);
}
uint Glulx::func_4_rl__pr(uint argc, uint *argv) {
uint obj;
uint id;
uint prop;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
prop = get_prop(obj, id);
if (prop == 0)
return 0;
return 4 * Mem2(prop + 2);
}
uint Glulx::func_5_oc__cl(uint argc, uint *argv) {
uint obj;
uint cla;
uint zr, prop, inlist, inlistlen, jx;
obj = ARG_IF_GIVEN(argv, argc, 0);
cla = ARG_IF_GIVEN(argv, argc, 1);
zr = func_1_z__region(1, &obj);
if (zr == 3)
return (cla == string_metaclass) ? 1 : 0;
if (zr == 2)
return (cla == routine_metaclass) ? 1 : 0;
if (zr != 1)
return 0;
if (cla == class_metaclass) {
if (obj_in_class(obj))
return 1;
if (obj == class_metaclass)
return 1;
if (obj == string_metaclass)
return 1;
if (obj == routine_metaclass)
return 1;
if (obj == object_metaclass)
return 1;
return 0;
}
if (cla == object_metaclass) {
if (obj_in_class(obj))
return 0;
if (obj == class_metaclass)
return 0;
if (obj == string_metaclass)
return 0;
if (obj == routine_metaclass)
return 0;
if (obj == object_metaclass)
return 0;
return 1;
}
if ((cla == string_metaclass) || (cla == routine_metaclass))
return 0;
if (!obj_in_class(cla)) {
accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
return 0;
}
prop = get_prop(obj, 2);
if (prop == 0)
return 0;
inlist = Mem4(prop + 4);
if (inlist == 0)
return 0;
inlistlen = Mem2(prop + 2);
for (jx = 0; jx < inlistlen; jx++) {
if (Mem4(inlist + (4 * jx)) == cla)
return 1;
}
return 0;
}
uint Glulx::func_6_rv__pr(uint argc, uint *argv) {
uint id;
uint addr;
id = ARG_IF_GIVEN(argv, argc, 1);
addr = func_3_ra__pr(argc, argv);
if (addr == 0) {
if ((id > 0) && (id < indiv_prop_start))
return Mem4(cpv__start + (4 * id));
accel_error("[** Programming error: tried to read (something) **]");
return 0;
}
return Mem4(addr);
}
uint Glulx::func_7_op__pr(uint argc, uint *argv) {
uint obj;
uint id;
uint zr;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
zr = func_1_z__region(1, &obj);
if (zr == 3) {
/* print is INDIV_PROP_START+6 */
if (id == indiv_prop_start + 6)
return 1;
/* print_to_array is INDIV_PROP_START+7 */
if (id == indiv_prop_start + 7)
return 1;
return 0;
}
if (zr == 2) {
/* call is INDIV_PROP_START+5 */
return ((id == indiv_prop_start + 5) ? 1 : 0);
}
if (zr != 1)
return 0;
if ((id >= indiv_prop_start) && (id < indiv_prop_start + 8)) {
if (obj_in_class(obj))
return 1;
}
return ((func_3_ra__pr(argc, argv)) ? 1 : 0);
}
uint Glulx::func_8_cp__tab(uint argc, uint *argv) {
uint obj;
uint id;
uint otab, max;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
if (func_1_z__region(1, &obj) != 1) {
accel_error("[** Programming error: tried to find the \".\" of (something) **]");
return 0;
}
otab = Mem4(obj + 4 * (3 + (int)(num_attr_bytes / 4)));
if (!otab)
return 0;
max = Mem4(otab);
otab += 4;
/* @binarysearch id 2 otab 10 max 0 0 res; */
return binary_search(id, 2, otab, 10, max, 0, 0);
}
uint Glulx::func_9_ra__pr(uint argc, uint *argv) {
uint obj;
uint id;
uint prop;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
prop = get_prop_new(obj, id);
if (prop == 0)
return 0;
return Mem4(prop + 4);
}
uint Glulx::func_10_rl__pr(uint argc, uint *argv) {
uint obj;
uint id;
uint prop;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
prop = get_prop_new(obj, id);
if (prop == 0)
return 0;
return 4 * Mem2(prop + 2);
}
uint Glulx::func_11_oc__cl(uint argc, uint *argv) {
uint obj;
uint cla;
uint zr, prop, inlist, inlistlen, jx;
obj = ARG_IF_GIVEN(argv, argc, 0);
cla = ARG_IF_GIVEN(argv, argc, 1);
zr = func_1_z__region(1, &obj);
if (zr == 3)
return (cla == string_metaclass) ? 1 : 0;
if (zr == 2)
return (cla == routine_metaclass) ? 1 : 0;
if (zr != 1)
return 0;
if (cla == class_metaclass) {
if (obj_in_class(obj))
return 1;
if (obj == class_metaclass)
return 1;
if (obj == string_metaclass)
return 1;
if (obj == routine_metaclass)
return 1;
if (obj == object_metaclass)
return 1;
return 0;
}
if (cla == object_metaclass) {
if (obj_in_class(obj))
return 0;
if (obj == class_metaclass)
return 0;
if (obj == string_metaclass)
return 0;
if (obj == routine_metaclass)
return 0;
if (obj == object_metaclass)
return 0;
return 1;
}
if ((cla == string_metaclass) || (cla == routine_metaclass))
return 0;
if (!obj_in_class(cla)) {
accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
return 0;
}
prop = get_prop_new(obj, 2);
if (prop == 0)
return 0;
inlist = Mem4(prop + 4);
if (inlist == 0)
return 0;
inlistlen = Mem2(prop + 2);
for (jx = 0; jx < inlistlen; jx++) {
if (Mem4(inlist + (4 * jx)) == cla)
return 1;
}
return 0;
}
uint Glulx::func_12_rv__pr(uint argc, uint *argv) {
uint id;
uint addr;
id = ARG_IF_GIVEN(argv, argc, 1);
addr = func_9_ra__pr(argc, argv);
if (addr == 0) {
if ((id > 0) && (id < indiv_prop_start))
return Mem4(cpv__start + (4 * id));
accel_error("[** Programming error: tried to read (something) **]");
return 0;
}
return Mem4(addr);
}
uint Glulx::func_13_op__pr(uint argc, uint *argv) {
uint obj;
uint id;
uint zr;
obj = ARG_IF_GIVEN(argv, argc, 0);
id = ARG_IF_GIVEN(argv, argc, 1);
zr = func_1_z__region(1, &obj);
if (zr == 3) {
/* print is INDIV_PROP_START+6 */
if (id == indiv_prop_start + 6)
return 1;
/* print_to_array is INDIV_PROP_START+7 */
if (id == indiv_prop_start + 7)
return 1;
return 0;
}
if (zr == 2) {
/* call is INDIV_PROP_START+5 */
return ((id == indiv_prop_start + 5) ? 1 : 0);
}
if (zr != 1)
return 0;
if ((id >= indiv_prop_start) && (id < indiv_prop_start + 8)) {
if (obj_in_class(obj))
return 1;
}
return ((func_9_ra__pr(argc, argv)) ? 1 : 0);
}
} // End of namespace Glulx
} // End of namespace Glk