diff options
-rw-r--r-- | engines/glk/glulxe/accel.cpp | 1010 | ||||
-rw-r--r-- | engines/glk/glulxe/exec.cpp | 2015 | ||||
-rw-r--r-- | engines/glk/glulxe/float.cpp | 183 | ||||
-rw-r--r-- | engines/glk/glulxe/funcs.cpp | 506 | ||||
-rw-r--r-- | engines/glk/glulxe/gestalt.cpp | 86 | ||||
-rw-r--r-- | engines/glk/glulxe/glkop.cpp | 2293 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe.cpp | 34 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe.h | 124 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe_types.h | 20 | ||||
-rw-r--r-- | engines/glk/glulxe/heap.cpp | 499 | ||||
-rw-r--r-- | engines/glk/glulxe/operand.cpp | 998 | ||||
-rw-r--r-- | engines/glk/glulxe/search.cpp | 335 | ||||
-rw-r--r-- | engines/glk/glulxe/serial.cpp | 1968 | ||||
-rw-r--r-- | engines/glk/glulxe/string.cpp | 1443 | ||||
-rw-r--r-- | engines/glk/glulxe/vm.cpp | 508 |
15 files changed, 5972 insertions, 6050 deletions
diff --git a/engines/glk/glulxe/accel.cpp b/engines/glk/glulxe/accel.cpp index 929c6ab461..1d0b9e5db4 100644 --- a/engines/glk/glulxe/accel.cpp +++ b/engines/glk/glulxe/accel.cpp @@ -41,581 +41,623 @@ namespace Glulxe { #define ARG_IF_GIVEN(argv, argc, ix) ((argc > ix) ? (ARG(argv, argc, ix)) : 0) acceleration_func Glulxe::accel_find_func(uint index) { - switch (index) { - case 0: return nullptr; // 0 always means no acceleration - case 1: return &Glulxe::func_1_z__region; - case 2: return &Glulxe::func_2_cp__tab; - case 3: return &Glulxe::func_3_ra__pr; - case 4: return &Glulxe::func_4_rl__pr; - case 5: return &Glulxe::func_5_oc__cl; - case 6: return &Glulxe::func_6_rv__pr; - case 7: return &Glulxe::func_7_op__pr; - case 8: return &Glulxe::func_8_cp__tab; - case 9: return &Glulxe::func_9_ra__pr; - case 10: return &Glulxe::func_10_rl__pr; - case 11: return &Glulxe::func_11_oc__cl; - case 12: return &Glulxe::func_12_rv__pr; - case 13: return &Glulxe::func_13_op__pr; - } - return nullptr; + switch (index) { + case 0: + return nullptr; // 0 always means no acceleration + case 1: + return &Glulxe::func_1_z__region; + case 2: + return &Glulxe::func_2_cp__tab; + case 3: + return &Glulxe::func_3_ra__pr; + case 4: + return &Glulxe::func_4_rl__pr; + case 5: + return &Glulxe::func_5_oc__cl; + case 6: + return &Glulxe::func_6_rv__pr; + case 7: + return &Glulxe::func_7_op__pr; + case 8: + return &Glulxe::func_8_cp__tab; + case 9: + return &Glulxe::func_9_ra__pr; + case 10: + return &Glulxe::func_10_rl__pr; + case 11: + return &Glulxe::func_11_oc__cl; + case 12: + return &Glulxe::func_12_rv__pr; + case 13: + return &Glulxe::func_13_op__pr; + } + return nullptr; } acceleration_func Glulxe::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; + 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 Glulxe::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); - } - } - } + 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 Glulxe::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; + 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 Glulxe::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; - } + 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 Glulxe::accel_get_param_count() const { - return 9; + return 9; } uint Glulxe::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; - } + 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 Glulxe::accel_error(const char *msg) { - glk_put_char('\n'); - glk_put_string(msg); - glk_put_char('\n'); + glk_put_char('\n'); + glk_put_string(msg); + glk_put_char('\n'); } int Glulxe::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); + // 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 Glulxe::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 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 Glulxe::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 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 Glulxe::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 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 Glulxe::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 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 Glulxe::func_3_ra__pr(uint argc, uint *argv) { - uint obj; - uint id; - uint prop; + uint obj; + uint id; + uint prop; - obj = ARG_IF_GIVEN(argv, argc, 0); - id = ARG_IF_GIVEN(argv, argc, 1); + obj = ARG_IF_GIVEN(argv, argc, 0); + id = ARG_IF_GIVEN(argv, argc, 1); - prop = get_prop(obj, id); - if (prop == 0) - return 0; + prop = get_prop(obj, id); + if (prop == 0) + return 0; - return Mem4(prop + 4); + return Mem4(prop + 4); } uint Glulxe::func_4_rl__pr(uint argc, uint *argv) { - uint obj; - uint id; - uint prop; + uint obj; + uint id; + uint prop; - obj = ARG_IF_GIVEN(argv, argc, 0); - id = ARG_IF_GIVEN(argv, argc, 1); + obj = ARG_IF_GIVEN(argv, argc, 0); + id = ARG_IF_GIVEN(argv, argc, 1); - prop = get_prop(obj, id); - if (prop == 0) - return 0; + prop = get_prop(obj, id); + if (prop == 0) + return 0; - return 4 * Mem2(prop + 2); + return 4 * Mem2(prop + 2); } uint Glulxe::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 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 Glulxe::func_6_rv__pr(uint argc, uint *argv) { - uint id; - uint addr; + uint id; + uint addr; - id = ARG_IF_GIVEN(argv, argc, 1); + id = ARG_IF_GIVEN(argv, argc, 1); - addr = func_3_ra__pr(argc, argv); + addr = func_3_ra__pr(argc, argv); - if (addr == 0) { - if ((id > 0) && (id < indiv_prop_start)) - return Mem4(cpv__start + (4 * id)); + 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; - } + accel_error("[** Programming error: tried to read (something) **]"); + return 0; + } - return Mem4(addr); + return Mem4(addr); } uint Glulxe::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 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 Glulxe::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 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 Glulxe::func_9_ra__pr(uint argc, uint *argv) { - uint obj; - uint id; - uint prop; + uint obj; + uint id; + uint prop; - obj = ARG_IF_GIVEN(argv, argc, 0); - id = ARG_IF_GIVEN(argv, argc, 1); + 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; + prop = get_prop_new(obj, id); + if (prop == 0) + return 0; - return Mem4(prop + 4); + return Mem4(prop + 4); } uint Glulxe::func_10_rl__pr(uint argc, uint *argv) { - uint obj; - uint id; - uint prop; + uint obj; + uint id; + uint prop; - obj = ARG_IF_GIVEN(argv, argc, 0); - id = ARG_IF_GIVEN(argv, argc, 1); + 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; + prop = get_prop_new(obj, id); + if (prop == 0) + return 0; - return 4 * Mem2(prop + 2); + return 4 * Mem2(prop + 2); } uint Glulxe::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 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 Glulxe::func_12_rv__pr(uint argc, uint *argv) { - uint id; - uint addr; + uint id; + uint addr; - id = ARG_IF_GIVEN(argv, argc, 1); + id = ARG_IF_GIVEN(argv, argc, 1); - addr = func_9_ra__pr(argc, argv); + addr = func_9_ra__pr(argc, argv); - if (addr == 0) { - if ((id > 0) && (id < indiv_prop_start)) - return Mem4(cpv__start + (4 * id)); + 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; - } + accel_error("[** Programming error: tried to read (something) **]"); + return 0; + } - return Mem4(addr); + return Mem4(addr); } uint Glulxe::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); + 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 Glulxe diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp index f314ca43e2..55cd918ebb 100644 --- a/engines/glk/glulxe/exec.cpp +++ b/engines/glk/glulxe/exec.cpp @@ -26,1042 +26,1023 @@ namespace Glk { namespace Glulxe { void Glulxe::execute_loop() { - bool done_executing = false; - int ix; - uint opcode; - const operandlist_t *oplist; - oparg_t inst[MAX_OPERANDS]; - uint value, addr, val0, val1; - int vals0, vals1; - uint *arglist; - uint arglistfix[3]; + bool done_executing = false; + int ix; + uint opcode; + const operandlist_t *oplist; + oparg_t inst[MAX_OPERANDS]; + uint value, addr, val0, val1; + int vals0, vals1; + uint *arglist; + uint arglistfix[3]; #ifdef FLOAT_SUPPORT - gfloat32 valf, valf1, valf2; + gfloat32 valf, valf1, valf2; #endif /* FLOAT_SUPPORT */ - while (!done_executing) { - - profile_tick(); - debugger_tick(); - /* Do OS-specific processing, if appropriate. */ - glk_tick(); - - /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */ - prevpc = pc; - - /* Fetch the opcode number. */ - opcode = Mem1(pc); - pc++; - if (opcode & 0x80) { - /* More than one-byte opcode. */ - if (opcode & 0x40) { - /* Four-byte opcode */ - opcode &= 0x3F; - opcode = (opcode << 8) | Mem1(pc); - pc++; - opcode = (opcode << 8) | Mem1(pc); - pc++; - opcode = (opcode << 8) | Mem1(pc); - pc++; - } - else { - /* Two-byte opcode */ - opcode &= 0x7F; - opcode = (opcode << 8) | Mem1(pc); - pc++; - } - } - - /* Now we have an opcode number. */ - - /* Fetch the structure that describes how the operands for this - opcode are arranged. This is a pointer to an immutable, - static object. */ - if (opcode < 0x80) - oplist = fast_operandlist[opcode]; - else - oplist = lookup_operandlist(opcode); - - if (!oplist) - fatal_error_i("Encountered unknown opcode.", opcode); - - /* Based on the oplist structure, load the actual operand values - into inst. This moves the PC up to the end of the instruction. */ - parse_operands(inst, oplist); - - /* Perform the opcode. This switch statement is split in two, based - on some paranoid suspicions about the ability of compilers to - optimize large-range switches. Ignore that. */ - - if (opcode < 0x80) { - - switch (opcode) { - - case op_nop: - break; - - case op_add: - value = inst[0].value + inst[1].value; - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_sub: - value = inst[0].value - inst[1].value; - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_mul: - value = inst[0].value * inst[1].value; - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_div: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals1 == 0) - fatal_error("Division by zero."); - /* Since C doesn't guarantee the results of division of negative - numbers, we carefully convert everything to positive values - first. They have to be unsigned values, too, otherwise the - 0x80000000 case goes wonky. */ - if (vals0 < 0) { - val0 = (-vals0); - if (vals1 < 0) { - val1 = (-vals1); - value = val0 / val1; - } - else { - val1 = vals1; - value = -(int)(val0 / val1); - } - } - else { - val0 = vals0; - if (vals1 < 0) { - val1 = (-vals1); - value = -(int)(val0 / val1); - } - else { - val1 = vals1; - value = val0 / val1; - } - } - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_mod: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals1 == 0) - fatal_error("Division by zero doing remainder."); - if (vals1 < 0) { - val1 = -vals1; - } - else { - val1 = vals1; - } - if (vals0 < 0) { - val0 = (-vals0); - value = -(int)(val0 % val1); - } - else { - val0 = vals0; - value = val0 % val1; - } - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_neg: - vals0 = inst[0].value; - value = (-vals0); - store_operand(inst[1].desttype, inst[1].value, value); - break; - - case op_bitand: - value = (inst[0].value & inst[1].value); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_bitor: - value = (inst[0].value | inst[1].value); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_bitxor: - value = (inst[0].value ^ inst[1].value); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_bitnot: - value = ~(inst[0].value); - store_operand(inst[1].desttype, inst[1].value, value); - break; - - case op_shiftl: - vals0 = inst[1].value; - if (vals0 < 0 || vals0 >= 32) - value = 0; - else - value = ((uint)(inst[0].value) << (uint)vals0); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_ushiftr: - vals0 = inst[1].value; - if (vals0 < 0 || vals0 >= 32) - value = 0; - else - value = ((uint)(inst[0].value) >> (uint)vals0); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_sshiftr: - vals0 = inst[1].value; - if (vals0 < 0 || vals0 >= 32) { - if (inst[0].value & 0x80000000) - value = 0xFFFFFFFF; - else - value = 0; - } - else { - /* This is somewhat foolhardy -- C doesn't guarantee that - right-shifting a signed value replicates the sign bit. - We'll assume it for now. */ - value = ((int)(inst[0].value) >> (int)vals0); - } - store_operand(inst[2].desttype, inst[2].value, value); - break; - - case op_jump: - value = inst[0].value; - /* fall through to PerformJump label. */ - - PerformJump: /* goto label for successful jumping... ironic, no? */ - if (value == 0 || value == 1) { - /* Return from function. This is exactly what happens in - return_op, but it's only a few lines of code, so I won't - bother with a "goto". */ - leave_function(); - if (stackptr == 0) { - done_executing = true; - break; - } - pop_callstub(value); /* zero or one */ - } - else { - /* Branch to a new PC value. */ - pc = (pc + value - 2); - } - break; - - case op_jz: - if (inst[0].value == 0) { - value = inst[1].value; - goto PerformJump; - } - break; - case op_jnz: - if (inst[0].value != 0) { - value = inst[1].value; - goto PerformJump; - } - break; - case op_jeq: - if (inst[0].value == inst[1].value) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jne: - if (inst[0].value != inst[1].value) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jlt: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals0 < vals1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jgt: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals0 > vals1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jle: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals0 <= vals1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jge: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals0 >= vals1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jltu: - val0 = inst[0].value; - val1 = inst[1].value; - if (val0 < val1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jgtu: - val0 = inst[0].value; - val1 = inst[1].value; - if (val0 > val1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jleu: - val0 = inst[0].value; - val1 = inst[1].value; - if (val0 <= val1) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jgeu: - val0 = inst[0].value; - val1 = inst[1].value; - if (val0 >= val1) { - value = inst[2].value; - goto PerformJump; - } - break; - - case op_call: - value = inst[1].value; - arglist = pop_arguments(value, 0); - push_callstub(inst[2].desttype, inst[2].value); - enter_function(inst[0].value, value, arglist); - break; - case op_return: - leave_function(); - if (stackptr == 0) { - done_executing = true; - break; - } - pop_callstub(inst[0].value); - break; - case op_tailcall: - value = inst[1].value; - arglist = pop_arguments(value, 0); - leave_function(); - enter_function(inst[0].value, value, arglist); - break; - - case op_catch: - push_callstub(inst[0].desttype, inst[0].value); - value = inst[1].value; - val0 = stackptr; - store_operand(inst[0].desttype, inst[0].value, val0); - goto PerformJump; - break; - case op_throw: - profile_fail("throw"); - value = inst[0].value; - stackptr = inst[1].value; - pop_callstub(value); - break; - - case op_copy: - value = inst[0].value; + while (!done_executing) { + + profile_tick(); + debugger_tick(); + /* Do OS-specific processing, if appropriate. */ + glk_tick(); + + /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */ + prevpc = pc; + + /* Fetch the opcode number. */ + opcode = Mem1(pc); + pc++; + if (opcode & 0x80) { + /* More than one-byte opcode. */ + if (opcode & 0x40) { + /* Four-byte opcode */ + opcode &= 0x3F; + opcode = (opcode << 8) | Mem1(pc); + pc++; + opcode = (opcode << 8) | Mem1(pc); + pc++; + opcode = (opcode << 8) | Mem1(pc); + pc++; + } else { + /* Two-byte opcode */ + opcode &= 0x7F; + opcode = (opcode << 8) | Mem1(pc); + pc++; + } + } + + /* Now we have an opcode number. */ + + /* Fetch the structure that describes how the operands for this + opcode are arranged. This is a pointer to an immutable, + static object. */ + if (opcode < 0x80) + oplist = fast_operandlist[opcode]; + else + oplist = lookup_operandlist(opcode); + + if (!oplist) + fatal_error_i("Encountered unknown opcode.", opcode); + + /* Based on the oplist structure, load the actual operand values + into inst. This moves the PC up to the end of the instruction. */ + parse_operands(inst, oplist); + + /* Perform the opcode. This switch statement is split in two, based + on some paranoid suspicions about the ability of compilers to + optimize large-range switches. Ignore that. */ + + if (opcode < 0x80) { + + switch (opcode) { + + case op_nop: + break; + + case op_add: + value = inst[0].value + inst[1].value; + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_sub: + value = inst[0].value - inst[1].value; + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_mul: + value = inst[0].value * inst[1].value; + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_div: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals1 == 0) + fatal_error("Division by zero."); + /* Since C doesn't guarantee the results of division of negative + numbers, we carefully convert everything to positive values + first. They have to be unsigned values, too, otherwise the + 0x80000000 case goes wonky. */ + if (vals0 < 0) { + val0 = (-vals0); + if (vals1 < 0) { + val1 = (-vals1); + value = val0 / val1; + } else { + val1 = vals1; + value = -(int)(val0 / val1); + } + } else { + val0 = vals0; + if (vals1 < 0) { + val1 = (-vals1); + value = -(int)(val0 / val1); + } else { + val1 = vals1; + value = val0 / val1; + } + } + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_mod: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals1 == 0) + fatal_error("Division by zero doing remainder."); + if (vals1 < 0) { + val1 = -vals1; + } else { + val1 = vals1; + } + if (vals0 < 0) { + val0 = (-vals0); + value = -(int)(val0 % val1); + } else { + val0 = vals0; + value = val0 % val1; + } + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_neg: + vals0 = inst[0].value; + value = (-vals0); + store_operand(inst[1].desttype, inst[1].value, value); + break; + + case op_bitand: + value = (inst[0].value & inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_bitor: + value = (inst[0].value | inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_bitxor: + value = (inst[0].value ^ inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_bitnot: + value = ~(inst[0].value); + store_operand(inst[1].desttype, inst[1].value, value); + break; + + case op_shiftl: + vals0 = inst[1].value; + if (vals0 < 0 || vals0 >= 32) + value = 0; + else + value = ((uint)(inst[0].value) << (uint)vals0); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_ushiftr: + vals0 = inst[1].value; + if (vals0 < 0 || vals0 >= 32) + value = 0; + else + value = ((uint)(inst[0].value) >> (uint)vals0); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_sshiftr: + vals0 = inst[1].value; + if (vals0 < 0 || vals0 >= 32) { + if (inst[0].value & 0x80000000) + value = 0xFFFFFFFF; + else + value = 0; + } else { + /* This is somewhat foolhardy -- C doesn't guarantee that + right-shifting a signed value replicates the sign bit. + We'll assume it for now. */ + value = ((int)(inst[0].value) >> (int)vals0); + } + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_jump: + value = inst[0].value; + /* fall through to PerformJump label. */ + +PerformJump: /* goto label for successful jumping... ironic, no? */ + if (value == 0 || value == 1) { + /* Return from function. This is exactly what happens in + return_op, but it's only a few lines of code, so I won't + bother with a "goto". */ + leave_function(); + if (stackptr == 0) { + done_executing = true; + break; + } + pop_callstub(value); /* zero or one */ + } else { + /* Branch to a new PC value. */ + pc = (pc + value - 2); + } + break; + + case op_jz: + if (inst[0].value == 0) { + value = inst[1].value; + goto PerformJump; + } + break; + case op_jnz: + if (inst[0].value != 0) { + value = inst[1].value; + goto PerformJump; + } + break; + case op_jeq: + if (inst[0].value == inst[1].value) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jne: + if (inst[0].value != inst[1].value) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jlt: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals0 < vals1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jgt: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals0 > vals1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jle: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals0 <= vals1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jge: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals0 >= vals1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jltu: + val0 = inst[0].value; + val1 = inst[1].value; + if (val0 < val1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jgtu: + val0 = inst[0].value; + val1 = inst[1].value; + if (val0 > val1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jleu: + val0 = inst[0].value; + val1 = inst[1].value; + if (val0 <= val1) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jgeu: + val0 = inst[0].value; + val1 = inst[1].value; + if (val0 >= val1) { + value = inst[2].value; + goto PerformJump; + } + break; + + case op_call: + value = inst[1].value; + arglist = pop_arguments(value, 0); + push_callstub(inst[2].desttype, inst[2].value); + enter_function(inst[0].value, value, arglist); + break; + case op_return: + leave_function(); + if (stackptr == 0) { + done_executing = true; + break; + } + pop_callstub(inst[0].value); + break; + case op_tailcall: + value = inst[1].value; + arglist = pop_arguments(value, 0); + leave_function(); + enter_function(inst[0].value, value, arglist); + break; + + case op_catch: + push_callstub(inst[0].desttype, inst[0].value); + value = inst[1].value; + val0 = stackptr; + store_operand(inst[0].desttype, inst[0].value, val0); + goto PerformJump; + break; + case op_throw: + profile_fail("throw"); + value = inst[0].value; + stackptr = inst[1].value; + pop_callstub(value); + break; + + case op_copy: + value = inst[0].value; #ifdef TOLERATE_SUPERGLUS_BUG - if (inst[1].desttype == 1 && inst[1].value == 0) - inst[1].desttype = 0; + if (inst[1].desttype == 1 && inst[1].value == 0) + inst[1].desttype = 0; #endif /* TOLERATE_SUPERGLUS_BUG */ - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_copys: - value = inst[0].value; - store_operand_s(inst[1].desttype, inst[1].value, value); - break; - case op_copyb: - value = inst[0].value; - store_operand_b(inst[1].desttype, inst[1].value, value); - break; - - case op_sexs: - val0 = inst[0].value; - if (val0 & 0x8000) - val0 |= 0xFFFF0000; - else - val0 &= 0x0000FFFF; - store_operand(inst[1].desttype, inst[1].value, val0); - break; - case op_sexb: - val0 = inst[0].value; - if (val0 & 0x80) - val0 |= 0xFFFFFF00; - else - val0 &= 0x000000FF; - store_operand(inst[1].desttype, inst[1].value, val0); - break; - - case op_aload: - value = inst[0].value; - value += 4 * inst[1].value; - val0 = Mem4(value); - store_operand(inst[2].desttype, inst[2].value, val0); - break; - case op_aloads: - value = inst[0].value; - value += 2 * inst[1].value; - val0 = Mem2(value); - store_operand(inst[2].desttype, inst[2].value, val0); - break; - case op_aloadb: - value = inst[0].value; - value += inst[1].value; - val0 = Mem1(value); - store_operand(inst[2].desttype, inst[2].value, val0); - break; - case op_aloadbit: - value = inst[0].value; - vals0 = inst[1].value; - val1 = (vals0 & 7); - if (vals0 >= 0) - value += (vals0 >> 3); - else - value -= (1 + ((-1 - vals0) >> 3)); - if (Mem1(value) & (1 << val1)) - val0 = 1; - else - val0 = 0; - store_operand(inst[2].desttype, inst[2].value, val0); - break; - - case op_astore: - value = inst[0].value; - value += 4 * inst[1].value; - val0 = inst[2].value; - MemW4(value, val0); - break; - case op_astores: - value = inst[0].value; - value += 2 * inst[1].value; - val0 = inst[2].value; - MemW2(value, val0); - break; - case op_astoreb: - value = inst[0].value; - value += inst[1].value; - val0 = inst[2].value; - MemW1(value, val0); - break; - case op_astorebit: - value = inst[0].value; - vals0 = inst[1].value; - val1 = (vals0 & 7); - if (vals0 >= 0) - value += (vals0 >> 3); - else - value -= (1 + ((-1 - vals0) >> 3)); - val0 = Mem1(value); - if (inst[2].value) - val0 |= (1 << val1); - else - val0 &= ~((uint)(1 << val1)); - MemW1(value, val0); - break; - - case op_stkcount: - value = (stackptr - valstackbase) / 4; - store_operand(inst[0].desttype, inst[0].value, value); - break; - case op_stkpeek: - vals0 = inst[0].value * 4; - if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase)) - fatal_error("Stkpeek outside current stack range."); - value = Stk4(stackptr - (vals0+4)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_stkswap: - if (stackptr < valstackbase+8) { - fatal_error("Stack underflow in stkswap."); - } - val0 = Stk4(stackptr-4); - val1 = Stk4(stackptr-8); - StkW4(stackptr-4, val1); - StkW4(stackptr-8, val0); - break; - case op_stkcopy: - vals0 = inst[0].value; - if (vals0 < 0) - fatal_error("Negative operand in stkcopy."); - if (vals0 == 0) - break; - if (stackptr < valstackbase+vals0*4) - fatal_error("Stack underflow in stkcopy."); - if (stackptr + vals0*4 > stacksize) - fatal_error("Stack overflow in stkcopy."); - addr = stackptr - vals0*4; - for (ix=0; ix<vals0; ix++) { - value = Stk4(addr + ix*4); - StkW4(stackptr + ix*4, value); - } - stackptr += vals0*4; - break; - case op_stkroll: - vals0 = inst[0].value; - vals1 = inst[1].value; - if (vals0 < 0) - fatal_error("Negative operand in stkroll."); - if (stackptr < valstackbase+vals0*4) - fatal_error("Stack underflow in stkroll."); - if (vals0 == 0) - break; - /* The following is a bit ugly. We want to do vals1 = vals0-vals1, - because rolling down is sort of easier than rolling up. But - we also want to take the result mod vals0. The % operator is - annoying for negative numbers, so we need to do this in two - cases. */ - if (vals1 > 0) { - vals1 = vals1 % vals0; - vals1 = (vals0) - vals1; - } - else { - vals1 = (-vals1) % vals0; - } - if (vals1 == 0) - break; - addr = stackptr - vals0*4; - for (ix=0; ix<vals1; ix++) { - value = Stk4(addr + ix*4); - StkW4(stackptr + ix*4, value); - } - for (ix=0; ix<vals0; ix++) { - value = Stk4(addr + (vals1+ix)*4); - StkW4(addr + ix*4, value); - } - break; - - case op_streamchar: - profile_in(0xE0000001, stackptr, false); - value = inst[0].value & 0xFF; - (this->*stream_char_handler)(value); - profile_out(stackptr); - break; - case op_streamunichar: - profile_in(0xE0000002, stackptr, false); - value = inst[0].value; - (this->*stream_unichar_handler)(value); - profile_out(stackptr); - break; - case op_streamnum: - profile_in(0xE0000003, stackptr, false); - vals0 = inst[0].value; - stream_num(vals0, false, 0); - profile_out(stackptr); - break; - case op_streamstr: - profile_in(0xE0000004, stackptr, false); - stream_string(inst[0].value, 0, 0); - profile_out(stackptr); - break; - - default: - fatal_error_i("Executed unknown opcode.", opcode); - } - } - else { - - switch (opcode) { - - case op_gestalt: - value = do_gestalt(inst[0].value, inst[1].value); - store_operand(inst[2].desttype, inst[2].value, value); - break; - - case op_debugtrap: + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_copys: + value = inst[0].value; + store_operand_s(inst[1].desttype, inst[1].value, value); + break; + case op_copyb: + value = inst[0].value; + store_operand_b(inst[1].desttype, inst[1].value, value); + break; + + case op_sexs: + val0 = inst[0].value; + if (val0 & 0x8000) + val0 |= 0xFFFF0000; + else + val0 &= 0x0000FFFF; + store_operand(inst[1].desttype, inst[1].value, val0); + break; + case op_sexb: + val0 = inst[0].value; + if (val0 & 0x80) + val0 |= 0xFFFFFF00; + else + val0 &= 0x000000FF; + store_operand(inst[1].desttype, inst[1].value, val0); + break; + + case op_aload: + value = inst[0].value; + value += 4 * inst[1].value; + val0 = Mem4(value); + store_operand(inst[2].desttype, inst[2].value, val0); + break; + case op_aloads: + value = inst[0].value; + value += 2 * inst[1].value; + val0 = Mem2(value); + store_operand(inst[2].desttype, inst[2].value, val0); + break; + case op_aloadb: + value = inst[0].value; + value += inst[1].value; + val0 = Mem1(value); + store_operand(inst[2].desttype, inst[2].value, val0); + break; + case op_aloadbit: + value = inst[0].value; + vals0 = inst[1].value; + val1 = (vals0 & 7); + if (vals0 >= 0) + value += (vals0 >> 3); + else + value -= (1 + ((-1 - vals0) >> 3)); + if (Mem1(value) & (1 << val1)) + val0 = 1; + else + val0 = 0; + store_operand(inst[2].desttype, inst[2].value, val0); + break; + + case op_astore: + value = inst[0].value; + value += 4 * inst[1].value; + val0 = inst[2].value; + MemW4(value, val0); + break; + case op_astores: + value = inst[0].value; + value += 2 * inst[1].value; + val0 = inst[2].value; + MemW2(value, val0); + break; + case op_astoreb: + value = inst[0].value; + value += inst[1].value; + val0 = inst[2].value; + MemW1(value, val0); + break; + case op_astorebit: + value = inst[0].value; + vals0 = inst[1].value; + val1 = (vals0 & 7); + if (vals0 >= 0) + value += (vals0 >> 3); + else + value -= (1 + ((-1 - vals0) >> 3)); + val0 = Mem1(value); + if (inst[2].value) + val0 |= (1 << val1); + else + val0 &= ~((uint)(1 << val1)); + MemW1(value, val0); + break; + + case op_stkcount: + value = (stackptr - valstackbase) / 4; + store_operand(inst[0].desttype, inst[0].value, value); + break; + case op_stkpeek: + vals0 = inst[0].value * 4; + if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase)) + fatal_error("Stkpeek outside current stack range."); + value = Stk4(stackptr - (vals0 + 4)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_stkswap: + if (stackptr < valstackbase + 8) { + fatal_error("Stack underflow in stkswap."); + } + val0 = Stk4(stackptr - 4); + val1 = Stk4(stackptr - 8); + StkW4(stackptr - 4, val1); + StkW4(stackptr - 8, val0); + break; + case op_stkcopy: + vals0 = inst[0].value; + if (vals0 < 0) + fatal_error("Negative operand in stkcopy."); + if (vals0 == 0) + break; + if (stackptr < valstackbase + vals0 * 4) + fatal_error("Stack underflow in stkcopy."); + if (stackptr + vals0 * 4 > stacksize) + fatal_error("Stack overflow in stkcopy."); + addr = stackptr - vals0 * 4; + for (ix = 0; ix < vals0; ix++) { + value = Stk4(addr + ix * 4); + StkW4(stackptr + ix * 4, value); + } + stackptr += vals0 * 4; + break; + case op_stkroll: + vals0 = inst[0].value; + vals1 = inst[1].value; + if (vals0 < 0) + fatal_error("Negative operand in stkroll."); + if (stackptr < valstackbase + vals0 * 4) + fatal_error("Stack underflow in stkroll."); + if (vals0 == 0) + break; + /* The following is a bit ugly. We want to do vals1 = vals0-vals1, + because rolling down is sort of easier than rolling up. But + we also want to take the result mod vals0. The % operator is + annoying for negative numbers, so we need to do this in two + cases. */ + if (vals1 > 0) { + vals1 = vals1 % vals0; + vals1 = (vals0) - vals1; + } else { + vals1 = (-vals1) % vals0; + } + if (vals1 == 0) + break; + addr = stackptr - vals0 * 4; + for (ix = 0; ix < vals1; ix++) { + value = Stk4(addr + ix * 4); + StkW4(stackptr + ix * 4, value); + } + for (ix = 0; ix < vals0; ix++) { + value = Stk4(addr + (vals1 + ix) * 4); + StkW4(addr + ix * 4, value); + } + break; + + case op_streamchar: + profile_in(0xE0000001, stackptr, false); + value = inst[0].value & 0xFF; + (this->*stream_char_handler)(value); + profile_out(stackptr); + break; + case op_streamunichar: + profile_in(0xE0000002, stackptr, false); + value = inst[0].value; + (this->*stream_unichar_handler)(value); + profile_out(stackptr); + break; + case op_streamnum: + profile_in(0xE0000003, stackptr, false); + vals0 = inst[0].value; + stream_num(vals0, false, 0); + profile_out(stackptr); + break; + case op_streamstr: + profile_in(0xE0000004, stackptr, false); + stream_string(inst[0].value, 0, 0); + profile_out(stackptr); + break; + + default: + fatal_error_i("Executed unknown opcode.", opcode); + } + } else { + + switch (opcode) { + + case op_gestalt: + value = do_gestalt(inst[0].value, inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_debugtrap: #if VM_DEBUGGER - /* We block and handle debug commands, but only if the - library has invoked debug features. (Meaning, has - the cycle handler ever been called.) */ - if (debugger_ever_invoked()) { - debugger_block_and_debug("user debugtrap, pausing..."); - break; - } + /* We block and handle debug commands, but only if the + library has invoked debug features. (Meaning, has + the cycle handler ever been called.) */ + if (debugger_ever_invoked()) { + debugger_block_and_debug("user debugtrap, pausing..."); + break; + } #endif /* VM_DEBUGGER */ - fatal_error_i("user debugtrap encountered.", inst[0].value); - - case op_jumpabs: - pc = inst[0].value; - break; - - case op_callf: - push_callstub(inst[1].desttype, inst[1].value); - enter_function(inst[0].value, 0, arglistfix); - break; - case op_callfi: - arglistfix[0] = inst[1].value; - push_callstub(inst[2].desttype, inst[2].value); - enter_function(inst[0].value, 1, arglistfix); - break; - case op_callfii: - arglistfix[0] = inst[1].value; - arglistfix[1] = inst[2].value; - push_callstub(inst[3].desttype, inst[3].value); - enter_function(inst[0].value, 2, arglistfix); - break; - case op_callfiii: - arglistfix[0] = inst[1].value; - arglistfix[1] = inst[2].value; - arglistfix[2] = inst[3].value; - push_callstub(inst[4].desttype, inst[4].value); - enter_function(inst[0].value, 3, arglistfix); - break; - - case op_getmemsize: - store_operand(inst[0].desttype, inst[0].value, endmem); - break; - case op_setmemsize: - value = change_memsize(inst[0].value, false); - store_operand(inst[1].desttype, inst[1].value, value); - break; - - case op_getstringtbl: - value = stream_get_table(); - store_operand(inst[0].desttype, inst[0].value, value); - break; - case op_setstringtbl: - stream_set_table(inst[0].value); - break; - - case op_getiosys: - stream_get_iosys(&val0, &val1); - store_operand(inst[0].desttype, inst[0].value, val0); - store_operand(inst[1].desttype, inst[1].value, val1); - break; - case op_setiosys: - stream_set_iosys(inst[0].value, inst[1].value); - break; - - case op_glk: - profile_in(0xF0000000+inst[0].value, stackptr, false); - value = inst[1].value; - arglist = pop_arguments(value, 0); - val0 = perform_glk(inst[0].value, value, arglist); + fatal_error_i("user debugtrap encountered.", inst[0].value); + + case op_jumpabs: + pc = inst[0].value; + break; + + case op_callf: + push_callstub(inst[1].desttype, inst[1].value); + enter_function(inst[0].value, 0, arglistfix); + break; + case op_callfi: + arglistfix[0] = inst[1].value; + push_callstub(inst[2].desttype, inst[2].value); + enter_function(inst[0].value, 1, arglistfix); + break; + case op_callfii: + arglistfix[0] = inst[1].value; + arglistfix[1] = inst[2].value; + push_callstub(inst[3].desttype, inst[3].value); + enter_function(inst[0].value, 2, arglistfix); + break; + case op_callfiii: + arglistfix[0] = inst[1].value; + arglistfix[1] = inst[2].value; + arglistfix[2] = inst[3].value; + push_callstub(inst[4].desttype, inst[4].value); + enter_function(inst[0].value, 3, arglistfix); + break; + + case op_getmemsize: + store_operand(inst[0].desttype, inst[0].value, endmem); + break; + case op_setmemsize: + value = change_memsize(inst[0].value, false); + store_operand(inst[1].desttype, inst[1].value, value); + break; + + case op_getstringtbl: + value = stream_get_table(); + store_operand(inst[0].desttype, inst[0].value, value); + break; + case op_setstringtbl: + stream_set_table(inst[0].value); + break; + + case op_getiosys: + stream_get_iosys(&val0, &val1); + store_operand(inst[0].desttype, inst[0].value, val0); + store_operand(inst[1].desttype, inst[1].value, val1); + break; + case op_setiosys: + stream_set_iosys(inst[0].value, inst[1].value); + break; + + case op_glk: + profile_in(0xF0000000 + inst[0].value, stackptr, false); + value = inst[1].value; + arglist = pop_arguments(value, 0); + val0 = perform_glk(inst[0].value, value, arglist); #ifdef TOLERATE_SUPERGLUS_BUG - if (inst[2].desttype == 1 && inst[2].value == 0) - inst[2].desttype = 0; + if (inst[2].desttype == 1 && inst[2].value == 0) + inst[2].desttype = 0; #endif /* TOLERATE_SUPERGLUS_BUG */ - store_operand(inst[2].desttype, inst[2].value, val0); - profile_out(stackptr); - break; - - case op_random: - vals0 = inst[0].value; - if (vals0 == 0) - value = glulx_random(); - else if (vals0 >= 1) - value = glulx_random() % (uint)(vals0); - else - value = -(int)(glulx_random() % (uint)(-vals0)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_setrandom: - glulx_setrandom(inst[0].value); - break; - - case op_verify: - value = perform_verify(); - store_operand(inst[0].desttype, inst[0].value, value); - break; - - case op_restart: - profile_fail("restart"); - vm_restart(); - break; - - case op_protect: - val0 = inst[0].value; - val1 = val0 + inst[1].value; - if (val0 == val1) { - val0 = 0; - val1 = 0; - } - protectstart = val0; - protectend = val1; - break; - - case op_save: - push_callstub(inst[1].desttype, inst[1].value); - value = perform_save(find_stream_by_id(inst[0].value)); - pop_callstub(value); - break; - - case op_restore: - value = perform_restore(find_stream_by_id(inst[0].value), false); - if (value == 0) { - /* We've succeeded, and the stack now contains the callstub - saved during saveundo. Ignore this opcode's operand. */ - value = (uint)-1; - pop_callstub(value); - } - else { - /* We've failed, so we must store the failure in this opcode's - operand. */ - store_operand(inst[1].desttype, inst[1].value, value); - } - break; - - case op_saveundo: - push_callstub(inst[0].desttype, inst[0].value); - value = perform_saveundo(); - pop_callstub(value); - break; - - case op_restoreundo: - value = perform_restoreundo(); - if (value == 0) { - /* We've succeeded, and the stack now contains the callstub - saved during saveundo. Ignore this opcode's operand. */ - value = (uint)-1; - pop_callstub(value); - } - else { - /* We've failed, so we must store the failure in this opcode's - operand. */ - store_operand(inst[0].desttype, inst[0].value, value); - } - break; - - case op_quit: - done_executing = true; - break; - - case op_linearsearch: - value = linear_search(inst[0].value, inst[1].value, inst[2].value, - inst[3].value, inst[4].value, inst[5].value, inst[6].value); - store_operand(inst[7].desttype, inst[7].value, value); - break; - case op_binarysearch: - value = binary_search(inst[0].value, inst[1].value, inst[2].value, - inst[3].value, inst[4].value, inst[5].value, inst[6].value); - store_operand(inst[7].desttype, inst[7].value, value); - break; - case op_linkedsearch: - value = linked_search(inst[0].value, inst[1].value, inst[2].value, - inst[3].value, inst[4].value, inst[5].value); - store_operand(inst[6].desttype, inst[6].value, value); - break; - - case op_mzero: { - uint lx; - uint count = inst[0].value; - addr = inst[1].value; - for (lx=0; lx<count; lx++, addr++) { - MemW1(addr, 0); - } - } - break; - case op_mcopy: { - uint lx; - uint count = inst[0].value; - uint addrsrc = inst[1].value; - uint addrdest = inst[2].value; - if (addrdest < addrsrc) { - for (lx=0; lx<count; lx++, addrsrc++, addrdest++) { - value = Mem1(addrsrc); - MemW1(addrdest, value); - } - } - else { - addrsrc += (count-1); - addrdest += (count-1); - for (lx=0; lx<count; lx++, addrsrc--, addrdest--) { - value = Mem1(addrsrc); - MemW1(addrdest, value); - } - } - } - break; - case op_malloc: - value = heap_alloc(inst[0].value); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_mfree: - heap_free(inst[0].value); - break; - - case op_accelfunc: - accel_set_func(inst[0].value, inst[1].value); - break; - case op_accelparam: - accel_set_param(inst[0].value, inst[1].value); - break; + store_operand(inst[2].desttype, inst[2].value, val0); + profile_out(stackptr); + break; + + case op_random: + vals0 = inst[0].value; + if (vals0 == 0) + value = glulx_random(); + else if (vals0 >= 1) + value = glulx_random() % (uint)(vals0); + else + value = -(int)(glulx_random() % (uint)(-vals0)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_setrandom: + glulx_setrandom(inst[0].value); + break; + + case op_verify: + value = perform_verify(); + store_operand(inst[0].desttype, inst[0].value, value); + break; + + case op_restart: + profile_fail("restart"); + vm_restart(); + break; + + case op_protect: + val0 = inst[0].value; + val1 = val0 + inst[1].value; + if (val0 == val1) { + val0 = 0; + val1 = 0; + } + protectstart = val0; + protectend = val1; + break; + + case op_save: + push_callstub(inst[1].desttype, inst[1].value); + value = perform_save(find_stream_by_id(inst[0].value)); + pop_callstub(value); + break; + + case op_restore: + value = perform_restore(find_stream_by_id(inst[0].value), false); + if (value == 0) { + /* We've succeeded, and the stack now contains the callstub + saved during saveundo. Ignore this opcode's operand. */ + value = (uint) - 1; + pop_callstub(value); + } else { + /* We've failed, so we must store the failure in this opcode's + operand. */ + store_operand(inst[1].desttype, inst[1].value, value); + } + break; + + case op_saveundo: + push_callstub(inst[0].desttype, inst[0].value); + value = perform_saveundo(); + pop_callstub(value); + break; + + case op_restoreundo: + value = perform_restoreundo(); + if (value == 0) { + /* We've succeeded, and the stack now contains the callstub + saved during saveundo. Ignore this opcode's operand. */ + value = (uint) - 1; + pop_callstub(value); + } else { + /* We've failed, so we must store the failure in this opcode's + operand. */ + store_operand(inst[0].desttype, inst[0].value, value); + } + break; + + case op_quit: + done_executing = true; + break; + + case op_linearsearch: + value = linear_search(inst[0].value, inst[1].value, inst[2].value, + inst[3].value, inst[4].value, inst[5].value, inst[6].value); + store_operand(inst[7].desttype, inst[7].value, value); + break; + case op_binarysearch: + value = binary_search(inst[0].value, inst[1].value, inst[2].value, + inst[3].value, inst[4].value, inst[5].value, inst[6].value); + store_operand(inst[7].desttype, inst[7].value, value); + break; + case op_linkedsearch: + value = linked_search(inst[0].value, inst[1].value, inst[2].value, + inst[3].value, inst[4].value, inst[5].value); + store_operand(inst[6].desttype, inst[6].value, value); + break; + + case op_mzero: { + uint lx; + uint count = inst[0].value; + addr = inst[1].value; + for (lx = 0; lx < count; lx++, addr++) { + MemW1(addr, 0); + } + } + break; + case op_mcopy: { + uint lx; + uint count = inst[0].value; + uint addrsrc = inst[1].value; + uint addrdest = inst[2].value; + if (addrdest < addrsrc) { + for (lx = 0; lx < count; lx++, addrsrc++, addrdest++) { + value = Mem1(addrsrc); + MemW1(addrdest, value); + } + } else { + addrsrc += (count - 1); + addrdest += (count - 1); + for (lx = 0; lx < count; lx++, addrsrc--, addrdest--) { + value = Mem1(addrsrc); + MemW1(addrdest, value); + } + } + } + break; + case op_malloc: + value = heap_alloc(inst[0].value); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_mfree: + heap_free(inst[0].value); + break; + + case op_accelfunc: + accel_set_func(inst[0].value, inst[1].value); + break; + case op_accelparam: + accel_set_param(inst[0].value, inst[1].value); + break; #ifdef FLOAT_SUPPORT - case op_numtof: - vals0 = inst[0].value; - value = encode_float((gfloat32)vals0); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_ftonumz: - valf = decode_float(inst[0].value); - if (!signbit(valf)) { - if (isnan(valf) || isinf(valf) || (valf > 2147483647.0)) - vals0 = 0x7FFFFFFF; - else - vals0 = (int)(truncf(valf)); - } - else { - if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) - vals0 = 0x80000000; - else - vals0 = (int)(truncf(valf)); - } - store_operand(inst[1].desttype, inst[1].value, vals0); - break; - case op_ftonumn: - valf = decode_float(inst[0].value); - if (!signbit(valf)) { - if (isnan(valf) || isinf(valf) || (valf > 2147483647.0)) - vals0 = 0x7FFFFFFF; - else - vals0 = (int)(roundf(valf)); - } - else { - if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) - vals0 = 0x80000000; - else - vals0 = (int)(roundf(valf)); - } - store_operand(inst[1].desttype, inst[1].value, vals0); - break; - - case op_fadd: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - value = encode_float(valf1 + valf2); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_fsub: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - value = encode_float(valf1 - valf2); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_fmul: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - value = encode_float(valf1 * valf2); - store_operand(inst[2].desttype, inst[2].value, value); - break; - case op_fdiv: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - value = encode_float(valf1 / valf2); - store_operand(inst[2].desttype, inst[2].value, value); - break; - - case op_fmod: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - valf = fmodf(valf1, valf2); - val0 = encode_float(valf); - val1 = encode_float((valf1-valf) / valf2); - if (val1 == 0x0 || val1 == 0x80000000) { - /* When the quotient is zero, the sign has been lost in the - shuffle. We'll set that by hand, based on the original - arguments. */ - val1 = (inst[0].value ^ inst[1].value) & 0x80000000; - } - store_operand(inst[2].desttype, inst[2].value, val0); - store_operand(inst[3].desttype, inst[3].value, val1); - break; - - case op_floor: - valf = decode_float(inst[0].value); - value = encode_float(floorf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_ceil: - valf = decode_float(inst[0].value); - value = encode_float(ceilf(valf)); - if (value == 0x0 || value == 0x80000000) { - /* When the result is zero, the sign may have been lost in the - shuffle. (This is a bug in some C libraries.) We'll set the - sign by hand, based on the original argument. */ - value = inst[0].value & 0x80000000; - } - store_operand(inst[1].desttype, inst[1].value, value); - break; - - case op_sqrt: - valf = decode_float(inst[0].value); - value = encode_float(sqrtf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_log: - valf = decode_float(inst[0].value); - value = encode_float(logf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_exp: - valf = decode_float(inst[0].value); - value = encode_float(expf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_pow: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - value = encode_float(glulx_powf(valf1, valf2)); - store_operand(inst[2].desttype, inst[2].value, value); - break; - - case op_sin: - valf = decode_float(inst[0].value); - value = encode_float(sinf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_cos: - valf = decode_float(inst[0].value); - value = encode_float(cosf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_tan: - valf = decode_float(inst[0].value); - value = encode_float(tanf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_asin: - valf = decode_float(inst[0].value); - value = encode_float(asinf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_acos: - valf = decode_float(inst[0].value); - value = encode_float(acosf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_atan: - valf = decode_float(inst[0].value); - value = encode_float(atanf(valf)); - store_operand(inst[1].desttype, inst[1].value, value); - break; - case op_atan2: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - value = encode_float(atan2f(valf1, valf2)); - store_operand(inst[2].desttype, inst[2].value, value); - break; - - case op_jisinf: - /* Infinity is well-defined, so we don't bother to convert to - float. */ - val0 = inst[0].value; - if (val0 == 0x7F800000 || val0 == 0xFF800000) { - value = inst[1].value; - goto PerformJump; - } - break; - case op_jisnan: - /* NaN is well-defined, so we don't bother to convert to - float. */ - val0 = inst[0].value; - if ((val0 & 0x7F800000) == 0x7F800000 && (val0 & 0x007FFFFF) != 0) { - value = inst[1].value; - goto PerformJump; - } - break; - - case op_jfeq: - if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) { - /* The delta is NaN, which can never match. */ - val0 = 0; - } - else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000) - && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) { - /* Both are infinite. Opposite infinities are never equal, - even if the difference is infinite, so this is easy. */ - val0 = (inst[0].value == inst[1].value); - } - else { - valf1 = decode_float(inst[1].value) - decode_float(inst[0].value); - valf2 = fabs(decode_float(inst[2].value)); - val0 = (valf1 <= valf2 && valf1 >= -valf2); - } - if (val0) { - value = inst[3].value; - goto PerformJump; - } - break; - case op_jfne: - if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) { - /* The delta is NaN, which can never match. */ - val0 = 0; - } - else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000) - && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) { - /* Both are infinite. Opposite infinities are never equal, - even if the difference is infinite, so this is easy. */ - val0 = (inst[0].value == inst[1].value); - } - else { - valf1 = decode_float(inst[1].value) - decode_float(inst[0].value); - valf2 = fabs(decode_float(inst[2].value)); - val0 = (valf1 <= valf2 && valf1 >= -valf2); - } - if (!val0) { - value = inst[3].value; - goto PerformJump; - } - break; - - case op_jflt: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - if (valf1 < valf2) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jfgt: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - if (valf1 > valf2) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jfle: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - if (valf1 <= valf2) { - value = inst[2].value; - goto PerformJump; - } - break; - case op_jfge: - valf1 = decode_float(inst[0].value); - valf2 = decode_float(inst[1].value); - if (valf1 >= valf2) { - value = inst[2].value; - goto PerformJump; - } - break; + case op_numtof: + vals0 = inst[0].value; + value = encode_float((gfloat32)vals0); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_ftonumz: + valf = decode_float(inst[0].value); + if (!signbit(valf)) { + if (isnan(valf) || isinf(valf) || (valf > 2147483647.0)) + vals0 = 0x7FFFFFFF; + else + vals0 = (int)(truncf(valf)); + } else { + if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) + vals0 = 0x80000000; + else + vals0 = (int)(truncf(valf)); + } + store_operand(inst[1].desttype, inst[1].value, vals0); + break; + case op_ftonumn: + valf = decode_float(inst[0].value); + if (!signbit(valf)) { + if (isnan(valf) || isinf(valf) || (valf > 2147483647.0)) + vals0 = 0x7FFFFFFF; + else + vals0 = (int)(roundf(valf)); + } else { + if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) + vals0 = 0x80000000; + else + vals0 = (int)(roundf(valf)); + } + store_operand(inst[1].desttype, inst[1].value, vals0); + break; + + case op_fadd: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 + valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_fsub: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 - valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_fmul: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 * valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_fdiv: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 / valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_fmod: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + valf = fmodf(valf1, valf2); + val0 = encode_float(valf); + val1 = encode_float((valf1 - valf) / valf2); + if (val1 == 0x0 || val1 == 0x80000000) { + /* When the quotient is zero, the sign has been lost in the + shuffle. We'll set that by hand, based on the original + arguments. */ + val1 = (inst[0].value ^ inst[1].value) & 0x80000000; + } + store_operand(inst[2].desttype, inst[2].value, val0); + store_operand(inst[3].desttype, inst[3].value, val1); + break; + + case op_floor: + valf = decode_float(inst[0].value); + value = encode_float(floorf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_ceil: + valf = decode_float(inst[0].value); + value = encode_float(ceilf(valf)); + if (value == 0x0 || value == 0x80000000) { + /* When the result is zero, the sign may have been lost in the + shuffle. (This is a bug in some C libraries.) We'll set the + sign by hand, based on the original argument. */ + value = inst[0].value & 0x80000000; + } + store_operand(inst[1].desttype, inst[1].value, value); + break; + + case op_sqrt: + valf = decode_float(inst[0].value); + value = encode_float(sqrtf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_log: + valf = decode_float(inst[0].value); + value = encode_float(logf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_exp: + valf = decode_float(inst[0].value); + value = encode_float(expf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_pow: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(glulx_powf(valf1, valf2)); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_sin: + valf = decode_float(inst[0].value); + value = encode_float(sinf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_cos: + valf = decode_float(inst[0].value); + value = encode_float(cosf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_tan: + valf = decode_float(inst[0].value); + value = encode_float(tanf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_asin: + valf = decode_float(inst[0].value); + value = encode_float(asinf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_acos: + valf = decode_float(inst[0].value); + value = encode_float(acosf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_atan: + valf = decode_float(inst[0].value); + value = encode_float(atanf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_atan2: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(atan2f(valf1, valf2)); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_jisinf: + /* Infinity is well-defined, so we don't bother to convert to + float. */ + val0 = inst[0].value; + if (val0 == 0x7F800000 || val0 == 0xFF800000) { + value = inst[1].value; + goto PerformJump; + } + break; + case op_jisnan: + /* NaN is well-defined, so we don't bother to convert to + float. */ + val0 = inst[0].value; + if ((val0 & 0x7F800000) == 0x7F800000 && (val0 & 0x007FFFFF) != 0) { + value = inst[1].value; + goto PerformJump; + } + break; + + case op_jfeq: + if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) { + /* The delta is NaN, which can never match. */ + val0 = 0; + } else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000) + && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) { + /* Both are infinite. Opposite infinities are never equal, + even if the difference is infinite, so this is easy. */ + val0 = (inst[0].value == inst[1].value); + } else { + valf1 = decode_float(inst[1].value) - decode_float(inst[0].value); + valf2 = fabs(decode_float(inst[2].value)); + val0 = (valf1 <= valf2 && valf1 >= -valf2); + } + if (val0) { + value = inst[3].value; + goto PerformJump; + } + break; + case op_jfne: + if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) { + /* The delta is NaN, which can never match. */ + val0 = 0; + } else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000) + && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) { + /* Both are infinite. Opposite infinities are never equal, + even if the difference is infinite, so this is easy. */ + val0 = (inst[0].value == inst[1].value); + } else { + valf1 = decode_float(inst[1].value) - decode_float(inst[0].value); + valf2 = fabs(decode_float(inst[2].value)); + val0 = (valf1 <= valf2 && valf1 >= -valf2); + } + if (!val0) { + value = inst[3].value; + goto PerformJump; + } + break; + + case op_jflt: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 < valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jfgt: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 > valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jfle: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 <= valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jfge: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 >= valf2) { + value = inst[2].value; + goto PerformJump; + } + break; #endif /* FLOAT_SUPPORT */ #ifdef GLULX_EXTEND_OPCODES - GLULX_EXTEND_OPCODES + GLULX_EXTEND_OPCODES #endif /* GLULX_EXTEND_OPCODES */ - default: - fatal_error_i("Executed unknown opcode.", opcode); - } - } - } - /* done executing */ + default: + fatal_error_i("Executed unknown opcode.", opcode); + } + } + } + /* done executing */ #if VM_DEBUGGER - debugger_handle_quit(); + debugger_handle_quit(); #endif /* VM_DEBUGGER */ } diff --git a/engines/glk/glulxe/float.cpp b/engines/glk/glulxe/float.cpp index 36ef2f479b..c978ef9c7f 100644 --- a/engines/glk/glulxe/float.cpp +++ b/engines/glk/glulxe/float.cpp @@ -26,104 +26,97 @@ namespace Glk { namespace Glulxe { uint Glulxe::encode_float(gfloat32 val) { - gfloat32 absval; - uint sign; - int expo; - gfloat32 mant; - uint fbits; - - if (signbit(val)) { - sign = 0x80000000; - absval = -val; - } - else { - sign = 0x0; - absval = val; - } - - if (isinf(val)) { - return sign | 0x7f800000; /* infinity */ - } - - if (isnan(val)) { - return sign | 0x7fc00000; - } - - mant = frexpf(absval, &expo); - - /* Normalize mantissa to be in the range [1.0, 2.0) */ - if (0.5 <= mant && mant < 1.0) { - mant *= 2.0; - expo--; - } - else if (mant == 0.0) { - expo = 0; - } - else { - return sign | 0x7f800000; /* infinity */ - } - - if (expo >= 128) { - return sign | 0x7f800000; /* infinity */ - } - else if (expo < -126) { - /* Denormalized (very small) number */ - mant = ldexpf(mant, 126 + expo); - expo = 0; - } - else if (!(expo == 0 && mant == 0.0)) { - expo += 127; - mant -= 1.0; /* Get rid of leading 1 */ - } - - mant *= 8388608.0; /* 2^23 */ - fbits = (uint)(mant + 0.5); /* round mant to nearest int */ - if (fbits >> 23) { - /* The carry propagated out of a string of 23 1 bits. */ - fbits = 0; - expo++; - if (expo >= 255) { - return sign | 0x7f800000; /* infinity */ - } - } - - return (sign) | ((uint)(expo << 23)) | (fbits); + gfloat32 absval; + uint sign; + int expo; + gfloat32 mant; + uint fbits; + + if (signbit(val)) { + sign = 0x80000000; + absval = -val; + } else { + sign = 0x0; + absval = val; + } + + if (isinf(val)) { + return sign | 0x7f800000; /* infinity */ + } + + if (isnan(val)) { + return sign | 0x7fc00000; + } + + mant = frexpf(absval, &expo); + + /* Normalize mantissa to be in the range [1.0, 2.0) */ + if (0.5 <= mant && mant < 1.0) { + mant *= 2.0; + expo--; + } else if (mant == 0.0) { + expo = 0; + } else { + return sign | 0x7f800000; /* infinity */ + } + + if (expo >= 128) { + return sign | 0x7f800000; /* infinity */ + } else if (expo < -126) { + /* Denormalized (very small) number */ + mant = ldexpf(mant, 126 + expo); + expo = 0; + } else if (!(expo == 0 && mant == 0.0)) { + expo += 127; + mant -= 1.0; /* Get rid of leading 1 */ + } + + mant *= 8388608.0; /* 2^23 */ + fbits = (uint)(mant + 0.5); /* round mant to nearest int */ + if (fbits >> 23) { + /* The carry propagated out of a string of 23 1 bits. */ + fbits = 0; + expo++; + if (expo >= 255) { + return sign | 0x7f800000; /* infinity */ + } + } + + return (sign) | ((uint)(expo << 23)) | (fbits); } gfloat32 Glulxe::decode_float(uint val) { - int sign; - int expo; - uint mant; - gfloat32 res; - - /* First byte */ - sign = ((val & 0x80000000) != 0); - expo = (val >> 23) & 0xFF; - mant = val & 0x7FFFFF; - - if (expo == 255) { - if (mant == 0) { - /* Infinity */ - return (sign ? (-INFINITY) : (INFINITY)); - } - else { - /* Not a number */ - return (sign ? (-NAN) : (NAN)); - } - } - - res = (gfloat32)mant / 8388608.0; - - if (expo == 0) { - expo = -126; - } - else { - res += 1.0; - expo -= 127; - } - res = ldexpf(res, expo); - - return (sign ? (-res) : (res)); + int sign; + int expo; + uint mant; + gfloat32 res; + + /* First byte */ + sign = ((val & 0x80000000) != 0); + expo = (val >> 23) & 0xFF; + mant = val & 0x7FFFFF; + + if (expo == 255) { + if (mant == 0) { + /* Infinity */ + return (sign ? (-INFINITY) : (INFINITY)); + } else { + /* Not a number */ + return (sign ? (-NAN) : (NAN)); + } + } + + res = (gfloat32)mant / 8388608.0; + + if (expo == 0) { + expo = -126; + } else { + res += 1.0; + expo -= 127; + } + res = ldexpf(res, expo); + + return (sign ? (-res) : (res)); } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/funcs.cpp b/engines/glk/glulxe/funcs.cpp index b85844a8c1..4af52630a3 100644 --- a/engines/glk/glulxe/funcs.cpp +++ b/engines/glk/glulxe/funcs.cpp @@ -26,279 +26,273 @@ namespace Glk { namespace Glulxe { void Glulxe::enter_function(uint funcaddr, uint argc, uint *argv) { - uint ix, jx; - acceleration_func accelFunc; - int locallen; - int functype; - uint modeaddr, opaddr, val; - int loctype, locnum; - uint addr = funcaddr; - - accelFunc = accel_get_func(addr); - if (accelFunc) { - profile_in(addr, stackptr, TRUE); - val = (this->*accelFunc)(argc, argv); - profile_out(stackptr); - pop_callstub(val); - return; - } - - profile_in(addr, stackptr, FALSE); - - /* Check the Glulx type identifier byte. */ - functype = Mem1(addr); - if (functype != 0xC0 && functype != 0xC1) { - if (functype >= 0xC0 && functype <= 0xDF) - fatal_error_i("Call to unknown type of function.", addr); - else - fatal_error_i("Call to non-function.", addr); - } - addr++; - - /* Bump the frameptr to the top. */ - frameptr = stackptr; - - /* Go through the function's locals-format list, copying it to the - call frame. At the same time, we work out how much space the locals - will actually take up. (Including padding.) */ - ix = 0; - locallen = 0; - while (1) { - /* Grab two bytes from the locals-format list. These are - unsigned (0..255 range). */ - loctype = Mem1(addr); - addr++; - locnum = Mem1(addr); - addr++; - - /* Copy them into the call frame. */ - StkW1(frameptr+8+2*ix, loctype); - StkW1(frameptr+8+2*ix+1, locnum); - ix++; - - /* If the type is zero, we're done, except possibly for two more - zero bytes in the call frame (to ensure 4-byte alignment.) */ - if (loctype == 0) { - /* Make sure ix is even. */ - if (ix & 1) { - StkW1(frameptr+8+2*ix, 0); - StkW1(frameptr+8+2*ix+1, 0); - ix++; - } - break; - } - - /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2 - bytes long. */ - if (loctype == 4) { - while (locallen & 3) - locallen++; - } - else if (loctype == 2) { - while (locallen & 1) - locallen++; - } - else if (loctype == 1) { - /* no padding */ - } - else { - fatal_error("Illegal local type in locals-format list."); - } - - /* Add the length of the locals themselves. */ - locallen += (loctype * locnum); - } - - /* Pad the locals to 4-byte alignment. */ - while (locallen & 3) - locallen++; - - /* We now know how long the locals-frame and locals segments are. */ - localsbase = frameptr+8+2*ix; - valstackbase = localsbase+locallen; - - /* Test for stack overflow. */ - /* This really isn't good enough; if the format list overflowed the - stack, we've already written outside the stack array. */ - if (valstackbase >= stacksize) - fatal_error("Stack overflow in function call."); - - /* Fill in the beginning of the stack frame. */ - StkW4(frameptr+4, 8+2*ix); - StkW4(frameptr, 8+2*ix+locallen); - - /* Set the stackptr and PC. */ - stackptr = valstackbase; - pc = addr; - - /* Zero out all the locals. */ - for (jx=0; jx < (uint)locallen; jx++) - StkW1(localsbase+jx, 0); - - if (functype == 0xC0) { - /* Push the function arguments on the stack. The locals have already - been zeroed. */ - if (stackptr+4*(argc+1) >= stacksize) - fatal_error("Stack overflow in function arguments."); - for (ix=0; ix<argc; ix++) { - val = argv[(argc-1)-ix]; - StkW4(stackptr, val); - stackptr += 4; - } - StkW4(stackptr, argc); - stackptr += 4; - } - else { - /* Copy in function arguments. This is a bit gross, since we have to - follow the locals format. If there are fewer arguments than locals, - that's fine -- we've already zeroed out this space. If there are - more arguments than locals, the extras are silently dropped. */ - modeaddr = frameptr+8; - opaddr = localsbase; - ix = 0; - while (ix < argc) { - loctype = Stk1(modeaddr); - modeaddr++; - locnum = Stk1(modeaddr); - modeaddr++; - if (loctype == 0) - break; - if (loctype == 4) { - while (opaddr & 3) - opaddr++; - while (ix < argc && locnum) { - val = argv[ix]; - StkW4(opaddr, val); - opaddr += 4; - ix++; - locnum--; - } - } - else if (loctype == 2) { - while (opaddr & 1) - opaddr++; - while (ix < argc && locnum) { - val = argv[ix] & 0xFFFF; - StkW2(opaddr, val); - opaddr += 2; - ix++; - locnum--; - } - } - else if (loctype == 1) { - while (ix < argc && locnum) { - val = argv[ix] & 0xFF; - StkW1(opaddr, val); - opaddr += 1; - ix++; - locnum--; - } - } - } - } - - /* If the debugger is compiled in, check for a breakpoint on this - function. (Checking the function address, not the starting PC.) */ - debugger_check_func_breakpoint(funcaddr); + uint ix, jx; + acceleration_func accelFunc; + int locallen; + int functype; + uint modeaddr, opaddr, val; + int loctype, locnum; + uint addr = funcaddr; + + accelFunc = accel_get_func(addr); + if (accelFunc) { + profile_in(addr, stackptr, TRUE); + val = (this->*accelFunc)(argc, argv); + profile_out(stackptr); + pop_callstub(val); + return; + } + + profile_in(addr, stackptr, FALSE); + + /* Check the Glulx type identifier byte. */ + functype = Mem1(addr); + if (functype != 0xC0 && functype != 0xC1) { + if (functype >= 0xC0 && functype <= 0xDF) + fatal_error_i("Call to unknown type of function.", addr); + else + fatal_error_i("Call to non-function.", addr); + } + addr++; + + /* Bump the frameptr to the top. */ + frameptr = stackptr; + + /* Go through the function's locals-format list, copying it to the + call frame. At the same time, we work out how much space the locals + will actually take up. (Including padding.) */ + ix = 0; + locallen = 0; + while (1) { + /* Grab two bytes from the locals-format list. These are + unsigned (0..255 range). */ + loctype = Mem1(addr); + addr++; + locnum = Mem1(addr); + addr++; + + /* Copy them into the call frame. */ + StkW1(frameptr + 8 + 2 * ix, loctype); + StkW1(frameptr + 8 + 2 * ix + 1, locnum); + ix++; + + /* If the type is zero, we're done, except possibly for two more + zero bytes in the call frame (to ensure 4-byte alignment.) */ + if (loctype == 0) { + /* Make sure ix is even. */ + if (ix & 1) { + StkW1(frameptr + 8 + 2 * ix, 0); + StkW1(frameptr + 8 + 2 * ix + 1, 0); + ix++; + } + break; + } + + /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2 + bytes long. */ + if (loctype == 4) { + while (locallen & 3) + locallen++; + } else if (loctype == 2) { + while (locallen & 1) + locallen++; + } else if (loctype == 1) { + /* no padding */ + } else { + fatal_error("Illegal local type in locals-format list."); + } + + /* Add the length of the locals themselves. */ + locallen += (loctype * locnum); + } + + /* Pad the locals to 4-byte alignment. */ + while (locallen & 3) + locallen++; + + /* We now know how long the locals-frame and locals segments are. */ + localsbase = frameptr + 8 + 2 * ix; + valstackbase = localsbase + locallen; + + /* Test for stack overflow. */ + /* This really isn't good enough; if the format list overflowed the + stack, we've already written outside the stack array. */ + if (valstackbase >= stacksize) + fatal_error("Stack overflow in function call."); + + /* Fill in the beginning of the stack frame. */ + StkW4(frameptr + 4, 8 + 2 * ix); + StkW4(frameptr, 8 + 2 * ix + locallen); + + /* Set the stackptr and PC. */ + stackptr = valstackbase; + pc = addr; + + /* Zero out all the locals. */ + for (jx = 0; jx < (uint)locallen; jx++) + StkW1(localsbase + jx, 0); + + if (functype == 0xC0) { + /* Push the function arguments on the stack. The locals have already + been zeroed. */ + if (stackptr + 4 * (argc + 1) >= stacksize) + fatal_error("Stack overflow in function arguments."); + for (ix = 0; ix < argc; ix++) { + val = argv[(argc - 1) - ix]; + StkW4(stackptr, val); + stackptr += 4; + } + StkW4(stackptr, argc); + stackptr += 4; + } else { + /* Copy in function arguments. This is a bit gross, since we have to + follow the locals format. If there are fewer arguments than locals, + that's fine -- we've already zeroed out this space. If there are + more arguments than locals, the extras are silently dropped. */ + modeaddr = frameptr + 8; + opaddr = localsbase; + ix = 0; + while (ix < argc) { + loctype = Stk1(modeaddr); + modeaddr++; + locnum = Stk1(modeaddr); + modeaddr++; + if (loctype == 0) + break; + if (loctype == 4) { + while (opaddr & 3) + opaddr++; + while (ix < argc && locnum) { + val = argv[ix]; + StkW4(opaddr, val); + opaddr += 4; + ix++; + locnum--; + } + } else if (loctype == 2) { + while (opaddr & 1) + opaddr++; + while (ix < argc && locnum) { + val = argv[ix] & 0xFFFF; + StkW2(opaddr, val); + opaddr += 2; + ix++; + locnum--; + } + } else if (loctype == 1) { + while (ix < argc && locnum) { + val = argv[ix] & 0xFF; + StkW1(opaddr, val); + opaddr += 1; + ix++; + locnum--; + } + } + } + } + + /* If the debugger is compiled in, check for a breakpoint on this + function. (Checking the function address, not the starting PC.) */ + debugger_check_func_breakpoint(funcaddr); } void Glulxe::leave_function() { - profile_out(stackptr); - stackptr = frameptr; + profile_out(stackptr); + stackptr = frameptr; } void Glulxe::push_callstub(uint desttype, uint destaddr) { - if (stackptr+16 > stacksize) - fatal_error("Stack overflow in callstub."); - StkW4(stackptr+0, desttype); - StkW4(stackptr+4, destaddr); - StkW4(stackptr+8, pc); - StkW4(stackptr+12, frameptr); - stackptr += 16; + if (stackptr + 16 > stacksize) + fatal_error("Stack overflow in callstub."); + StkW4(stackptr + 0, desttype); + StkW4(stackptr + 4, destaddr); + StkW4(stackptr + 8, pc); + StkW4(stackptr + 12, frameptr); + stackptr += 16; } void Glulxe::pop_callstub(uint returnvalue) { - uint desttype, destaddr; - uint newpc, newframeptr; - - if (stackptr < 16) - fatal_error("Stack underflow in callstub."); - stackptr -= 16; - - newframeptr = Stk4(stackptr+12); - newpc = Stk4(stackptr+8); - destaddr = Stk4(stackptr+4); - desttype = Stk4(stackptr+0); - - pc = newpc; - frameptr = newframeptr; - - /* Recompute valstackbase and localsbase */ - valstackbase = frameptr + Stk4(frameptr); - localsbase = frameptr + Stk4(frameptr+4); - - switch (desttype) { - - case 0x11: - fatal_error("String-terminator call stub at end of function call."); - break; - - case 0x10: - /* This call stub was pushed during a string-decoding operation! - We have to restart it. (Note that the return value is discarded.) */ - stream_string(pc, 0xE1, destaddr); - break; - - case 0x12: - /* This call stub was pushed during a number-printing operation. - Restart that. (Return value discarded.) */ - stream_num(pc, true, destaddr); - break; - - case 0x13: - /* This call stub was pushed during a C-string printing operation. - We have to restart it. (Note that the return value is discarded.) */ - stream_string(pc, 0xE0, destaddr); - break; - - case 0x14: - /* This call stub was pushed during a Unicode printing operation. - We have to restart it. (Note that the return value is discarded.) */ - stream_string(pc, 0xE2, destaddr); - break; - - default: - /* We're back in the original frame, so we can store the returnvalue. - (If we tried to do this before resetting frameptr, a result - destination on the stack would go astray.) */ - store_operand(desttype, destaddr, returnvalue); - break; - } + uint desttype, destaddr; + uint newpc, newframeptr; + + if (stackptr < 16) + fatal_error("Stack underflow in callstub."); + stackptr -= 16; + + newframeptr = Stk4(stackptr + 12); + newpc = Stk4(stackptr + 8); + destaddr = Stk4(stackptr + 4); + desttype = Stk4(stackptr + 0); + + pc = newpc; + frameptr = newframeptr; + + /* Recompute valstackbase and localsbase */ + valstackbase = frameptr + Stk4(frameptr); + localsbase = frameptr + Stk4(frameptr + 4); + + switch (desttype) { + + case 0x11: + fatal_error("String-terminator call stub at end of function call."); + break; + + case 0x10: + /* This call stub was pushed during a string-decoding operation! + We have to restart it. (Note that the return value is discarded.) */ + stream_string(pc, 0xE1, destaddr); + break; + + case 0x12: + /* This call stub was pushed during a number-printing operation. + Restart that. (Return value discarded.) */ + stream_num(pc, true, destaddr); + break; + + case 0x13: + /* This call stub was pushed during a C-string printing operation. + We have to restart it. (Note that the return value is discarded.) */ + stream_string(pc, 0xE0, destaddr); + break; + + case 0x14: + /* This call stub was pushed during a Unicode printing operation. + We have to restart it. (Note that the return value is discarded.) */ + stream_string(pc, 0xE2, destaddr); + break; + + default: + /* We're back in the original frame, so we can store the returnvalue. + (If we tried to do this before resetting frameptr, a result + destination on the stack would go astray.) */ + store_operand(desttype, destaddr, returnvalue); + break; + } } uint Glulxe::pop_callstub_string(int *bitnum) { - uint desttype, destaddr, newpc; + uint desttype, destaddr, newpc; - if (stackptr < 16) - fatal_error("Stack underflow in callstub."); - stackptr -= 16; + if (stackptr < 16) + fatal_error("Stack underflow in callstub."); + stackptr -= 16; - newpc = Stk4(stackptr+8); - destaddr = Stk4(stackptr+4); - desttype = Stk4(stackptr+0); + newpc = Stk4(stackptr + 8); + destaddr = Stk4(stackptr + 4); + desttype = Stk4(stackptr + 0); - pc = newpc; + pc = newpc; - if (desttype == 0x11) { - return 0; - } - if (desttype == 0x10) { - *bitnum = destaddr; - return pc; - } + if (desttype == 0x11) { + return 0; + } + if (desttype == 0x10) { + *bitnum = destaddr; + return pc; + } - fatal_error("Function-terminator call stub at end of string."); - return 0; + fatal_error("Function-terminator call stub at end of string."); + return 0; } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/gestalt.cpp b/engines/glk/glulxe/gestalt.cpp index 21f8974513..18f9083b34 100644 --- a/engines/glk/glulxe/gestalt.cpp +++ b/engines/glk/glulxe/gestalt.cpp @@ -26,75 +26,75 @@ namespace Glk { namespace Glulxe { uint Glulxe::do_gestalt(uint val, uint val2) { - switch (val) { + switch (val) { - case gestulx_GlulxVersion: - return 0x00030102; /* Glulx spec version 3.1.2 */ + case gestulx_GlulxVersion: + return 0x00030102; /* Glulx spec version 3.1.2 */ - case gestulx_TerpVersion: - return 0x00000504; /* Glulxe version 0.5.4 */ + case gestulx_TerpVersion: + return 0x00000504; /* Glulxe version 0.5.4 */ - case gestulx_ResizeMem: + case gestulx_ResizeMem: #ifdef FIXED_MEMSIZE - return 0; /* The setmemsize opcodes are compiled out. */ + return 0; /* The setmemsize opcodes are compiled out. */ #else /* FIXED_MEMSIZE */ - return 1; /* We can handle setmemsize. */ + return 1; /* We can handle setmemsize. */ #endif /* FIXED_MEMSIZE */ - case gestulx_Undo: - return 1; /* We can handle saveundo and restoreundo. */ + case gestulx_Undo: + return 1; /* We can handle saveundo and restoreundo. */ - case gestulx_IOSystem: - switch (val2) { - case 0: - return 1; /* The "null" system always works. */ - case 1: - return 1; /* The "filter" system always works. */ - case 2: - return 1; /* A Glk library is hooked up. */ - default: - return 0; - } + case gestulx_IOSystem: + switch (val2) { + case 0: + return 1; /* The "null" system always works. */ + case 1: + return 1; /* The "filter" system always works. */ + case 2: + return 1; /* A Glk library is hooked up. */ + default: + return 0; + } - case gestulx_Unicode: - return 1; /* We can handle Unicode. */ + case gestulx_Unicode: + return 1; /* We can handle Unicode. */ - case gestulx_MemCopy: - return 1; /* We can do mcopy/mzero. */ + case gestulx_MemCopy: + return 1; /* We can do mcopy/mzero. */ - case gestulx_MAlloc: + case gestulx_MAlloc: #ifdef FIXED_MEMSIZE - return 0; /* The malloc opcodes are compiled out. */ + return 0; /* The malloc opcodes are compiled out. */ #else /* FIXED_MEMSIZE */ - return 1; /* We can handle malloc/mfree. */ + return 1; /* We can handle malloc/mfree. */ #endif /* FIXED_MEMSIZE */ - case gestulx_MAllocHeap: - return heap_get_start(); + case gestulx_MAllocHeap: + return heap_get_start(); - case gestulx_Acceleration: - return 1; /* We can do accelfunc/accelparam. */ + case gestulx_Acceleration: + return 1; /* We can do accelfunc/accelparam. */ - case gestulx_AccelFunc: - if (accel_find_func(val2)) - return 1; /* We know this accelerated function. */ - return 0; + case gestulx_AccelFunc: + if (accel_find_func(val2)) + return 1; /* We know this accelerated function. */ + return 0; - case gestulx_Float: + case gestulx_Float: #ifdef FLOAT_SUPPORT - return 1; /* We can do floating-point operations. */ + return 1; /* We can do floating-point operations. */ #else /* FLOAT_SUPPORT */ - return 0; /* The floating-point opcodes are not compiled in. */ + return 0; /* The floating-point opcodes are not compiled in. */ #endif /* FLOAT_SUPPORT */ #ifdef GLULX_EXTEND_GESTALT - GLULX_EXTEND_GESTALT + GLULX_EXTEND_GESTALT #endif /* GLULX_EXTEND_GESTALT */ - default: - return 0; + default: + return 0; - } + } } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/glkop.cpp b/engines/glk/glulxe/glkop.cpp index 12d4fef0d3..cb912ecb19 100644 --- a/engines/glk/glulxe/glkop.cpp +++ b/engines/glk/glulxe/glkop.cpp @@ -27,7 +27,7 @@ namespace Glulxe { /* This code is actually very general; it could work for almost any 32-bit VM which remotely resembles Glulxe or the Z-machine in design. - + To be precise, we make the following assumptions: - An argument list is an array of 32-bit values, which can represent @@ -66,41 +66,41 @@ namespace Glulxe { */ #define ReadMemory(addr) \ - (((addr) == 0xffffffff) \ - ? (stackptr -= 4, Stk4(stackptr)) \ - : (Mem4(addr))) + (((addr) == 0xffffffff) \ + ? (stackptr -= 4, Stk4(stackptr)) \ + : (Mem4(addr))) #define WriteMemory(addr, val) \ - (((addr) == 0xffffffff) \ - ? (StkW4(stackptr, (val)), stackptr += 4) \ - : (MemW4((addr), (val)))) + (((addr) == 0xffffffff) \ + ? (StkW4(stackptr, (val)), stackptr += 4) \ + : (MemW4((addr), (val)))) #define CaptureCArray(addr, len, passin) \ - (grab_temp_c_array(addr, len, passin)) + (grab_temp_c_array(addr, len, passin)) #define ReleaseCArray(ptr, addr, len, passout) \ - (release_temp_c_array(ptr, addr, len, passout)) + (release_temp_c_array(ptr, addr, len, passout)) #define CaptureIArray(addr, len, passin) \ - (grab_temp_i_array(addr, len, passin)) + (grab_temp_i_array(addr, len, passin)) #define ReleaseIArray(ptr, addr, len, passout) \ - (release_temp_i_array(ptr, addr, len, passout)) + (release_temp_i_array(ptr, addr, len, passout)) #define CapturePtrArray(addr, len, objclass, passin) \ - (grab_temp_ptr_array(addr, len, objclass, passin)) + (grab_temp_ptr_array(addr, len, objclass, passin)) #define ReleasePtrArray(ptr, addr, len, objclass, passout) \ - (release_temp_ptr_array(ptr, addr, len, objclass, passout)) + (release_temp_ptr_array(ptr, addr, len, objclass, passout)) #define ReadStructField(addr, fieldnum) \ - (((addr) == 0xffffffff) \ - ? (stackptr -= 4, Stk4(stackptr)) \ - : (Mem4((addr)+(fieldnum)*4))) + (((addr) == 0xffffffff) \ + ? (stackptr -= 4, Stk4(stackptr)) \ + : (Mem4((addr)+(fieldnum)*4))) #define WriteStructField(addr, fieldnum, val) \ - (((addr) == 0xffffffff) \ - ? (StkW4(stackptr, (val)), stackptr += 4) \ - : (MemW4((addr)+(fieldnum)*4, (val)))) + (((addr) == 0xffffffff) \ + ? (StkW4(stackptr, (val)), stackptr += 4) \ + : (MemW4((addr)+(fieldnum)*4, (val)))) #define DecodeVMString(addr) \ - (make_temp_string(addr)) + (make_temp_string(addr)) #define ReleaseVMString(ptr) \ - (free_temp_string(ptr)) + (free_temp_string(ptr)) #define DecodeVMUstring(addr) \ - (make_temp_ustring(addr)) + (make_temp_ustring(addr)) #define ReleaseVMUstring(ptr) \ - (free_temp_ustring(ptr)) + (free_temp_ustring(ptr)) static gidispatch_rock_t classtable_register(void *obj, uint objclass) { return g_vm->glulxe_classtable_register(obj, objclass); @@ -129,1252 +129,1213 @@ void Glulxe::glkopInit() { Set up the class hash tables and other startup-time stuff. */ bool Glulxe::init_dispatch() { - int ix; - - /* What with one thing and another, this *could* be called more than - once. We only need to allocate the tables once. */ - if (classes) - return true; - - /* Set up the game-ID hook. (This is ifdeffed because not all Glk - libraries have this call.) */ + int ix; + + /* What with one thing and another, this *could* be called more than + once. We only need to allocate the tables once. */ + if (classes) + return true; + + /* Set up the game-ID hook. (This is ifdeffed because not all Glk + libraries have this call.) */ #ifdef GI_DISPA_GAME_ID_AVAILABLE - gidispatch_set_game_id_hook(&get_game_id); + gidispatch_set_game_id_hook(&get_game_id); #endif /* GI_DISPA_GAME_ID_AVAILABLE */ - - /* Allocate the class hash tables. */ - num_classes = gidispatch_count_classes(); - classes = (classtable_t **)glulx_malloc(num_classes * sizeof(classtable_t *)); - if (!classes) - return false; - - for (ix=0; ix<num_classes; ix++) { - classes[ix] = new_classtable((glulx_random() % (uint)(101)) + 1); - if (!classes[ix]) - return false; - } - - /* Set up the two callbacks. */ - gidispatch_set_object_registry(&classtable_register, &classtable_unregister); - gidispatch_set_retained_registry(&retained_register, &retained_unregister); - - /* If the library supports autorestore callbacks, set those up too. - (These are only used in iosglk, currently.) */ + + /* Allocate the class hash tables. */ + num_classes = gidispatch_count_classes(); + classes = (classtable_t **)glulx_malloc(num_classes * sizeof(classtable_t *)); + if (!classes) + return false; + + for (ix = 0; ix < num_classes; ix++) { + classes[ix] = new_classtable((glulx_random() % (uint)(101)) + 1); + if (!classes[ix]) + return false; + } + + /* Set up the two callbacks. */ + gidispatch_set_object_registry(&classtable_register, &classtable_unregister); + gidispatch_set_retained_registry(&retained_register, &retained_unregister); + + /* If the library supports autorestore callbacks, set those up too. + (These are only used in iosglk, currently.) */ #ifdef GIDISPATCH_AUTORESTORE_REGISTRY - gidispatch_set_autorestore_registry(&glulxe_array_locate, &glulxe_array_restore); + gidispatch_set_autorestore_registry(&glulxe_array_locate, &glulxe_array_restore); #endif /* GIDISPATCH_AUTORESTORE_REGISTRY */ - - return true; + + return true; } uint Glulxe::perform_glk(uint funcnum, uint numargs, uint *arglist) { - uint retval = 0; - - switch (funcnum) { - /* To speed life up, we implement commonly-used Glk functions - directly -- instead of bothering with the whole prototype - mess. */ - - case 0x0047: /* stream_set_current */ - if (numargs != 1) - goto WrongArgNum; - glk_stream_set_current(find_stream_by_id(arglist[0])); - break; - case 0x0048: /* stream_get_current */ - if (numargs != 0) - goto WrongArgNum; - retval = find_id_for_stream(glk_stream_get_current()); - break; - case 0x0080: /* put_char */ - if (numargs != 1) - goto WrongArgNum; - glk_put_char(arglist[0] & 0xFF); - break; - case 0x0081: /* put_char_stream */ - if (numargs != 2) - goto WrongArgNum; - glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF); - break; - case 0x00C0: /* select */ - /* call a library hook on every glk_select() */ - if (library_select_hook) - library_select_hook(arglist[0]); - /* but then fall through to full dispatcher, because there's no real - need for speed here */ - goto FullDispatcher; - case 0x00A0: /* char_to_lower */ - if (numargs != 1) - goto WrongArgNum; - retval = glk_char_to_lower(arglist[0] & 0xFF); - break; - case 0x00A1: /* char_to_upper */ - if (numargs != 1) - goto WrongArgNum; - retval = glk_char_to_upper(arglist[0] & 0xFF); - break; - case 0x0128: /* put_char_uni */ - if (numargs != 1) - goto WrongArgNum; - glk_put_char_uni(arglist[0]); - break; - case 0x012B: /* put_char_stream_uni */ - if (numargs != 2) - goto WrongArgNum; - glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]); - break; - - WrongArgNum: - error("Wrong number of arguments to Glk function."); - break; - - FullDispatcher: - default: { - /* Go through the full dispatcher prototype foo. */ - const char *proto, *cx; - dispatch_splot_t splot; - int argnum, argnum2; - - /* Grab the string. */ - proto = gidispatch_prototype(funcnum); - if (!proto) - error("Unknown Glk function."); - - splot.varglist = arglist; - splot.numvargs = numargs; - splot.retval = &retval; - - /* The work goes in four phases. First, we figure out how many - arguments we want, and allocate space for the Glk argument - list. Then we go through the Glulxe arguments and load them - into the Glk list. Then we call. Then we go through the - arguments again, unloading the data back into Glulx memory. */ - - /* Phase 0. */ - prepare_glk_args(proto, &splot); - - /* Phase 1. */ - argnum = 0; - cx = proto; - parse_glk_args(&splot, &cx, 0, &argnum, 0, 0); - - /* Phase 2. */ - gidispatch_call(funcnum, argnum, splot.garglist); - - /* Phase 3. */ - argnum2 = 0; - cx = proto; - unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0); - if (argnum != argnum2) - error("Argument counts did not match."); - - break; - } - } - - return retval; + uint retval = 0; + + switch (funcnum) { + /* To speed life up, we implement commonly-used Glk functions + directly -- instead of bothering with the whole prototype + mess. */ + + case 0x0047: /* stream_set_current */ + if (numargs != 1) + goto WrongArgNum; + glk_stream_set_current(find_stream_by_id(arglist[0])); + break; + case 0x0048: /* stream_get_current */ + if (numargs != 0) + goto WrongArgNum; + retval = find_id_for_stream(glk_stream_get_current()); + break; + case 0x0080: /* put_char */ + if (numargs != 1) + goto WrongArgNum; + glk_put_char(arglist[0] & 0xFF); + break; + case 0x0081: /* put_char_stream */ + if (numargs != 2) + goto WrongArgNum; + glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF); + break; + case 0x00C0: /* select */ + /* call a library hook on every glk_select() */ + if (library_select_hook) + library_select_hook(arglist[0]); + /* but then fall through to full dispatcher, because there's no real + need for speed here */ + goto FullDispatcher; + case 0x00A0: /* char_to_lower */ + if (numargs != 1) + goto WrongArgNum; + retval = glk_char_to_lower(arglist[0] & 0xFF); + break; + case 0x00A1: /* char_to_upper */ + if (numargs != 1) + goto WrongArgNum; + retval = glk_char_to_upper(arglist[0] & 0xFF); + break; + case 0x0128: /* put_char_uni */ + if (numargs != 1) + goto WrongArgNum; + glk_put_char_uni(arglist[0]); + break; + case 0x012B: /* put_char_stream_uni */ + if (numargs != 2) + goto WrongArgNum; + glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]); + break; + +WrongArgNum: + error("Wrong number of arguments to Glk function."); + break; + +FullDispatcher: + default: { + /* Go through the full dispatcher prototype foo. */ + const char *proto, *cx; + dispatch_splot_t splot; + int argnum, argnum2; + + /* Grab the string. */ + proto = gidispatch_prototype(funcnum); + if (!proto) + error("Unknown Glk function."); + + splot.varglist = arglist; + splot.numvargs = numargs; + splot.retval = &retval; + + /* The work goes in four phases. First, we figure out how many + arguments we want, and allocate space for the Glk argument + list. Then we go through the Glulxe arguments and load them + into the Glk list. Then we call. Then we go through the + arguments again, unloading the data back into Glulx memory. */ + + /* Phase 0. */ + prepare_glk_args(proto, &splot); + + /* Phase 1. */ + argnum = 0; + cx = proto; + parse_glk_args(&splot, &cx, 0, &argnum, 0, 0); + + /* Phase 2. */ + gidispatch_call(funcnum, argnum, splot.garglist); + + /* Phase 3. */ + argnum2 = 0; + cx = proto; + unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0); + if (argnum != argnum2) + error("Argument counts did not match."); + + break; + } + } + + return retval; } const char *Glulxe::read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout, - int *nullok, int *isretained, int *isreturn) { - *isref = false; - *passin = false; - *passout = false; - *nullok = true; - *isarray = false; - *isretained = false; - *isreturn = false; - while (1) { - if (*cx == '<') { - *isref = true; - *passout = true; - } - else if (*cx == '>') { - *isref = true; - *passin = true; - } - else if (*cx == '&') { - *isref = true; - *passout = true; - *passin = true; - } - else if (*cx == '+') { - *nullok = false; - } - else if (*cx == ':') { - *isref = true; - *passout = true; - *nullok = false; - *isreturn = true; - } - else if (*cx == '#') { - *isarray = true; - } - else if (*cx == '!') { - *isretained = true; - } - else { - break; - } - cx++; - } - return cx; + int *nullok, int *isretained, int *isreturn) { + *isref = false; + *passin = false; + *passout = false; + *nullok = true; + *isarray = false; + *isretained = false; + *isreturn = false; + while (1) { + if (*cx == '<') { + *isref = true; + *passout = true; + } else if (*cx == '>') { + *isref = true; + *passin = true; + } else if (*cx == '&') { + *isref = true; + *passout = true; + *passin = true; + } else if (*cx == '+') { + *nullok = false; + } else if (*cx == ':') { + *isref = true; + *passout = true; + *nullok = false; + *isreturn = true; + } else if (*cx == '#') { + *isarray = true; + } else if (*cx == '!') { + *isretained = true; + } else { + break; + } + cx++; + } + return cx; } void Glulxe::prepare_glk_args(const char *proto, dispatch_splot_t *splot) { - static gluniversal_t *garglist = nullptr; - static int garglist_size = 0; - - int ix; - int numwanted, numvargswanted, maxargs; - const char *cx; - - cx = proto; - numwanted = 0; - while (*cx >= '0' && *cx <= '9') { - numwanted = 10 * numwanted + (*cx - '0'); - cx++; - } - splot->numwanted = numwanted; - - maxargs = 0; - numvargswanted = 0; - for (ix = 0; ix < numwanted; ix++) { - int isref, passin, passout, nullok, isarray, isretained, isreturn; - cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, - &isretained, &isreturn); - if (isref) { - maxargs += 2; - } - else { - maxargs += 1; - } - if (!isreturn) { - if (isarray) { - numvargswanted += 2; - } - else { - numvargswanted += 1; - } - } - - if (*cx == 'I' || *cx == 'C') { - cx += 2; - } - else if (*cx == 'Q') { - cx += 2; - } - else if (*cx == 'S' || *cx == 'U') { - cx += 1; - } - else if (*cx == '[') { - int refdepth, nwx; - cx++; - nwx = 0; - while (*cx >= '0' && *cx <= '9') { - nwx = 10 * nwx + (*cx - '0'); - cx++; - } - maxargs += nwx; /* This is *only* correct because all structs contain + static gluniversal_t *garglist = nullptr; + static int garglist_size = 0; + + int ix; + int numwanted, numvargswanted, maxargs; + const char *cx; + + cx = proto; + numwanted = 0; + while (*cx >= '0' && *cx <= '9') { + numwanted = 10 * numwanted + (*cx - '0'); + cx++; + } + splot->numwanted = numwanted; + + maxargs = 0; + numvargswanted = 0; + for (ix = 0; ix < numwanted; ix++) { + int isref, passin, passout, nullok, isarray, isretained, isreturn; + cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, + &isretained, &isreturn); + if (isref) { + maxargs += 2; + } else { + maxargs += 1; + } + if (!isreturn) { + if (isarray) { + numvargswanted += 2; + } else { + numvargswanted += 1; + } + } + + if (*cx == 'I' || *cx == 'C') { + cx += 2; + } else if (*cx == 'Q') { + cx += 2; + } else if (*cx == 'S' || *cx == 'U') { + cx += 1; + } else if (*cx == '[') { + int refdepth, nwx; + cx++; + nwx = 0; + while (*cx >= '0' && *cx <= '9') { + nwx = 10 * nwx + (*cx - '0'); + cx++; + } + maxargs += nwx; /* This is *only* correct because all structs contain plain values. */ - refdepth = 1; - while (refdepth > 0) { - if (*cx == '[') - refdepth++; - else if (*cx == ']') - refdepth--; - cx++; - } - } - else { - error("Illegal format string."); - } - } - - if (*cx != ':' && *cx != '\0') - error("Illegal format string."); - - splot->maxargs = maxargs; - - if (splot->numvargs != numvargswanted) - error("Wrong number of arguments to Glk function."); - - if (garglist && garglist_size < maxargs) { - glulx_free(garglist); - garglist = nullptr; - garglist_size = 0; - } - if (!garglist) { - garglist_size = maxargs + 16; - garglist = (gluniversal_t *)glulx_malloc(garglist_size - * sizeof(gluniversal_t)); - } - if (!garglist) - error("Unable to allocate storage for Glk arguments."); - - splot->garglist = garglist; + refdepth = 1; + while (refdepth > 0) { + if (*cx == '[') + refdepth++; + else if (*cx == ']') + refdepth--; + cx++; + } + } else { + error("Illegal format string."); + } + } + + if (*cx != ':' && *cx != '\0') + error("Illegal format string."); + + splot->maxargs = maxargs; + + if (splot->numvargs != numvargswanted) + error("Wrong number of arguments to Glk function."); + + if (garglist && garglist_size < maxargs) { + glulx_free(garglist); + garglist = nullptr; + garglist_size = 0; + } + if (!garglist) { + garglist_size = maxargs + 16; + garglist = (gluniversal_t *)glulx_malloc(garglist_size + * sizeof(gluniversal_t)); + } + if (!garglist) + error("Unable to allocate storage for Glk arguments."); + + splot->garglist = garglist; } void Glulxe::parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr, - uint subaddress, int subpassin) { - const char *cx; - int ix, argx; - int gargnum, numwanted; - void *opref; - gluniversal_t *garglist; - uint *varglist; - - garglist = splot->garglist; - varglist = splot->varglist; - gargnum = *argnumptr; - cx = *proto; - - numwanted = 0; - while (*cx >= '0' && *cx <= '9') { - numwanted = 10 * numwanted + (*cx - '0'); - cx++; - } - - for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) { - char typeclass; - int skipval; - int isref, passin, passout, nullok, isarray, isretained, isreturn; - cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, - &isretained, &isreturn); - - typeclass = *cx; - cx++; - - skipval = false; - if (isref) { - if (!isreturn && varglist[ix] == 0) { - if (!nullok) - error("Zero passed invalidly to Glk function."); - garglist[gargnum]._ptrflag = false; - gargnum++; - skipval = true; - } - else { - garglist[gargnum]._ptrflag = true; - gargnum++; - } - } - if (!skipval) { - uint thisval; - - if (typeclass == '[') { - - parse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passin); - - } - else if (isarray) { - /* definitely isref */ - - switch (typeclass) { - case 'C': - /* This test checks for a giant array length, which is - deprecated. It displays a warning and cuts it down to - something reasonable. Future releases of this interpreter - may remove this test and go on to verify_array_addresses(), - which treats this case as a fatal error. */ - if (varglist[ix+1] > endmem - || varglist[ix]+varglist[ix+1] > endmem) { - nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix+1]); - varglist[ix+1] = endmem - varglist[ix]; - } - verify_array_addresses(varglist[ix], varglist[ix+1], 1); - garglist[gargnum]._array = CaptureCArray(varglist[ix], varglist[ix+1], passin); - gargnum++; - ix++; - garglist[gargnum]._uint = varglist[ix]; - gargnum++; - cx++; - break; - case 'I': - /* See comment above. */ - if (varglist[ix+1] > endmem/4 - || varglist[ix+1] > (endmem-varglist[ix])/4) { - nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix+1]); - varglist[ix+1] = (endmem - varglist[ix]) / 4; - } - verify_array_addresses(varglist[ix], varglist[ix+1], 4); - garglist[gargnum]._array = CaptureIArray(varglist[ix], varglist[ix+1], passin); - gargnum++; - ix++; - garglist[gargnum]._uint = varglist[ix]; - gargnum++; - cx++; - break; - case 'Q': - /* This case was added after the giant arrays were deprecated, - so we don't bother to allow for that case. We just verify - the length. */ - verify_array_addresses(varglist[ix], varglist[ix+1], 4); - garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix+1], (*cx-'a'), passin); - gargnum++; - ix++; - garglist[gargnum]._uint = varglist[ix]; - gargnum++; - cx++; - break; - default: - error("Illegal format string."); - break; - } - } - else { - /* a plain value or a reference to one. */ - - if (isreturn) { - thisval = 0; - } - else if (depth > 0) { - /* Definitely not isref or isarray. */ - if (subpassin) - thisval = ReadStructField(subaddress, ix); - else - thisval = 0; - } - else if (isref) { - if (passin) - thisval = ReadMemory(varglist[ix]); - else - thisval = 0; - } - else { - thisval = varglist[ix]; - } - - switch (typeclass) { - case 'I': - if (*cx == 'u') - garglist[gargnum]._uint = (uint)(thisval); - else if (*cx == 's') - garglist[gargnum]._sint = (int)(thisval); - else - error("Illegal format string."); - gargnum++; - cx++; - break; - case 'Q': - if (thisval) { - opref = classes_get(*cx-'a', thisval); - if (!opref) { - error("Reference to nonexistent Glk object."); - } - } - else { - opref = nullptr; - } - garglist[gargnum]._opaqueref = opref; - gargnum++; - cx++; - break; - case 'C': - if (*cx == 'u') - garglist[gargnum]._uch = (unsigned char)(thisval); - else if (*cx == 's') - garglist[gargnum]._sch = (signed char)(thisval); - else if (*cx == 'n') - garglist[gargnum]._ch = (char)(thisval); - else - error("Illegal format string."); - gargnum++; - cx++; - break; - case 'S': - garglist[gargnum]._charstr = DecodeVMString(thisval); - gargnum++; - break; + uint subaddress, int subpassin) { + const char *cx; + int ix, argx; + int gargnum, numwanted; + void *opref; + gluniversal_t *garglist; + uint *varglist; + + garglist = splot->garglist; + varglist = splot->varglist; + gargnum = *argnumptr; + cx = *proto; + + numwanted = 0; + while (*cx >= '0' && *cx <= '9') { + numwanted = 10 * numwanted + (*cx - '0'); + cx++; + } + + for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) { + char typeclass; + int skipval; + int isref, passin, passout, nullok, isarray, isretained, isreturn; + cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, + &isretained, &isreturn); + + typeclass = *cx; + cx++; + + skipval = false; + if (isref) { + if (!isreturn && varglist[ix] == 0) { + if (!nullok) + error("Zero passed invalidly to Glk function."); + garglist[gargnum]._ptrflag = false; + gargnum++; + skipval = true; + } else { + garglist[gargnum]._ptrflag = true; + gargnum++; + } + } + if (!skipval) { + uint thisval; + + if (typeclass == '[') { + + parse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passin); + + } else if (isarray) { + /* definitely isref */ + + switch (typeclass) { + case 'C': + /* This test checks for a giant array length, which is + deprecated. It displays a warning and cuts it down to + something reasonable. Future releases of this interpreter + may remove this test and go on to verify_array_addresses(), + which treats this case as a fatal error. */ + if (varglist[ix + 1] > endmem + || varglist[ix] + varglist[ix + 1] > endmem) { + nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]); + varglist[ix + 1] = endmem - varglist[ix]; + } + verify_array_addresses(varglist[ix], varglist[ix + 1], 1); + garglist[gargnum]._array = CaptureCArray(varglist[ix], varglist[ix + 1], passin); + gargnum++; + ix++; + garglist[gargnum]._uint = varglist[ix]; + gargnum++; + cx++; + break; + case 'I': + /* See comment above. */ + if (varglist[ix + 1] > endmem / 4 + || varglist[ix + 1] > (endmem - varglist[ix]) / 4) { + nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]); + varglist[ix + 1] = (endmem - varglist[ix]) / 4; + } + verify_array_addresses(varglist[ix], varglist[ix + 1], 4); + garglist[gargnum]._array = CaptureIArray(varglist[ix], varglist[ix + 1], passin); + gargnum++; + ix++; + garglist[gargnum]._uint = varglist[ix]; + gargnum++; + cx++; + break; + case 'Q': + /* This case was added after the giant arrays were deprecated, + so we don't bother to allow for that case. We just verify + the length. */ + verify_array_addresses(varglist[ix], varglist[ix + 1], 4); + garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix + 1], (*cx - 'a'), passin); + gargnum++; + ix++; + garglist[gargnum]._uint = varglist[ix]; + gargnum++; + cx++; + break; + default: + error("Illegal format string."); + break; + } + } else { + /* a plain value or a reference to one. */ + + if (isreturn) { + thisval = 0; + } else if (depth > 0) { + /* Definitely not isref or isarray. */ + if (subpassin) + thisval = ReadStructField(subaddress, ix); + else + thisval = 0; + } else if (isref) { + if (passin) + thisval = ReadMemory(varglist[ix]); + else + thisval = 0; + } else { + thisval = varglist[ix]; + } + + switch (typeclass) { + case 'I': + if (*cx == 'u') + garglist[gargnum]._uint = (uint)(thisval); + else if (*cx == 's') + garglist[gargnum]._sint = (int)(thisval); + else + error("Illegal format string."); + gargnum++; + cx++; + break; + case 'Q': + if (thisval) { + opref = classes_get(*cx - 'a', thisval); + if (!opref) { + error("Reference to nonexistent Glk object."); + } + } else { + opref = nullptr; + } + garglist[gargnum]._opaqueref = opref; + gargnum++; + cx++; + break; + case 'C': + if (*cx == 'u') + garglist[gargnum]._uch = (unsigned char)(thisval); + else if (*cx == 's') + garglist[gargnum]._sch = (signed char)(thisval); + else if (*cx == 'n') + garglist[gargnum]._ch = (char)(thisval); + else + error("Illegal format string."); + gargnum++; + cx++; + break; + case 'S': + garglist[gargnum]._charstr = DecodeVMString(thisval); + gargnum++; + break; #ifdef GLK_MODULE_UNICODE - case 'U': - garglist[gargnum]._unicharstr = DecodeVMUstring(thisval); - gargnum++; - break; + case 'U': + garglist[gargnum]._unicharstr = DecodeVMUstring(thisval); + gargnum++; + break; #endif /* GLK_MODULE_UNICODE */ - default: - error("Illegal format string."); - break; - } - } - } - else { - /* We got a null reference, so we have to skip the format element. */ - if (typeclass == '[') { - int numsubwanted, refdepth; - numsubwanted = 0; - while (*cx >= '0' && *cx <= '9') { - numsubwanted = 10 * numsubwanted + (*cx - '0'); - cx++; - } - refdepth = 1; - while (refdepth > 0) { - if (*cx == '[') - refdepth++; - else if (*cx == ']') - refdepth--; - cx++; - } - } - else if (typeclass == 'S' || typeclass == 'U') { - /* leave it */ - } - else { - cx++; - if (isarray) - ix++; - } - } - } - - if (depth > 0) { - if (*cx != ']') - error("Illegal format string."); - cx++; - } - else { - if (*cx != ':' && *cx != '\0') - error("Illegal format string."); - } - - *proto = cx; - *argnumptr = gargnum; + default: + error("Illegal format string."); + break; + } + } + } else { + /* We got a null reference, so we have to skip the format element. */ + if (typeclass == '[') { + int numsubwanted, refdepth; + numsubwanted = 0; + while (*cx >= '0' && *cx <= '9') { + numsubwanted = 10 * numsubwanted + (*cx - '0'); + cx++; + } + refdepth = 1; + while (refdepth > 0) { + if (*cx == '[') + refdepth++; + else if (*cx == ']') + refdepth--; + cx++; + } + } else if (typeclass == 'S' || typeclass == 'U') { + /* leave it */ + } else { + cx++; + if (isarray) + ix++; + } + } + } + + if (depth > 0) { + if (*cx != ']') + error("Illegal format string."); + cx++; + } else { + if (*cx != ':' && *cx != '\0') + error("Illegal format string."); + } + + *proto = cx; + *argnumptr = gargnum; } void Glulxe::unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, - int *argnumptr, uint subaddress, int subpassout) { - const char *cx; - int ix, argx; - int gargnum, numwanted; - void *opref; - gluniversal_t *garglist; - uint *varglist; - - garglist = splot->garglist; - varglist = splot->varglist; - gargnum = *argnumptr; - cx = *proto; - - numwanted = 0; - while (*cx >= '0' && *cx <= '9') { - numwanted = 10 * numwanted + (*cx - '0'); - cx++; - } - - for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) { - char typeclass; - int skipval; - int isref, passin, passout, nullok, isarray, isretained, isreturn; - cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, - &isretained, &isreturn); - - typeclass = *cx; - cx++; - - skipval = false; - if (isref) { - if (!isreturn && varglist[ix] == 0) { - if (!nullok) - error("Zero passed invalidly to Glk function."); - garglist[gargnum]._ptrflag = false; - gargnum++; - skipval = true; - } else { - garglist[gargnum]._ptrflag = true; - gargnum++; - } - } - if (!skipval) { - uint thisval = 0; - - if (typeclass == '[') { - - unparse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passout); - - } - else if (isarray) { - /* definitely isref */ - - switch (typeclass) { - case 'C': - ReleaseCArray((char *)garglist[gargnum]._array, varglist[ix], varglist[ix+1], passout); - gargnum++; - ix++; - gargnum++; - cx++; - break; - case 'I': - ReleaseIArray((uint *)garglist[gargnum]._array, varglist[ix], varglist[ix+1], passout); - gargnum++; - ix++; - gargnum++; - cx++; - break; - case 'Q': - ReleasePtrArray((void **)garglist[gargnum]._array, varglist[ix], varglist[ix+1], (*cx-'a'), passout); - gargnum++; - ix++; - gargnum++; - cx++; - break; - default: - error("Illegal format string."); - break; - } - } - else { - /* a plain value or a reference to one. */ - - if (isreturn || (depth > 0 && subpassout) || (isref && passout)) { - skipval = false; - } - else { - skipval = true; - } - - switch (typeclass) { - case 'I': - if (!skipval) { - if (*cx == 'u') - thisval = (uint)garglist[gargnum]._uint; - else if (*cx == 's') - thisval = (uint)garglist[gargnum]._sint; - else - error("Illegal format string."); - } - gargnum++; - cx++; - break; - case 'Q': - if (!skipval) { - opref = garglist[gargnum]._opaqueref; - if (opref) { - gidispatch_rock_t objrock = gidispatch_get_objrock(opref, *cx - 'a'); - thisval = ((classref_t *)objrock.ptr)->id; - } - else { - thisval = 0; - } - } - gargnum++; - cx++; - break; - case 'C': - if (!skipval) { - if (*cx == 'u') - thisval = (uint)garglist[gargnum]._uch; - else if (*cx == 's') - thisval = (uint)garglist[gargnum]._sch; - else if (*cx == 'n') - thisval = (uint)garglist[gargnum]._ch; - else - error("Illegal format string."); - } - gargnum++; - cx++; - break; - case 'S': - if (garglist[gargnum]._charstr) - ReleaseVMString(garglist[gargnum]._charstr); - gargnum++; - break; + int *argnumptr, uint subaddress, int subpassout) { + const char *cx; + int ix, argx; + int gargnum, numwanted; + void *opref; + gluniversal_t *garglist; + uint *varglist; + + garglist = splot->garglist; + varglist = splot->varglist; + gargnum = *argnumptr; + cx = *proto; + + numwanted = 0; + while (*cx >= '0' && *cx <= '9') { + numwanted = 10 * numwanted + (*cx - '0'); + cx++; + } + + for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) { + char typeclass; + int skipval; + int isref, passin, passout, nullok, isarray, isretained, isreturn; + cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, + &isretained, &isreturn); + + typeclass = *cx; + cx++; + + skipval = false; + if (isref) { + if (!isreturn && varglist[ix] == 0) { + if (!nullok) + error("Zero passed invalidly to Glk function."); + garglist[gargnum]._ptrflag = false; + gargnum++; + skipval = true; + } else { + garglist[gargnum]._ptrflag = true; + gargnum++; + } + } + if (!skipval) { + uint thisval = 0; + + if (typeclass == '[') { + + unparse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passout); + + } else if (isarray) { + /* definitely isref */ + + switch (typeclass) { + case 'C': + ReleaseCArray((char *)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], passout); + gargnum++; + ix++; + gargnum++; + cx++; + break; + case 'I': + ReleaseIArray((uint *)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], passout); + gargnum++; + ix++; + gargnum++; + cx++; + break; + case 'Q': + ReleasePtrArray((void **)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], (*cx - 'a'), passout); + gargnum++; + ix++; + gargnum++; + cx++; + break; + default: + error("Illegal format string."); + break; + } + } else { + /* a plain value or a reference to one. */ + + if (isreturn || (depth > 0 && subpassout) || (isref && passout)) { + skipval = false; + } else { + skipval = true; + } + + switch (typeclass) { + case 'I': + if (!skipval) { + if (*cx == 'u') + thisval = (uint)garglist[gargnum]._uint; + else if (*cx == 's') + thisval = (uint)garglist[gargnum]._sint; + else + error("Illegal format string."); + } + gargnum++; + cx++; + break; + case 'Q': + if (!skipval) { + opref = garglist[gargnum]._opaqueref; + if (opref) { + gidispatch_rock_t objrock = gidispatch_get_objrock(opref, *cx - 'a'); + thisval = ((classref_t *)objrock.ptr)->id; + } else { + thisval = 0; + } + } + gargnum++; + cx++; + break; + case 'C': + if (!skipval) { + if (*cx == 'u') + thisval = (uint)garglist[gargnum]._uch; + else if (*cx == 's') + thisval = (uint)garglist[gargnum]._sch; + else if (*cx == 'n') + thisval = (uint)garglist[gargnum]._ch; + else + error("Illegal format string."); + } + gargnum++; + cx++; + break; + case 'S': + if (garglist[gargnum]._charstr) + ReleaseVMString(garglist[gargnum]._charstr); + gargnum++; + break; #ifdef GLK_MODULE_UNICODE - case 'U': - if (garglist[gargnum]._unicharstr) - ReleaseVMUstring(garglist[gargnum]._unicharstr); - gargnum++; - break; + case 'U': + if (garglist[gargnum]._unicharstr) + ReleaseVMUstring(garglist[gargnum]._unicharstr); + gargnum++; + break; #endif /* GLK_MODULE_UNICODE */ - default: - error("Illegal format string."); - break; - } - - if (isreturn) { - *(splot->retval) = thisval; - } - else if (depth > 0) { - /* Definitely not isref or isarray. */ - if (subpassout) - WriteStructField(subaddress, ix, thisval); - } - else if (isref) { - if (passout) - WriteMemory(varglist[ix], thisval); - } - } - } - else { - /* We got a null reference, so we have to skip the format element. */ - if (typeclass == '[') { - int numsubwanted, refdepth; - numsubwanted = 0; - while (*cx >= '0' && *cx <= '9') { - numsubwanted = 10 * numsubwanted + (*cx - '0'); - cx++; - } - refdepth = 1; - while (refdepth > 0) { - if (*cx == '[') - refdepth++; - else if (*cx == ']') - refdepth--; - cx++; - } - } - else if (typeclass == 'S' || typeclass == 'U') { - /* leave it */ - } - else { - cx++; - if (isarray) - ix++; - } - } - } - - if (depth > 0) { - if (*cx != ']') - error("Illegal format string."); - cx++; - } - else { - if (*cx != ':' && *cx != '\0') - error("Illegal format string."); - } - - *proto = cx; - *argnumptr = gargnum; + default: + error("Illegal format string."); + break; + } + + if (isreturn) { + *(splot->retval) = thisval; + } else if (depth > 0) { + /* Definitely not isref or isarray. */ + if (subpassout) + WriteStructField(subaddress, ix, thisval); + } else if (isref) { + if (passout) + WriteMemory(varglist[ix], thisval); + } + } + } else { + /* We got a null reference, so we have to skip the format element. */ + if (typeclass == '[') { + int numsubwanted, refdepth; + numsubwanted = 0; + while (*cx >= '0' && *cx <= '9') { + numsubwanted = 10 * numsubwanted + (*cx - '0'); + cx++; + } + refdepth = 1; + while (refdepth > 0) { + if (*cx == '[') + refdepth++; + else if (*cx == ']') + refdepth--; + cx++; + } + } else if (typeclass == 'S' || typeclass == 'U') { + /* leave it */ + } else { + cx++; + if (isarray) + ix++; + } + } + } + + if (depth > 0) { + if (*cx != ']') + error("Illegal format string."); + cx++; + } else { + if (*cx != ':' && *cx != '\0') + error("Illegal format string."); + } + + *proto = cx; + *argnumptr = gargnum; } strid_t Glulxe::find_stream_by_id(uint objid) { - if (!objid) - return nullptr; + if (!objid) + return nullptr; - // Recall that class 1 ("b") is streams - return (strid_t)classes_get(gidisp_Class_Stream, objid); + // Recall that class 1 ("b") is streams + return (strid_t)classes_get(gidisp_Class_Stream, objid); } uint Glulxe::find_id_for_window(winid_t win) { - gidispatch_rock_t objrock; + gidispatch_rock_t objrock; - if (!win) - return 0; + if (!win) + return 0; - objrock = gidispatch_get_objrock(win, gidisp_Class_Window); - if (!objrock.ptr) - return 0; - return ((classref_t *)objrock.ptr)->id; + objrock = gidispatch_get_objrock(win, gidisp_Class_Window); + if (!objrock.ptr) + return 0; + return ((classref_t *)objrock.ptr)->id; } uint Glulxe::find_id_for_stream(strid_t str) { - gidispatch_rock_t objrock; + gidispatch_rock_t objrock; - if (!str) - return 0; + if (!str) + return 0; - objrock = gidispatch_get_objrock(str, gidisp_Class_Stream); - if (!objrock.ptr) - return 0; - return ((classref_t *)objrock.ptr)->id; + objrock = gidispatch_get_objrock(str, gidisp_Class_Stream); + if (!objrock.ptr) + return 0; + return ((classref_t *)objrock.ptr)->id; } uint Glulxe::find_id_for_fileref(frefid_t fref) { - gidispatch_rock_t objrock; + gidispatch_rock_t objrock; - if (!fref) - return 0; + if (!fref) + return 0; - objrock = gidispatch_get_objrock(fref, gidisp_Class_Fileref); - if (!objrock.ptr) - return 0; - return ((classref_t *)objrock.ptr)->id; + objrock = gidispatch_get_objrock(fref, gidisp_Class_Fileref); + if (!objrock.ptr) + return 0; + return ((classref_t *)objrock.ptr)->id; } uint Glulxe::find_id_for_schannel(schanid_t schan) { - gidispatch_rock_t objrock; + gidispatch_rock_t objrock; - if (!schan) - return 0; + if (!schan) + return 0; - objrock = gidispatch_get_objrock(schan, gidisp_Class_Schannel); - if (!objrock.ptr) - return 0; - return ((classref_t *)objrock.ptr)->id; + objrock = gidispatch_get_objrock(schan, gidisp_Class_Schannel); + if (!objrock.ptr) + return 0; + return ((classref_t *)objrock.ptr)->id; } classtable_t *Glulxe::new_classtable(uint firstid) { - int ix; - classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t)); - if (!ctab) - return nullptr; - - for (ix=0; ix<CLASSHASH_SIZE; ix++) - ctab->bucket[ix] = nullptr; - - ctab->lastid = firstid; - - return ctab; + int ix; + classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t)); + if (!ctab) + return nullptr; + + for (ix = 0; ix < CLASSHASH_SIZE; ix++) + ctab->bucket[ix] = nullptr; + + ctab->lastid = firstid; + + return ctab; } void *Glulxe::classes_get(int classid, uint objid) { - classtable_t *ctab; - classref_t *cref; - if (classid < 0 || classid >= num_classes) - return nullptr; - ctab = classes[classid]; - cref = ctab->bucket[objid % CLASSHASH_SIZE]; - for (; cref; cref = cref->next) { - if (cref->id == objid) - return cref->obj; - } - return nullptr; + classtable_t *ctab; + classref_t *cref; + if (classid < 0 || classid >= num_classes) + return nullptr; + ctab = classes[classid]; + cref = ctab->bucket[objid % CLASSHASH_SIZE]; + for (; cref; cref = cref->next) { + if (cref->id == objid) + return cref->obj; + } + return nullptr; } classref_t *Glulxe::classes_put(int classid, void *obj, uint origid) { - int bucknum; - classtable_t *ctab; - classref_t *cref; - if (classid < 0 || classid >= num_classes) - return nullptr; - ctab = classes[classid]; - cref = (classref_t *)glulx_malloc(sizeof(classref_t)); - if (!cref) - return nullptr; - cref->obj = obj; - if (!origid) { - cref->id = ctab->lastid; - ctab->lastid++; - } - else { - cref->id = origid; - if (ctab->lastid <= origid) - ctab->lastid = origid+1; - } - bucknum = cref->id % CLASSHASH_SIZE; - cref->bucknum = bucknum; - cref->next = ctab->bucket[bucknum]; - ctab->bucket[bucknum] = cref; - return cref; + int bucknum; + classtable_t *ctab; + classref_t *cref; + if (classid < 0 || classid >= num_classes) + return nullptr; + ctab = classes[classid]; + cref = (classref_t *)glulx_malloc(sizeof(classref_t)); + if (!cref) + return nullptr; + cref->obj = obj; + if (!origid) { + cref->id = ctab->lastid; + ctab->lastid++; + } else { + cref->id = origid; + if (ctab->lastid <= origid) + ctab->lastid = origid + 1; + } + bucknum = cref->id % CLASSHASH_SIZE; + cref->bucknum = bucknum; + cref->next = ctab->bucket[bucknum]; + ctab->bucket[bucknum] = cref; + return cref; } -void Glulxe::classes_remove(int classid, void *obj) -{ - classtable_t *ctab; - classref_t *cref; - classref_t **crefp; - gidispatch_rock_t objrock; - if (classid < 0 || classid >= num_classes) - return; - ctab = classes[classid]; - objrock = gidispatch_get_objrock(obj, classid); - cref = (classref_t *)objrock.ptr; - if (!cref) - return; - crefp = &(ctab->bucket[cref->bucknum]); - for (; *crefp; crefp = &((*crefp)->next)) { - if ((*crefp) == cref) { - *crefp = cref->next; - if (!cref->obj) { - nonfatal_warning("attempt to free nullptr object!"); - } - cref->obj = nullptr; - cref->id = 0; - cref->next = nullptr; - glulx_free(cref); - return; - } - } - return; +void Glulxe::classes_remove(int classid, void *obj) { + classtable_t *ctab; + classref_t *cref; + classref_t **crefp; + gidispatch_rock_t objrock; + if (classid < 0 || classid >= num_classes) + return; + ctab = classes[classid]; + objrock = gidispatch_get_objrock(obj, classid); + cref = (classref_t *)objrock.ptr; + if (!cref) + return; + crefp = &(ctab->bucket[cref->bucknum]); + for (; *crefp; crefp = &((*crefp)->next)) { + if ((*crefp) == cref) { + *crefp = cref->next; + if (!cref->obj) { + nonfatal_warning("attempt to free nullptr object!"); + } + cref->obj = nullptr; + cref->id = 0; + cref->next = nullptr; + glulx_free(cref); + return; + } + } + return; } gidispatch_rock_t Glulxe::glulxe_classtable_register(void *obj, uint objclass) { - classref_t *cref; - gidispatch_rock_t objrock; - cref = classes_put(objclass, obj, 0); - objrock.ptr = cref; - return objrock; + classref_t *cref; + gidispatch_rock_t objrock; + cref = classes_put(objclass, obj, 0); + objrock.ptr = cref; + return objrock; } -void Glulxe::glulxe_classtable_unregister(void *obj, uint objclass, - gidispatch_rock_t objrock) { - classes_remove(objclass, obj); +void Glulxe::glulxe_classtable_unregister(void *obj, uint objclass, + gidispatch_rock_t objrock) { + classes_remove(objclass, obj); } gidispatch_rock_t Glulxe::glulxe_classtable_register_existing(void *obj, uint objclass, uint dispid) { - classref_t *cref; - gidispatch_rock_t objrock; - cref = classes_put(objclass, obj, dispid); - objrock.ptr = cref; - return objrock; + classref_t *cref; + gidispatch_rock_t objrock; + cref = classes_put(objclass, obj, dispid); + objrock.ptr = cref; + return objrock; } char *Glulxe::grab_temp_c_array(uint addr, uint len, int passin) { - arrayref_t *arref = nullptr; - char *arr = nullptr; - uint ix, addr2; - - if (len) { - arr = (char *)glulx_malloc(len * sizeof(char)); - arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t)); - if (!arr || !arref) - error("Unable to allocate space for array argument to Glk call."); - - arref->array = arr; - arref->addr = addr; - arref->elemsize = 1; - arref->retained = false; - arref->len = len; - arref->next = arrays; - arrays = arref; - - if (passin) { - for (ix=0, addr2=addr; ix<len; ix++, addr2+=1) { - arr[ix] = Mem1(addr2); - } - } - } - - return arr; + arrayref_t *arref = nullptr; + char *arr = nullptr; + uint ix, addr2; + + if (len) { + arr = (char *)glulx_malloc(len * sizeof(char)); + arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t)); + if (!arr || !arref) + error("Unable to allocate space for array argument to Glk call."); + + arref->array = arr; + arref->addr = addr; + arref->elemsize = 1; + arref->retained = false; + arref->len = len; + arref->next = arrays; + arrays = arref; + + if (passin) { + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) { + arr[ix] = Mem1(addr2); + } + } + } + + return arr; } void Glulxe::release_temp_c_array(char *arr, uint addr, uint len, int passout) { - arrayref_t *arref = nullptr; - arrayref_t **aptr; - uint ix, val, addr2; - - if (arr) { - for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { - if ((*aptr)->array == arr) - break; - } - arref = *aptr; - if (!arref) - error("Unable to re-find array argument in Glk call."); - if (arref->addr != addr || arref->len != len) - error("Mismatched array argument in Glk call."); - - if (arref->retained) { - return; - } - - *aptr = arref->next; - arref->next = nullptr; - - if (passout) { - for (ix=0, addr2=addr; ix<len; ix++, addr2+=1) { - val = arr[ix]; - MemW1(addr2, val); - } - } - glulx_free(arr); - glulx_free(arref); - } + arrayref_t *arref = nullptr; + arrayref_t **aptr; + uint ix, val, addr2; + + if (arr) { + for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { + if ((*aptr)->array == arr) + break; + } + arref = *aptr; + if (!arref) + error("Unable to re-find array argument in Glk call."); + if (arref->addr != addr || arref->len != len) + error("Mismatched array argument in Glk call."); + + if (arref->retained) { + return; + } + + *aptr = arref->next; + arref->next = nullptr; + + if (passout) { + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) { + val = arr[ix]; + MemW1(addr2, val); + } + } + glulx_free(arr); + glulx_free(arref); + } } uint *Glulxe::grab_temp_i_array(uint addr, uint len, int passin) { - arrayref_t *arref = nullptr; - uint *arr = nullptr; - uint ix, addr2; - - if (len) { - arr = (uint *)glulx_malloc(len * sizeof(uint)); - arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t)); - if (!arr || !arref) - error("Unable to allocate space for array argument to Glk call."); - - arref->array = arr; - arref->addr = addr; - arref->elemsize = 4; - arref->retained = false; - arref->len = len; - arref->next = arrays; - arrays = arref; - - if (passin) { - for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { - arr[ix] = Mem4(addr2); - } - } - } - - return arr; + arrayref_t *arref = nullptr; + uint *arr = nullptr; + uint ix, addr2; + + if (len) { + arr = (uint *)glulx_malloc(len * sizeof(uint)); + arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t)); + if (!arr || !arref) + error("Unable to allocate space for array argument to Glk call."); + + arref->array = arr; + arref->addr = addr; + arref->elemsize = 4; + arref->retained = false; + arref->len = len; + arref->next = arrays; + arrays = arref; + + if (passin) { + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) { + arr[ix] = Mem4(addr2); + } + } + } + + return arr; } void Glulxe::release_temp_i_array(uint *arr, uint addr, uint len, int passout) { - arrayref_t *arref = nullptr; - arrayref_t **aptr; - uint ix, val, addr2; - - if (arr) { - for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { - if ((*aptr)->array == arr) - break; - } - arref = *aptr; - if (!arref) - error("Unable to re-find array argument in Glk call."); - if (arref->addr != addr || arref->len != len) - error("Mismatched array argument in Glk call."); - - if (arref->retained) { - return; - } - - *aptr = arref->next; - arref->next = nullptr; - - if (passout) { - for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { - val = arr[ix]; - MemW4(addr2, val); - } - } - glulx_free(arr); - glulx_free(arref); - } + arrayref_t *arref = nullptr; + arrayref_t **aptr; + uint ix, val, addr2; + + if (arr) { + for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { + if ((*aptr)->array == arr) + break; + } + arref = *aptr; + if (!arref) + error("Unable to re-find array argument in Glk call."); + if (arref->addr != addr || arref->len != len) + error("Mismatched array argument in Glk call."); + + if (arref->retained) { + return; + } + + *aptr = arref->next; + arref->next = nullptr; + + if (passout) { + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) { + val = arr[ix]; + MemW4(addr2, val); + } + } + glulx_free(arr); + glulx_free(arref); + } } void **Glulxe::grab_temp_ptr_array(uint addr, uint len, int objclass, int passin) { - arrayref_t *arref = nullptr; - void **arr = nullptr; - uint ix, addr2; - - if (len) { - arr = (void **)glulx_malloc(len * sizeof(void *)); - arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t)); - if (!arr || !arref) - error("Unable to allocate space for array argument to Glk call."); - - arref->array = arr; - arref->addr = addr; - arref->elemsize = sizeof(void *); - arref->retained = false; - arref->len = len; - arref->next = arrays; - arrays = arref; - - if (passin) { - for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { - uint thisval = Mem4(addr2); - if (thisval) - arr[ix] = classes_get(objclass, thisval); - else - arr[ix] = nullptr; - } - } - } - - return arr; + arrayref_t *arref = nullptr; + void **arr = nullptr; + uint ix, addr2; + + if (len) { + arr = (void **)glulx_malloc(len * sizeof(void *)); + arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t)); + if (!arr || !arref) + error("Unable to allocate space for array argument to Glk call."); + + arref->array = arr; + arref->addr = addr; + arref->elemsize = sizeof(void *); + arref->retained = false; + arref->len = len; + arref->next = arrays; + arrays = arref; + + if (passin) { + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) { + uint thisval = Mem4(addr2); + if (thisval) + arr[ix] = classes_get(objclass, thisval); + else + arr[ix] = nullptr; + } + } + } + + return arr; } void Glulxe::release_temp_ptr_array(void **arr, uint addr, uint len, int objclass, int passout) { - arrayref_t *arref = nullptr; - arrayref_t **aptr; - uint ix, val, addr2; - - if (arr) { - for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { - if ((*aptr)->array == arr) - break; - } - arref = *aptr; - if (!arref) - error("Unable to re-find array argument in Glk call."); - if (arref->addr != addr || arref->len != len) - error("Mismatched array argument in Glk call."); - - if (arref->retained) { - return; - } - - *aptr = arref->next; - arref->next = nullptr; - - if (passout) { - for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { - void *opref = arr[ix]; - if (opref) { - gidispatch_rock_t objrock = - gidispatch_get_objrock(opref, objclass); - val = ((classref_t *)objrock.ptr)->id; - } - else { - val = 0; - } - MemW4(addr2, val); - } - } - glulx_free(arr); - glulx_free(arref); - } + arrayref_t *arref = nullptr; + arrayref_t **aptr; + uint ix, val, addr2; + + if (arr) { + for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { + if ((*aptr)->array == arr) + break; + } + arref = *aptr; + if (!arref) + error("Unable to re-find array argument in Glk call."); + if (arref->addr != addr || arref->len != len) + error("Mismatched array argument in Glk call."); + + if (arref->retained) { + return; + } + + *aptr = arref->next; + arref->next = nullptr; + + if (passout) { + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) { + void *opref = arr[ix]; + if (opref) { + gidispatch_rock_t objrock = + gidispatch_get_objrock(opref, objclass); + val = ((classref_t *)objrock.ptr)->id; + } else { + val = 0; + } + MemW4(addr2, val); + } + } + glulx_free(arr); + glulx_free(arref); + } } gidispatch_rock_t Glulxe::glulxe_retained_register(void *array, uint len, char *typecode) { - gidispatch_rock_t rock; - arrayref_t *arref = nullptr; - arrayref_t **aptr; - uint elemsize = 0; - - if (typecode[4] == 'C') - elemsize = 1; - else if (typecode[4] == 'I') - elemsize = 4; - - if (!elemsize || array == nullptr) { - rock.ptr = nullptr; - return rock; - } - - for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { - if ((*aptr)->array == array) - break; - } - arref = *aptr; - if (!arref) - error("Unable to re-find array argument in Glk call."); - if (arref->elemsize != elemsize || arref->len != len) - error("Mismatched array argument in Glk call."); - - arref->retained = true; - - rock.ptr = arref; - return rock; + gidispatch_rock_t rock; + arrayref_t *arref = nullptr; + arrayref_t **aptr; + uint elemsize = 0; + + if (typecode[4] == 'C') + elemsize = 1; + else if (typecode[4] == 'I') + elemsize = 4; + + if (!elemsize || array == nullptr) { + rock.ptr = nullptr; + return rock; + } + + for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { + if ((*aptr)->array == array) + break; + } + arref = *aptr; + if (!arref) + error("Unable to re-find array argument in Glk call."); + if (arref->elemsize != elemsize || arref->len != len) + error("Mismatched array argument in Glk call."); + + arref->retained = true; + + rock.ptr = arref; + return rock; } void Glulxe::glulxe_retained_unregister(void *array, uint len, char *typecode, gidispatch_rock_t objrock) { - arrayref_t *arref = nullptr; - arrayref_t **aptr; - uint ix, addr2, val; - uint elemsize = 0; - - if (typecode[4] == 'C') - elemsize = 1; - else if (typecode[4] == 'I') - elemsize = 4; - - if (!elemsize || array == nullptr) { - return; - } - - for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { - if ((*aptr)->array == array) - break; - } - arref = *aptr; - if (!arref) - error("Unable to re-find array argument in Glk call."); - if (arref != objrock.ptr) - error("Mismatched array reference in Glk call."); - if (!arref->retained) - error("Unretained array reference in Glk call."); - if (arref->elemsize != elemsize || arref->len != len) - error("Mismatched array argument in Glk call."); - - *aptr = arref->next; - arref->next = nullptr; - - if (elemsize == 1) { - for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=1) { - val = ((char *)array)[ix]; - MemW1(addr2, val); - } - } - else if (elemsize == 4) { - for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=4) { - val = ((uint *)array)[ix]; - MemW4(addr2, val); - } - } - - glulx_free(array); - glulx_free(arref); + arrayref_t *arref = nullptr; + arrayref_t **aptr; + uint ix, addr2, val; + uint elemsize = 0; + + if (typecode[4] == 'C') + elemsize = 1; + else if (typecode[4] == 'I') + elemsize = 4; + + if (!elemsize || array == nullptr) { + return; + } + + for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { + if ((*aptr)->array == array) + break; + } + arref = *aptr; + if (!arref) + error("Unable to re-find array argument in Glk call."); + if (arref != objrock.ptr) + error("Mismatched array reference in Glk call."); + if (!arref->retained) + error("Unretained array reference in Glk call."); + if (arref->elemsize != elemsize || arref->len != len) + error("Mismatched array argument in Glk call."); + + *aptr = arref->next; + arref->next = nullptr; + + if (elemsize == 1) { + for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 1) { + val = ((char *)array)[ix]; + MemW1(addr2, val); + } + } else if (elemsize == 4) { + for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 4) { + val = ((uint *)array)[ix]; + MemW4(addr2, val); + } + } + + glulx_free(array); + glulx_free(arref); } long Glulxe::glulxe_array_locate(void *array, uint len, char *typecode, gidispatch_rock_t objrock, int *elemsizeref) { - arrayref_t *arref = nullptr; - arrayref_t **aptr; - uint elemsize = 0; - - if (typecode[4] == 'C') - elemsize = 1; - else if (typecode[4] == 'I') - elemsize = 4; - - if (!elemsize || array == nullptr) { - *elemsizeref = 0; /* No need to save the array separately */ - return (unsigned char *)array - memmap; - } - - for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) { - if ((*aptr)->array == array) - break; - } - arref = *aptr; - if (!arref) - error("Unable to re-find array argument in array_locate."); - if (arref != objrock.ptr) - error("Mismatched array reference in array_locate."); - if (!arref->retained) - error("Unretained array reference in array_locate."); - if (arref->elemsize != elemsize || arref->len != len) - error("Mismatched array argument in array_locate."); - - *elemsizeref = arref->elemsize; - return arref->addr; + arrayref_t *arref = nullptr; + arrayref_t **aptr; + uint elemsize = 0; + + if (typecode[4] == 'C') + elemsize = 1; + else if (typecode[4] == 'I') + elemsize = 4; + + if (!elemsize || array == nullptr) { + *elemsizeref = 0; /* No need to save the array separately */ + return (unsigned char *)array - memmap; + } + + for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) { + if ((*aptr)->array == array) + break; + } + arref = *aptr; + if (!arref) + error("Unable to re-find array argument in array_locate."); + if (arref != objrock.ptr) + error("Mismatched array reference in array_locate."); + if (!arref->retained) + error("Unretained array reference in array_locate."); + if (arref->elemsize != elemsize || arref->len != len) + error("Mismatched array argument in array_locate."); + + *elemsizeref = arref->elemsize; + return arref->addr; } gidispatch_rock_t Glulxe::glulxe_array_restore(long bufkey, uint len, char *typecode, void **arrayref) { - gidispatch_rock_t rock; - int elemsize = 0; - - if (typecode[4] == 'C') - elemsize = 1; - else if (typecode[4] == 'I') - elemsize = 4; - - if (!elemsize) { - unsigned char *buf = memmap + bufkey; - *arrayref = buf; - rock.ptr = nullptr; - return rock; - } - - if (elemsize == 1) { - char *cbuf = grab_temp_c_array(bufkey, len, false); - rock = glulxe_retained_register(cbuf, len, typecode); - *arrayref = cbuf; - } - else { - uint *ubuf = grab_temp_i_array(bufkey, len, false); - rock = glulxe_retained_register(ubuf, len, typecode); - *arrayref = ubuf; - } - return rock; + gidispatch_rock_t rock; + int elemsize = 0; + + if (typecode[4] == 'C') + elemsize = 1; + else if (typecode[4] == 'I') + elemsize = 4; + + if (!elemsize) { + unsigned char *buf = memmap + bufkey; + *arrayref = buf; + rock.ptr = nullptr; + return rock; + } + + if (elemsize == 1) { + char *cbuf = grab_temp_c_array(bufkey, len, false); + rock = glulxe_retained_register(cbuf, len, typecode); + *arrayref = cbuf; + } else { + uint *ubuf = grab_temp_i_array(bufkey, len, false); + rock = glulxe_retained_register(ubuf, len, typecode); + *arrayref = ubuf; + } + return rock; } void Glulxe::set_library_select_hook(void (*func)(uint)) { - library_select_hook = func; + library_select_hook = func; } char *Glulxe::get_game_id() { - /* This buffer gets rewritten on every call, but that's okay -- the caller - is supposed to copy out the result. */ - static char buf[2*64+2]; - int ix, jx; - - if (!memmap) - return nullptr; - - for (ix=0, jx=0; ix<64; ix++) { - char ch = memmap[ix]; - int val = ((ch >> 4) & 0x0F); - buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10)); - val = (ch & 0x0F); - buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10)); - } - buf[jx++] = '\0'; - - return buf; + /* This buffer gets rewritten on every call, but that's okay -- the caller + is supposed to copy out the result. */ + static char buf[2 * 64 + 2]; + int ix, jx; + + if (!memmap) + return nullptr; + + for (ix = 0, jx = 0; ix < 64; ix++) { + char ch = memmap[ix]; + int val = ((ch >> 4) & 0x0F); + buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10)); + val = (ch & 0x0F); + buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10)); + } + buf[jx++] = '\0'; + + return buf; } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp index 5d4420b203..a78e22cea4 100644 --- a/engines/glk/glulxe/glulxe.cpp +++ b/engines/glk/glulxe/glulxe.cpp @@ -30,23 +30,23 @@ namespace Glulxe { Glulxe *g_vm; Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), _random("glulxe"), - vm_exited_cleanly(false), gamefile_start(0), gamefile_len(0), memmap(nullptr), stack(nullptr), - ramstart(0), endgamefile(0), origendmem(0), stacksize(0), startfuncaddr(0), checksum(0), - stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0), stringtable(0), valstackbase(0), - localsbase(0), endmem(0), protectstart(0), protectend(0), - stream_char_handler(nullptr), stream_unichar_handler(nullptr), - // main - library_autorestore_hook(nullptr), - // accel - classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0), - routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0), - accelentries(nullptr), - // heap - heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr), - // serial - max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr), - // string - iosys_mode(0), iosys_rock(0), tablecache_valid(false), glkio_unichar_han_ptr(nullptr) { + vm_exited_cleanly(false), gamefile_start(0), gamefile_len(0), memmap(nullptr), stack(nullptr), + ramstart(0), endgamefile(0), origendmem(0), stacksize(0), startfuncaddr(0), checksum(0), + stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0), stringtable(0), valstackbase(0), + localsbase(0), endmem(0), protectstart(0), protectend(0), + stream_char_handler(nullptr), stream_unichar_handler(nullptr), + // main + library_autorestore_hook(nullptr), + // accel + classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0), + routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0), + accelentries(nullptr), + // heap + heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr), + // serial + max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr), + // string + iosys_mode(0), iosys_rock(0), tablecache_valid(false), glkio_unichar_han_ptr(nullptr) { g_vm = this; } diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h index d9003c851e..b985beb7c4 100644 --- a/engines/glk/glulxe/glulxe.h +++ b/engines/glk/glulxe/glulxe.h @@ -78,10 +78,10 @@ private: * @{ */ - /** - * The library_autorestore_hook is called right after the VM's initial setup. This is an appropriate time - * to autorestore an initial game state, if the library has that capability. (Currently, only iosglk does.) - */ + /** + * The library_autorestore_hook is called right after the VM's initial setup. This is an appropriate time + * to autorestore an initial game state, if the library has that capability. (Currently, only iosglk does.) + */ void(*library_autorestore_hook)(void); Common::RandomSource _random; @@ -110,7 +110,7 @@ private: * @{ */ - uint heap_start; ///< zero for inactive heap + uint heap_start; ///< zero for inactive heap int alloc_count; /* The heap_head/heap_tail is a doubly-linked list of blocks, both @@ -151,9 +151,9 @@ private: * @{ */ - /** - * This can be adjusted before startup by platform-specific startup code -- that is, preference code. - */ + /** + * This can be adjusted before startup by platform-specific startup code -- that is, preference code. + */ int max_undo_level; int undo_chain_size; @@ -182,7 +182,7 @@ private: cacheblock_t tablecache; /* This misbehaves if a Glk function has more than one S argument. */ - #define STATIC_TEMP_BUFSIZE (127) +#define STATIC_TEMP_BUFSIZE (127) char temp_buf[STATIC_TEMP_BUFSIZE + 1]; /**@}*/ @@ -253,7 +253,7 @@ protected: */ uint get_prop_new(uint obj, uint id); - /**@}*/ + /**@}*/ /** * \defgroup glkop support methods @@ -302,12 +302,12 @@ protected: * to deal with structures. */ void parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr, uint subaddress, int subpassin); - + /** * This is about the reverse of parse_glk_args(). */ void unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, - int *argnumptr, uint subaddress, int subpassout); + int *argnumptr, uint subaddress, int subpassout); /** * Create a string identifying this game. We use the first 64 bytes of the memory map, encoded as hex, @@ -389,7 +389,9 @@ public: /** * Returns the running interpreter type */ - virtual InterpreterType getInterpreterType() const override { return INTERPRETER_GLULXE; } + virtual InterpreterType getInterpreterType() const override { + return INTERPRETER_GLULXE; + } /** * Load a savegame from the passed stream @@ -427,9 +429,9 @@ public: * @{ */ - /** - * Validates the game file, and if it's invalid, displays an error dialog - */ + /** + * Validates the game file, and if it's invalid, displays an error dialog + */ bool is_gamefile_valid(); /**@}*/ @@ -439,16 +441,16 @@ public: * @{ */ - /** - * Read in the game file and build the machine, allocating all the memory necessary. - */ + /** + * Read in the game file and build the machine, allocating all the memory necessary. + */ void setup_vm(); /** * Deallocate all the memory and shut down the machine. */ void finalize_vm(); - + /** * Put the VM into a state where it's ready to begin executing the game. This is called * both at startup time, and when the machine performs a "restart" opcode. @@ -461,14 +463,14 @@ public: * when the heap-allocation system is calling. Returns 0 for success; otherwise, the operation failed. */ uint change_memsize(uint newlen, bool internal); - + /** * If addr is 0, pop N arguments off the stack, and put them in an array. If non-0, take N arguments * from that main memory address instead. This has to dynamically allocate if there are more than * 32 arguments, but that shouldn't be a problem. */ uint *pop_arguments(uint count, uint addr); - + /** * Make sure that count bytes beginning with addr all fall within the current memory map. * This is called at every memory (read) access if VERIFY_MEMORY_ACCESS is defined in the header file. @@ -495,9 +497,9 @@ public: * @{ */ - /** - * The main interpreter loop. This repeats until the program is done - */ + /** + * The main interpreter loop. This repeats until the program is done + */ void execute_loop(); /**@}*/ @@ -507,9 +509,9 @@ public: * @{ */ - /** - * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up. - */ + /** + * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up. + */ void init_operands(); /** @@ -549,7 +551,7 @@ public: * Note that if argc is zero, argv may be nullptr. */ void enter_function(uint addr, uint argc, uint *argv); - + /** * Pop the current call frame off the stack. This is very simple. */ @@ -610,11 +612,11 @@ public: /** * Create an array of words, in the VM serialization format: * - * heap_start - * alloc_count - * addr of first block - * len of first block - * ... + * heap_start + * alloc_count + * addr of first block + * len of first block + * ... * * (Note that these are uint values -- native byte ordering. Also, the blocks will be in address order, * which is a stricter guarantee than the VM specifies; that'll help in heap_apply_summary().) @@ -649,19 +651,19 @@ public: */ - /** - * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes. - * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from - * the start of the structure.) Search through these in order. If one is found whose key matches, return it. - * If numstructs are searched with no result, return nullptr. - * - * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search. - * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key. - * - * The KeyIndirect, ZeroKeyTerminates, and ReturnIndex options may be used. - */ + /** + * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes. + * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from + * the start of the structure.) Search through these in order. If one is found whose key matches, return it. + * If numstructs are searched with no result, return nullptr. + * + * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search. + * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key. + * + * The KeyIndirect, ZeroKeyTerminates, and ReturnIndex options may be used. + */ uint linear_search(uint key, uint keysize, uint start, uint structsize, uint numstructs, - uint keyoffset, uint options); + uint keyoffset, uint options); /** * An array of data structures is in memory, as above. However, the structs must be stored in forward @@ -671,7 +673,7 @@ public: * The KeyIndirect and ReturnIndex options may be used. */ uint binary_search(uint key, uint keysize, uint start, uint structsize, uint numstructs, - uint keyoffset, uint options); + uint keyoffset, uint options); /** * The structures may be anywhere in memory, in any order. They are linked by a four-byte address field, @@ -723,9 +725,9 @@ public: * @{ */ - /** - * glkop section initialization - */ + /** + * glkop section initialization + */ void glkopInit(); void set_library_select_hook(void(*func)(uint)); @@ -754,7 +756,7 @@ public: * Read the prefixes of an argument string -- the "<>&+:#!" chars. */ const char *read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout, - int *nullok, int *isretained, int *isreturn); + int *nullok, int *isretained, int *isreturn); /** * This is used by some interpreter code which has to, well, find a Glk stream given its ID. @@ -868,9 +870,11 @@ public: that it can reinterpret-cast IEEE-754 int values into gfloat32 values. If you uncomment this, Glulxe switches to lengthier (but safer) encoding and decoding functions. */ - /* #define FLOAT_NOT_NATIVE (1) */ + /* #define FLOAT_NOT_NATIVE (1) */ - int init_float() { return true; } + int init_float() { + return true; + } /** * Encode floats by a lot of annoying bit manipulation. @@ -887,7 +891,7 @@ public: /* Uncomment this definition if your powf() function does not support all the corner cases specified by C99. If you uncomment this, osdepend.c will provide a safer implementation of glulx_powf(). */ - /* #define FLOAT_COMPILE_SAFER_POWF (1) */ + /* #define FLOAT_COMPILE_SAFER_POWF (1) */ inline gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2) const { return powf(val1, val2); @@ -901,9 +905,9 @@ public: * @{ */ - /** - * Set up the undo chain and anything else that needs to be set up. - */ + /** + * Set up the undo chain and anything else that needs to be set up. + */ bool init_serial(); /** @@ -946,9 +950,9 @@ public: * @{ */ - /** - * Write a signed integer to the current output stream. - */ + /** + * Write a signed integer to the current output stream. + */ void stream_num(int val, int inmiddle, int charnum); /** diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h index 9200ec27eb..d57600edb7 100644 --- a/engines/glk/glulxe/glulxe_types.h +++ b/engines/glk/glulxe/glulxe_types.h @@ -103,18 +103,18 @@ class Glulxe; * degradation or even crashes, depending on the machine CPU. */ #define Stk1(adr) \ - (*((unsigned char *)(stack+(adr)))) + (*((unsigned char *)(stack+(adr)))) #define Stk2(adr) \ - (*((uint16 *)(stack+(adr)))) + (*((uint16 *)(stack+(adr)))) #define Stk4(adr) \ - (*((uint32 *)(stack+(adr)))) + (*((uint32 *)(stack+(adr)))) #define StkW1(adr, vl) \ - (*((byte *)(stack+(adr))) = (byte)(vl)) + (*((byte *)(stack+(adr))) = (byte)(vl)) #define StkW2(adr, vl) \ - (*((uint16 *)(stack+(adr))) = (uint16)(vl)) + (*((uint16 *)(stack+(adr))) = (uint16)(vl)) #define StkW4(adr, vl) \ - (*((uint32 *)(stack+(adr))) = (uint32)(vl)) + (*((uint32 *)(stack+(adr))) = (uint32)(vl)) enum Opcode { op_nop = 0x00, @@ -319,9 +319,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 - const 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; @@ -389,7 +389,7 @@ enum iosys { }; #define CACHEBITS (4) -#define CACHESIZE (1 << CACHEBITS) +#define CACHESIZE (1 << CACHEBITS) #define CACHEMASK (15) struct cacheblock_struct { diff --git a/engines/glk/glulxe/heap.cpp b/engines/glk/glulxe/heap.cpp index 00b8dcb74c..86cd4439b8 100644 --- a/engines/glk/glulxe/heap.cpp +++ b/engines/glk/glulxe/heap.cpp @@ -26,295 +26,290 @@ namespace Glk { namespace Glulxe { void Glulxe::heap_clear() { - while (heap_head) { - heapblock_t *blo = heap_head; - heap_head = blo->next; - blo->next = nullptr; - blo->prev = nullptr; - glulx_free(blo); - } - heap_tail = nullptr; - - if (heap_start) { - uint res = change_memsize(heap_start, true); - if (res) - fatal_error_i("Unable to revert memory size when deactivating heap.", - heap_start); - } - - heap_start = 0; - alloc_count = 0; - /* heap_sanity_check(); */ + while (heap_head) { + heapblock_t *blo = heap_head; + heap_head = blo->next; + blo->next = nullptr; + blo->prev = nullptr; + glulx_free(blo); + } + heap_tail = nullptr; + + if (heap_start) { + uint res = change_memsize(heap_start, true); + if (res) + fatal_error_i("Unable to revert memory size when deactivating heap.", + heap_start); + } + + heap_start = 0; + alloc_count = 0; + /* heap_sanity_check(); */ } int Glulxe::heap_is_active() const { - return (heap_start != 0); + return (heap_start != 0); } uint Glulxe::heap_get_start() const { - return heap_start; + return heap_start; } uint Glulxe::heap_alloc(uint len) { - heapblock_t *blo, *newblo; + heapblock_t *blo, *newblo; #ifdef FIXED_MEMSIZE - return 0; + return 0; #else /* FIXED_MEMSIZE */ - if (len <= 0) - fatal_error("Heap allocation length must be positive."); - - blo = heap_head; - while (blo) { - if (blo->isfree && blo->len >= len) - break; - - if (!blo->isfree) { - blo = blo->next; - continue; - } - - if (!blo->next || !blo->next->isfree) { - blo = blo->next; - continue; - } - - /* This is a free block, but the next block in the list is also - free, so we "advance" by merging rather than by going to - blo->next. */ - newblo = blo->next; - blo->len += newblo->len; - if (newblo->next) { - blo->next = newblo->next; - newblo->next->prev = blo; - } - else { - blo->next = nullptr; - heap_tail = blo; - } - newblo->next = nullptr; - newblo->prev = nullptr; - glulx_free(newblo); - newblo = nullptr; - continue; - } - - if (!blo) { - /* No free area is visible on the list. Try extending memory. How - much? Double the heap size, or by 256 bytes, or by the memory - length requested -- whichever is greatest. */ - uint res; - uint extension; - uint oldendmem = endmem; - - extension = 0; - if (heap_start) - extension = endmem - heap_start; - if (extension < len) - extension = len; - if (extension < 256) - extension = 256; - /* And it must be rounded up to a multiple of 256. */ - extension = (extension + 0xFF) & (~(uint)0xFF); - - res = change_memsize(endmem+extension, true); - if (res) - return 0; - - /* If we just started the heap, note that. */ - if (heap_start == 0) - heap_start = oldendmem; - - if (heap_tail && heap_tail->isfree) { - /* Append the new space to the last block. */ - blo = heap_tail; - blo->len += extension; - } - else { - /* Append the new space to the block list, as a new block. */ - newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); - if (!newblo) - fatal_error("Unable to allocate record for heap block."); - newblo->addr = oldendmem; - newblo->len = extension; - newblo->isfree = true; - newblo->next = nullptr; - newblo->prev = nullptr; - - if (!heap_tail) { - heap_head = newblo; - heap_tail = newblo; - } - else { - blo = heap_tail; - heap_tail = newblo; - blo->next = newblo; - newblo->prev = blo; - } - - blo = newblo; - newblo = nullptr; - } - - /* and continue forwards, using this new block (blo). */ - } - - /* Something strange happened. */ - if (!blo || !blo->isfree || blo->len < len) - return 0; - - /* We now have a free block of size len or longer. */ - - if (blo->len == len) { - blo->isfree = false; - } - else { - newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); - if (!newblo) - fatal_error("Unable to allocate record for heap block."); - newblo->isfree = true; - newblo->addr = blo->addr + len; - newblo->len = blo->len - len; - blo->len = len; - blo->isfree = false; - newblo->next = blo->next; - if (newblo->next) - newblo->next->prev = newblo; - newblo->prev = blo; - blo->next = newblo; - if (heap_tail == blo) - heap_tail = newblo; - } - - alloc_count++; - /* heap_sanity_check(); */ - return blo->addr; + if (len <= 0) + fatal_error("Heap allocation length must be positive."); + + blo = heap_head; + while (blo) { + if (blo->isfree && blo->len >= len) + break; + + if (!blo->isfree) { + blo = blo->next; + continue; + } + + if (!blo->next || !blo->next->isfree) { + blo = blo->next; + continue; + } + + /* This is a free block, but the next block in the list is also + free, so we "advance" by merging rather than by going to + blo->next. */ + newblo = blo->next; + blo->len += newblo->len; + if (newblo->next) { + blo->next = newblo->next; + newblo->next->prev = blo; + } else { + blo->next = nullptr; + heap_tail = blo; + } + newblo->next = nullptr; + newblo->prev = nullptr; + glulx_free(newblo); + newblo = nullptr; + continue; + } + + if (!blo) { + /* No free area is visible on the list. Try extending memory. How + much? Double the heap size, or by 256 bytes, or by the memory + length requested -- whichever is greatest. */ + uint res; + uint extension; + uint oldendmem = endmem; + + extension = 0; + if (heap_start) + extension = endmem - heap_start; + if (extension < len) + extension = len; + if (extension < 256) + extension = 256; + /* And it must be rounded up to a multiple of 256. */ + extension = (extension + 0xFF) & (~(uint)0xFF); + + res = change_memsize(endmem + extension, true); + if (res) + return 0; + + /* If we just started the heap, note that. */ + if (heap_start == 0) + heap_start = oldendmem; + + if (heap_tail && heap_tail->isfree) { + /* Append the new space to the last block. */ + blo = heap_tail; + blo->len += extension; + } else { + /* Append the new space to the block list, as a new block. */ + newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); + if (!newblo) + fatal_error("Unable to allocate record for heap block."); + newblo->addr = oldendmem; + newblo->len = extension; + newblo->isfree = true; + newblo->next = nullptr; + newblo->prev = nullptr; + + if (!heap_tail) { + heap_head = newblo; + heap_tail = newblo; + } else { + blo = heap_tail; + heap_tail = newblo; + blo->next = newblo; + newblo->prev = blo; + } + + blo = newblo; + newblo = nullptr; + } + + /* and continue forwards, using this new block (blo). */ + } + + /* Something strange happened. */ + if (!blo || !blo->isfree || blo->len < len) + return 0; + + /* We now have a free block of size len or longer. */ + + if (blo->len == len) { + blo->isfree = false; + } else { + newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); + if (!newblo) + fatal_error("Unable to allocate record for heap block."); + newblo->isfree = true; + newblo->addr = blo->addr + len; + newblo->len = blo->len - len; + blo->len = len; + blo->isfree = false; + newblo->next = blo->next; + if (newblo->next) + newblo->next->prev = newblo; + newblo->prev = blo; + blo->next = newblo; + if (heap_tail == blo) + heap_tail = newblo; + } + + alloc_count++; + /* heap_sanity_check(); */ + return blo->addr; #endif /* FIXED_MEMSIZE */ } void Glulxe::heap_free(uint addr) { - heapblock_t *blo; - - for (blo = heap_head; blo; blo = blo->next) { - if (blo->addr == addr) - break; - }; - if (!blo || blo->isfree) - fatal_error_i("Attempt to free unallocated address from heap.", addr); - - blo->isfree = true; - alloc_count--; - if (alloc_count <= 0) { - heap_clear(); - } - - /* heap_sanity_check(); */ + heapblock_t *blo; + + for (blo = heap_head; blo; blo = blo->next) { + if (blo->addr == addr) + break; + }; + if (!blo || blo->isfree) + fatal_error_i("Attempt to free unallocated address from heap.", addr); + + blo->isfree = true; + alloc_count--; + if (alloc_count <= 0) { + heap_clear(); + } + + /* heap_sanity_check(); */ } int Glulxe::heap_get_summary(uint *valcount, uint **summary) { - uint *arr, len, pos; - heapblock_t *blo; + uint *arr, len, pos; + heapblock_t *blo; - *valcount = 0; - *summary = nullptr; + *valcount = 0; + *summary = nullptr; - if (heap_start == 0) - return 0; + if (heap_start == 0) + return 0; - len = 2 + 2*alloc_count; - arr = (uint *)glulx_malloc(len * sizeof(uint)); - if (!arr) - return 1; + len = 2 + 2 * alloc_count; + arr = (uint *)glulx_malloc(len * sizeof(uint)); + if (!arr) + return 1; - pos = 0; - arr[pos++] = heap_start; - arr[pos++] = alloc_count; + pos = 0; + arr[pos++] = heap_start; + arr[pos++] = alloc_count; - for (blo = heap_head; blo; blo = blo->next) { - if (blo->isfree) - continue; - arr[pos++] = blo->addr; - arr[pos++] = blo->len; - } + for (blo = heap_head; blo; blo = blo->next) { + if (blo->isfree) + continue; + arr[pos++] = blo->addr; + arr[pos++] = blo->len; + } - if (pos != len) - fatal_error("Wrong number of active blocks in heap"); + if (pos != len) + fatal_error("Wrong number of active blocks in heap"); - *valcount = len; - *summary = arr; - return 0; + *valcount = len; + *summary = arr; + return 0; } int Glulxe::heap_apply_summary(uint valcount, uint *summary) { - uint lx, jx, lastend; + uint lx, jx, lastend; - if (heap_start) - fatal_error("Heap active when heap_apply_summary called"); + if (heap_start) + fatal_error("Heap active when heap_apply_summary called"); - if (valcount == 0 || summary == nullptr) - return 0; - if (valcount == 2 && summary[0] == 0 && summary[1] == 0) - return 0; + if (valcount == 0 || summary == nullptr) + return 0; + if (valcount == 2 && summary[0] == 0 && summary[1] == 0) + return 0; #ifdef FIXED_MEMSIZE - return 1; + return 1; #else /* FIXED_MEMSIZE */ - lx = 0; - heap_start = summary[lx++]; - alloc_count = summary[lx++]; - - for (jx=lx; jx+2<valcount; jx+=2) { - if (summary[jx] >= summary[jx+2]) - fatal_error("Heap block summary is out of order."); - } - - lastend = heap_start; - - while (lx < valcount || lastend < endmem) { - heapblock_t *blo; - - blo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); - if (!blo) - fatal_error("Unable to allocate record for heap block."); - - if (lx >= valcount) { - blo->addr = lastend; - blo->len = endmem - lastend; - blo->isfree = true; - } else { - if (lastend < summary[lx]) { - blo->addr = lastend; - blo->len = summary[lx] - lastend; - blo->isfree = true; - } else { - blo->addr = summary[lx++]; - blo->len = summary[lx++]; - blo->isfree = false; - } - } - - blo->prev = nullptr; - blo->next = nullptr; - - if (!heap_head) { - heap_head = blo; - heap_tail = blo; - } - else { - heap_tail->next = blo; - blo->prev = heap_tail; - heap_tail = blo; - } - - lastend = blo->addr + blo->len; - } - - /* heap_sanity_check(); */ - - return 0; + lx = 0; + heap_start = summary[lx++]; + alloc_count = summary[lx++]; + + for (jx = lx; jx + 2 < valcount; jx += 2) { + if (summary[jx] >= summary[jx + 2]) + fatal_error("Heap block summary is out of order."); + } + + lastend = heap_start; + + while (lx < valcount || lastend < endmem) { + heapblock_t *blo; + + blo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t)); + if (!blo) + fatal_error("Unable to allocate record for heap block."); + + if (lx >= valcount) { + blo->addr = lastend; + blo->len = endmem - lastend; + blo->isfree = true; + } else { + if (lastend < summary[lx]) { + blo->addr = lastend; + blo->len = summary[lx] - lastend; + blo->isfree = true; + } else { + blo->addr = summary[lx++]; + blo->len = summary[lx++]; + blo->isfree = false; + } + } + + blo->prev = nullptr; + blo->next = nullptr; + + if (!heap_head) { + heap_head = blo; + heap_tail = blo; + } else { + heap_tail->next = blo; + blo->prev = heap_tail; + heap_tail = blo; + } + + lastend = blo->addr + blo->len; + } + + /* heap_sanity_check(); */ + + return 0; #endif /* FIXED_MEMSIZE */ } diff --git a/engines/glk/glulxe/operand.cpp b/engines/glk/glulxe/operand.cpp index 23018d408f..d41b2b0fe7 100644 --- a/engines/glk/glulxe/operand.cpp +++ b/engines/glk/glulxe/operand.cpp @@ -65,552 +65,546 @@ static const int array_LLSS[4] = { modeform_Load, modeform_Load, 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); + 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; + 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; + 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 + GLULX_EXTEND_OPERANDS #endif /* GLULX_EXTEND_OPERANDS */ - default: - return nullptr; - } + default: + return nullptr; + } } 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."); - } - } - } + 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) { + switch (desttype) { - case 0: /* do nothing; discard the value. */ - return; + case 0: /* do nothing; discard the value. */ + return; - case 1: /* main memory. */ - MemW4(destaddr, storeval); - return; + case 1: /* main memory. */ + MemW4(destaddr, storeval); + return; - case 2: /* locals. */ - destaddr += localsbase; - StkW4(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; + 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."); + default: + fatal_error("Unknown destination type in store operand."); - } + } } void Glulxe::store_operand_s(uint desttype, uint destaddr, uint storeval) { - storeval &= 0xFFFF; + storeval &= 0xFFFF; - switch (desttype) { + switch (desttype) { - case 0: /* do nothing; discard the value. */ - return; + case 0: /* do nothing; discard the value. */ + return; - case 1: /* main memory. */ - MemW2(destaddr, storeval); - return; + case 1: /* main memory. */ + MemW2(destaddr, storeval); + return; - case 2: /* locals. */ - destaddr += localsbase; - StkW2(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; + 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."); + default: + fatal_error("Unknown destination type in store operand."); - } + } } void Glulxe::store_operand_b(uint desttype, uint destaddr, uint storeval) { - storeval &= 0xFF; + storeval &= 0xFF; - switch (desttype) { + switch (desttype) { - case 0: /* do nothing; discard the value. */ - return; + case 0: /* do nothing; discard the value. */ + return; - case 1: /* main memory. */ - MemW1(destaddr, storeval); - return; + case 1: /* main memory. */ + MemW1(destaddr, storeval); + return; - case 2: /* locals. */ - destaddr += localsbase; - StkW1(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; + 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."); + default: + fatal_error("Unknown destination type in store operand."); - } + } } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/search.cpp b/engines/glk/glulxe/search.cpp index 440da553f0..1bbe8fa027 100644 --- a/engines/glk/glulxe/search.cpp +++ b/engines/glk/glulxe/search.cpp @@ -31,185 +31,180 @@ enum serop { serop_ReturnIndex = 0x04 }; -uint Glulxe::linear_search(uint key, uint keysize, uint start, uint structsize, uint numstructs, - uint keyoffset, uint options) { - unsigned char keybuf[4]; - uint count; - uint ix; - int retindex = ((options & serop_ReturnIndex) != 0); - int zeroterm = ((options & serop_ZeroKeyTerminates) != 0); - - fetchkey(keybuf, key, keysize, options); - - for (count=0; count<numstructs; count++, start+=structsize) { - int match = true; - if (keysize <= 4) { - for (ix=0; match && ix<keysize; ix++) { - if (Mem1(start + keyoffset + ix) != keybuf[ix]) - match = false; - } - } - else { - for (ix=0; match && ix<keysize; ix++) { - if (Mem1(start + keyoffset + ix) != Mem1(key + ix)) - match = false; - } - } - - if (match) { - if (retindex) - return count; - else - return start; - } - - if (zeroterm) { - match = true; - for (ix=0; match && ix<keysize; ix++) { - if (Mem1(start + keyoffset + ix) != 0) - match = false; - } - if (match) { - break; - } - } - } - - if (retindex) - return (uint)-1; - else - return 0; +uint Glulxe::linear_search(uint key, uint keysize, uint start, uint structsize, uint numstructs, + uint keyoffset, uint options) { + unsigned char keybuf[4]; + uint count; + uint ix; + int retindex = ((options & serop_ReturnIndex) != 0); + int zeroterm = ((options & serop_ZeroKeyTerminates) != 0); + + fetchkey(keybuf, key, keysize, options); + + for (count = 0; count < numstructs; count++, start += structsize) { + int match = true; + if (keysize <= 4) { + for (ix = 0; match && ix < keysize; ix++) { + if (Mem1(start + keyoffset + ix) != keybuf[ix]) + match = false; + } + } else { + for (ix = 0; match && ix < keysize; ix++) { + if (Mem1(start + keyoffset + ix) != Mem1(key + ix)) + match = false; + } + } + + if (match) { + if (retindex) + return count; + else + return start; + } + + if (zeroterm) { + match = true; + for (ix = 0; match && ix < keysize; ix++) { + if (Mem1(start + keyoffset + ix) != 0) + match = false; + } + if (match) { + break; + } + } + } + + if (retindex) + return (uint) - 1; + else + return 0; } -uint Glulxe::binary_search(uint key, uint keysize, uint start, uint structsize, uint numstructs, - uint keyoffset, uint options) { - byte keybuf[4]; - byte byte1, byte2; - uint top, bot, val, addr; - uint ix; - int retindex = ((options & serop_ReturnIndex) != 0); - - fetchkey(keybuf, key, keysize, options); - - bot = 0; - top = numstructs; - while (bot < top) { - int cmp = 0; - val = (top+bot) / 2; - addr = start + val * structsize; - - if (keysize <= 4) { - for (ix=0; (!cmp) && ix<keysize; ix++) { - byte1 = Mem1(addr + keyoffset + ix); - byte2 = keybuf[ix]; - if (byte1 < byte2) - cmp = -1; - else if (byte1 > byte2) - cmp = 1; - } - } - else { - for (ix=0; (!cmp) && ix<keysize; ix++) { - byte1 = Mem1(addr + keyoffset + ix); - byte2 = Mem1(key + ix); - if (byte1 < byte2) - cmp = -1; - else if (byte1 > byte2) - cmp = 1; - } - } - - if (!cmp) { - if (retindex) - return val; - else - return addr; - } - - if (cmp < 0) { - bot = val+1; - } - else { - top = val; - } - } - - if (retindex) - return (uint)-1; - else - return 0; +uint Glulxe::binary_search(uint key, uint keysize, uint start, uint structsize, uint numstructs, + uint keyoffset, uint options) { + byte keybuf[4]; + byte byte1, byte2; + uint top, bot, val, addr; + uint ix; + int retindex = ((options & serop_ReturnIndex) != 0); + + fetchkey(keybuf, key, keysize, options); + + bot = 0; + top = numstructs; + while (bot < top) { + int cmp = 0; + val = (top + bot) / 2; + addr = start + val * structsize; + + if (keysize <= 4) { + for (ix = 0; (!cmp) && ix < keysize; ix++) { + byte1 = Mem1(addr + keyoffset + ix); + byte2 = keybuf[ix]; + if (byte1 < byte2) + cmp = -1; + else if (byte1 > byte2) + cmp = 1; + } + } else { + for (ix = 0; (!cmp) && ix < keysize; ix++) { + byte1 = Mem1(addr + keyoffset + ix); + byte2 = Mem1(key + ix); + if (byte1 < byte2) + cmp = -1; + else if (byte1 > byte2) + cmp = 1; + } + } + + if (!cmp) { + if (retindex) + return val; + else + return addr; + } + + if (cmp < 0) { + bot = val + 1; + } else { + top = val; + } + } + + if (retindex) + return (uint) - 1; + else + return 0; } uint Glulxe::linked_search(uint key, uint keysize, uint start, uint keyoffset, uint nextoffset, uint options) { - unsigned char keybuf[4]; - uint ix; - uint val; - int zeroterm = ((options & serop_ZeroKeyTerminates) != 0); - - fetchkey(keybuf, key, keysize, options); - - while (start != 0) { - int match = true; - if (keysize <= 4) { - for (ix=0; match && ix<keysize; ix++) { - if (Mem1(start + keyoffset + ix) != keybuf[ix]) - match = false; - } - } - else { - for (ix=0; match && ix<keysize; ix++) { - if (Mem1(start + keyoffset + ix) != Mem1(key + ix)) - match = false; - } - } - - if (match) { - return start; - } - - if (zeroterm) { - match = true; - for (ix=0; match && ix<keysize; ix++) { - if (Mem1(start + keyoffset + ix) != 0) - match = false; - } - if (match) { - break; - } - } - - val = start + nextoffset; - start = Mem4(val); - } - - return 0; + unsigned char keybuf[4]; + uint ix; + uint val; + int zeroterm = ((options & serop_ZeroKeyTerminates) != 0); + + fetchkey(keybuf, key, keysize, options); + + while (start != 0) { + int match = true; + if (keysize <= 4) { + for (ix = 0; match && ix < keysize; ix++) { + if (Mem1(start + keyoffset + ix) != keybuf[ix]) + match = false; + } + } else { + for (ix = 0; match && ix < keysize; ix++) { + if (Mem1(start + keyoffset + ix) != Mem1(key + ix)) + match = false; + } + } + + if (match) { + return start; + } + + if (zeroterm) { + match = true; + for (ix = 0; match && ix < keysize; ix++) { + if (Mem1(start + keyoffset + ix) != 0) + match = false; + } + if (match) { + break; + } + } + + val = start + nextoffset; + start = Mem4(val); + } + + return 0; } void Glulxe::fetchkey(unsigned char *keybuf, uint key, uint keysize, uint options) { - uint ix; - - if (options & serop_KeyIndirect) { - if (keysize <= 4) { - for (ix=0; ix<keysize; ix++) - keybuf[ix] = Mem1(key+ix); - } - } - else { - switch (keysize) { - case 4: - Write4(keybuf, key); - break; - case 2: - Write2(keybuf, key); - break; - case 1: - Write1(keybuf, key); - break; - default: - fatal_error("Direct search key must hold one, two, or four bytes."); - } - } + uint ix; + + if (options & serop_KeyIndirect) { + if (keysize <= 4) { + for (ix = 0; ix < keysize; ix++) + keybuf[ix] = Mem1(key + ix); + } + } else { + switch (keysize) { + case 4: + Write4(keybuf, key); + break; + case 2: + Write2(keybuf, key); + break; + case 1: + Write1(keybuf, key); + break; + default: + fatal_error("Direct search key must hold one, two, or four bytes."); + } + } } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp index bc267884b5..01848fc8cd 100644 --- a/engines/glk/glulxe/serial.cpp +++ b/engines/glk/glulxe/serial.cpp @@ -28,1116 +28,1104 @@ namespace Glulxe { #define IFFID(c1, c2, c3, c4) MKTAG(c1, c2, c3, c4) bool Glulxe::init_serial() { - undo_chain_num = 0; - undo_chain_size = max_undo_level; - undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size); - if (!undo_chain) - return false; + undo_chain_num = 0; + undo_chain_size = max_undo_level; + undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size); + if (!undo_chain) + return false; #ifdef SERIALIZE_CACHE_RAM - { - uint len = (endmem - ramstart); - uint res; - ramcache = (unsigned char *)glulx_malloc(sizeof(unsigned char *) * len); - if (!ramcache) - return false; - - _gameFile.seek(gamefile_start + ramstart); - res = _gameFile.read(ramcache, len); - if (res != len) - return false; - } + { + uint len = (endmem - ramstart); + uint res; + ramcache = (unsigned char *)glulx_malloc(sizeof(unsigned char *) * len); + if (!ramcache) + return false; + + _gameFile.seek(gamefile_start + ramstart); + res = _gameFile.read(ramcache, len); + if (res != len) + return false; + } #endif /* SERIALIZE_CACHE_RAM */ - return true; + return true; } void Glulxe::final_serial() { - if (undo_chain) { - int ix; - for (ix=0; ix<undo_chain_num; ix++) { - glulx_free(undo_chain[ix]); - } - glulx_free(undo_chain); - } - undo_chain = nullptr; - undo_chain_size = 0; - undo_chain_num = 0; + if (undo_chain) { + int ix; + for (ix = 0; ix < undo_chain_num; ix++) { + glulx_free(undo_chain[ix]); + } + glulx_free(undo_chain); + } + undo_chain = nullptr; + undo_chain_size = 0; + undo_chain_num = 0; #ifdef SERIALIZE_CACHE_RAM - if (ramcache) { - glulx_free(ramcache); - ramcache = nullptr; - } + if (ramcache) { + glulx_free(ramcache); + ramcache = nullptr; + } #endif /* SERIALIZE_CACHE_RAM */ } uint Glulxe::perform_saveundo() { - dest_t dest; - uint res; - uint memstart = 0, memlen = 0, heapstart = 0, heaplen = 0; - uint stackstart = 0, stacklen = 0; - - /* The format for undo-saves is simpler than for saves on disk. We - just have a memory chunk, a heap chunk, and a stack chunk, in - that order. We skip the IFF chunk headers (although the size - fields are still there.) We also don't bother with IFF's 16-bit - alignment. */ - - if (undo_chain_size == 0) - return 1; - - dest.ismem = true; - dest.size = 0; - dest.pos = 0; - dest.ptr = nullptr; - dest.str = nullptr; - - res = 0; - if (res == 0) { - res = write_long(&dest, 0); /* space for chunk length */ - } - if (res == 0) { - memstart = dest.pos; - res = write_memstate(&dest); - memlen = dest.pos - memstart; - } - if (res == 0) { - res = write_long(&dest, 0); /* space for chunk length */ - } - if (res == 0) { - heapstart = dest.pos; - res = write_heapstate(&dest, false); - heaplen = dest.pos - heapstart; - } - if (res == 0) { - res = write_long(&dest, 0); /* space for chunk length */ - } - if (res == 0) { - stackstart = dest.pos; - res = write_stackstate(&dest, false); - stacklen = dest.pos - stackstart; - } - - if (res == 0) { - /* Trim it down to the perfect size. */ - dest.ptr = (byte *)glulx_realloc(dest.ptr, dest.pos); - if (!dest.ptr) - res = 1; - } - if (res == 0) { - res = reposition_write(&dest, memstart-4); - } - if (res == 0) { - res = write_long(&dest, memlen); - } - if (res == 0) { - res = reposition_write(&dest, heapstart-4); - } - if (res == 0) { - res = write_long(&dest, heaplen); - } - if (res == 0) { - res = reposition_write(&dest, stackstart-4); - } - if (res == 0) { - res = write_long(&dest, stacklen); - } - - if (res == 0) { - /* It worked. */ - if (undo_chain_num >= undo_chain_size) { - glulx_free(undo_chain[undo_chain_num-1]); - undo_chain[undo_chain_num-1] = nullptr; - } - if (undo_chain_size > 1) - memmove(undo_chain+1, undo_chain, - (undo_chain_size-1) * sizeof(unsigned char *)); - undo_chain[0] = dest.ptr; - if (undo_chain_num < undo_chain_size) - undo_chain_num += 1; - dest.ptr = nullptr; - } - else { - /* It didn't work. */ - if (dest.ptr) { - glulx_free(dest.ptr); - dest.ptr = nullptr; - } - } - - return res; + dest_t dest; + uint res; + uint memstart = 0, memlen = 0, heapstart = 0, heaplen = 0; + uint stackstart = 0, stacklen = 0; + + /* The format for undo-saves is simpler than for saves on disk. We + just have a memory chunk, a heap chunk, and a stack chunk, in + that order. We skip the IFF chunk headers (although the size + fields are still there.) We also don't bother with IFF's 16-bit + alignment. */ + + if (undo_chain_size == 0) + return 1; + + dest.ismem = true; + dest.size = 0; + dest.pos = 0; + dest.ptr = nullptr; + dest.str = nullptr; + + res = 0; + if (res == 0) { + res = write_long(&dest, 0); /* space for chunk length */ + } + if (res == 0) { + memstart = dest.pos; + res = write_memstate(&dest); + memlen = dest.pos - memstart; + } + if (res == 0) { + res = write_long(&dest, 0); /* space for chunk length */ + } + if (res == 0) { + heapstart = dest.pos; + res = write_heapstate(&dest, false); + heaplen = dest.pos - heapstart; + } + if (res == 0) { + res = write_long(&dest, 0); /* space for chunk length */ + } + if (res == 0) { + stackstart = dest.pos; + res = write_stackstate(&dest, false); + stacklen = dest.pos - stackstart; + } + + if (res == 0) { + /* Trim it down to the perfect size. */ + dest.ptr = (byte *)glulx_realloc(dest.ptr, dest.pos); + if (!dest.ptr) + res = 1; + } + if (res == 0) { + res = reposition_write(&dest, memstart - 4); + } + if (res == 0) { + res = write_long(&dest, memlen); + } + if (res == 0) { + res = reposition_write(&dest, heapstart - 4); + } + if (res == 0) { + res = write_long(&dest, heaplen); + } + if (res == 0) { + res = reposition_write(&dest, stackstart - 4); + } + if (res == 0) { + res = write_long(&dest, stacklen); + } + + if (res == 0) { + /* It worked. */ + if (undo_chain_num >= undo_chain_size) { + glulx_free(undo_chain[undo_chain_num - 1]); + undo_chain[undo_chain_num - 1] = nullptr; + } + if (undo_chain_size > 1) + memmove(undo_chain + 1, undo_chain, + (undo_chain_size - 1) * sizeof(unsigned char *)); + undo_chain[0] = dest.ptr; + if (undo_chain_num < undo_chain_size) + undo_chain_num += 1; + dest.ptr = nullptr; + } else { + /* It didn't work. */ + if (dest.ptr) { + glulx_free(dest.ptr); + dest.ptr = nullptr; + } + } + + return res; } uint Glulxe::perform_restoreundo() { - dest_t dest; - uint res, val = 0; - uint heapsumlen = 0; - uint *heapsumarr = nullptr; - - /* If profiling is enabled and active then fail. */ - #if VM_PROFILING - if (profile_profiling_active()) - return 1; - #endif /* VM_PROFILING */ - - if (undo_chain_size == 0 || undo_chain_num == 0) - return 1; - - dest.ismem = true; - dest.size = 0; - dest.pos = 0; - dest.ptr = undo_chain[0]; - dest.str = nullptr; - - res = 0; - if (res == 0) { - res = read_long(&dest, &val); - } - if (res == 0) { - res = read_memstate(&dest, val); - } - if (res == 0) { - res = read_long(&dest, &val); - } - if (res == 0) { - res = read_heapstate(&dest, val, false, &heapsumlen, &heapsumarr); - } - if (res == 0) { - res = read_long(&dest, &val); - } - if (res == 0) { - res = read_stackstate(&dest, val, false); - } - /* ### really, many of the failure modes of those calls ought to - cause fatal errors. The stack or main memory may be damaged now. */ - - if (res == 0) { - if (heapsumarr) - res = heap_apply_summary(heapsumlen, heapsumarr); - } - - if (res == 0) { - /* It worked. */ - if (undo_chain_size > 1) - memmove(undo_chain, undo_chain+1, - (undo_chain_size-1) * sizeof(unsigned char *)); - undo_chain_num -= 1; - glulx_free(dest.ptr); - dest.ptr = nullptr; - } - else { - /* It didn't work. */ - dest.ptr = nullptr; - } - - return res; + dest_t dest; + uint res, val = 0; + uint heapsumlen = 0; + uint *heapsumarr = nullptr; + + /* If profiling is enabled and active then fail. */ +#if VM_PROFILING + if (profile_profiling_active()) + return 1; +#endif /* VM_PROFILING */ + + if (undo_chain_size == 0 || undo_chain_num == 0) + return 1; + + dest.ismem = true; + dest.size = 0; + dest.pos = 0; + dest.ptr = undo_chain[0]; + dest.str = nullptr; + + res = 0; + if (res == 0) { + res = read_long(&dest, &val); + } + if (res == 0) { + res = read_memstate(&dest, val); + } + if (res == 0) { + res = read_long(&dest, &val); + } + if (res == 0) { + res = read_heapstate(&dest, val, false, &heapsumlen, &heapsumarr); + } + if (res == 0) { + res = read_long(&dest, &val); + } + if (res == 0) { + res = read_stackstate(&dest, val, false); + } + /* ### really, many of the failure modes of those calls ought to + cause fatal errors. The stack or main memory may be damaged now. */ + + if (res == 0) { + if (heapsumarr) + res = heap_apply_summary(heapsumlen, heapsumarr); + } + + if (res == 0) { + /* It worked. */ + if (undo_chain_size > 1) + memmove(undo_chain, undo_chain + 1, + (undo_chain_size - 1) * sizeof(unsigned char *)); + undo_chain_num -= 1; + glulx_free(dest.ptr); + dest.ptr = nullptr; + } else { + /* It didn't work. */ + dest.ptr = nullptr; + } + + return res; } uint Glulxe::perform_save(strid_t str) { - dest_t dest; - int ix; - uint res, lx, val; - uint memstart = 0, memlen = 0, stackstart = 0, stacklen = 0; - uint heapstart = 0, heaplen = 0, filestart = 0, filelen = 0; - - stream_get_iosys(&val, &lx); - if (val != 2) { - /* Not using the Glk I/O system, so bail. This function only - knows how to write to a Glk stream. */ - fatal_error("Streams are only available in Glk I/O system."); - } - - if (str == 0) - return 1; - - dest.ismem = false; - dest.size = 0; - dest.pos = 0; - dest.ptr = nullptr; - dest.str = str; - - res = 0; - - /* Quetzal header. */ - if (res == 0) { - res = write_long(&dest, IFFID('F', 'O', 'R', 'M')); - } - if (res == 0) { - res = write_long(&dest, 0); /* space for file length */ - filestart = dest.pos; - } - - if (res == 0) { - res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */ - } - - /* Header chunk. This is the first 128 bytes of memory. */ - if (res == 0) { - res = write_long(&dest, IFFID('I', 'F', 'h', 'd')); - } - if (res == 0) { - res = write_long(&dest, 128); - } - for (ix=0; res==0 && ix<128; ix++) { - res = write_byte(&dest, Mem1(ix)); - } - /* Always even, so no padding necessary. */ - - /* Memory chunk. */ - if (res == 0) { - res = write_long(&dest, IFFID('C', 'M', 'e', 'm')); - } - if (res == 0) { - res = write_long(&dest, 0); /* space for chunk length */ - } - if (res == 0) { - memstart = dest.pos; - res = write_memstate(&dest); - memlen = dest.pos - memstart; - } - if (res == 0 && (memlen & 1) != 0) { - res = write_byte(&dest, 0); - } - - /* Heap chunk. */ - if (res == 0) { - res = write_long(&dest, IFFID('M', 'A', 'l', 'l')); - } - if (res == 0) { - res = write_long(&dest, 0); /* space for chunk length */ - } - if (res == 0) { - heapstart = dest.pos; - res = write_heapstate(&dest, true); - heaplen = dest.pos - heapstart; - } - /* Always even, so no padding necessary. */ - - /* Stack chunk. */ - if (res == 0) { - res = write_long(&dest, IFFID('S', 't', 'k', 's')); - } - if (res == 0) { - res = write_long(&dest, 0); /* space for chunk length */ - } - if (res == 0) { - stackstart = dest.pos; - res = write_stackstate(&dest, true); - stacklen = dest.pos - stackstart; - } - if (res == 0 && (stacklen & 1) != 0) { - res = write_byte(&dest, 0); - } - - filelen = dest.pos - filestart; - - /* Okay, fill in all the lengths. */ - if (res == 0) { - res = reposition_write(&dest, memstart-4); - } - if (res == 0) { - res = write_long(&dest, memlen); - } - if (res == 0) { - res = reposition_write(&dest, heapstart-4); - } - if (res == 0) { - res = write_long(&dest, heaplen); - } - if (res == 0) { - res = reposition_write(&dest, stackstart-4); - } - if (res == 0) { - res = write_long(&dest, stacklen); - } - if (res == 0) { - res = reposition_write(&dest, filestart-4); - } - if (res == 0) { - res = write_long(&dest, filelen); - } - - /* All done. */ - - return res; + dest_t dest; + int ix; + uint res, lx, val; + uint memstart = 0, memlen = 0, stackstart = 0, stacklen = 0; + uint heapstart = 0, heaplen = 0, filestart = 0, filelen = 0; + + stream_get_iosys(&val, &lx); + if (val != 2) { + /* Not using the Glk I/O system, so bail. This function only + knows how to write to a Glk stream. */ + fatal_error("Streams are only available in Glk I/O system."); + } + + if (str == 0) + return 1; + + dest.ismem = false; + dest.size = 0; + dest.pos = 0; + dest.ptr = nullptr; + dest.str = str; + + res = 0; + + /* Quetzal header. */ + if (res == 0) { + res = write_long(&dest, IFFID('F', 'O', 'R', 'M')); + } + if (res == 0) { + res = write_long(&dest, 0); /* space for file length */ + filestart = dest.pos; + } + + if (res == 0) { + res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */ + } + + /* Header chunk. This is the first 128 bytes of memory. */ + if (res == 0) { + res = write_long(&dest, IFFID('I', 'F', 'h', 'd')); + } + if (res == 0) { + res = write_long(&dest, 128); + } + for (ix = 0; res == 0 && ix < 128; ix++) { + res = write_byte(&dest, Mem1(ix)); + } + /* Always even, so no padding necessary. */ + + /* Memory chunk. */ + if (res == 0) { + res = write_long(&dest, IFFID('C', 'M', 'e', 'm')); + } + if (res == 0) { + res = write_long(&dest, 0); /* space for chunk length */ + } + if (res == 0) { + memstart = dest.pos; + res = write_memstate(&dest); + memlen = dest.pos - memstart; + } + if (res == 0 && (memlen & 1) != 0) { + res = write_byte(&dest, 0); + } + + /* Heap chunk. */ + if (res == 0) { + res = write_long(&dest, IFFID('M', 'A', 'l', 'l')); + } + if (res == 0) { + res = write_long(&dest, 0); /* space for chunk length */ + } + if (res == 0) { + heapstart = dest.pos; + res = write_heapstate(&dest, true); + heaplen = dest.pos - heapstart; + } + /* Always even, so no padding necessary. */ + + /* Stack chunk. */ + if (res == 0) { + res = write_long(&dest, IFFID('S', 't', 'k', 's')); + } + if (res == 0) { + res = write_long(&dest, 0); /* space for chunk length */ + } + if (res == 0) { + stackstart = dest.pos; + res = write_stackstate(&dest, true); + stacklen = dest.pos - stackstart; + } + if (res == 0 && (stacklen & 1) != 0) { + res = write_byte(&dest, 0); + } + + filelen = dest.pos - filestart; + + /* Okay, fill in all the lengths. */ + if (res == 0) { + res = reposition_write(&dest, memstart - 4); + } + if (res == 0) { + res = write_long(&dest, memlen); + } + if (res == 0) { + res = reposition_write(&dest, heapstart - 4); + } + if (res == 0) { + res = write_long(&dest, heaplen); + } + if (res == 0) { + res = reposition_write(&dest, stackstart - 4); + } + if (res == 0) { + res = write_long(&dest, stacklen); + } + if (res == 0) { + res = reposition_write(&dest, filestart - 4); + } + if (res == 0) { + res = write_long(&dest, filelen); + } + + /* All done. */ + + return res; } uint Glulxe::perform_restore(strid_t str, int fromshell) { - dest_t dest; - int ix; - uint lx, res, val; - uint filestart, filelen = 0; - uint heapsumlen = 0; - uint *heapsumarr = nullptr; - - /* If profiling is enabled and active then fail. */ - #if VM_PROFILING - if (profile_profiling_active()) - return 1; - #endif /* VM_PROFILING */ - - stream_get_iosys(&val, &lx); - if (val != 2 && !fromshell) { - /* Not using the Glk I/O system, so bail. This function only - knows how to read from a Glk stream. (But in the autorestore - case, iosys hasn't been set yet, so ignore this test.) */ - fatal_error("Streams are only available in Glk I/O system."); - } - - if (str == 0) - return 1; - - dest.ismem = false; - dest.size = 0; - dest.pos = 0; - dest.ptr = nullptr; - dest.str = str; - - res = 0; - - /* ### the format errors checked below should send error messages to - the current stream. */ - - if (res == 0) { - res = read_long(&dest, &val); - } - if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) { - /* ### bad header */ - return 1; - } - if (res == 0) { - res = read_long(&dest, &filelen); - } - filestart = dest.pos; - - if (res == 0) { - res = read_long(&dest, &val); - } - if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */ - /* ### bad header */ - return 1; - } - - while (res == 0 && dest.pos < filestart+filelen) { - /* Read a chunk and deal with it. */ - uint chunktype=0, chunkstart=0, chunklen=0; - unsigned char dummy; - - if (res == 0) { - res = read_long(&dest, &chunktype); - } - if (res == 0) { - res = read_long(&dest, &chunklen); - } - chunkstart = dest.pos; - - if (chunktype == IFFID('I', 'F', 'h', 'd')) { - for (ix=0; res==0 && ix<128; ix++) { - res = read_byte(&dest, &dummy); - if (res == 0 && Mem1(ix) != dummy) { - /* ### non-matching header */ - return 1; - } - } - } - else if (chunktype == IFFID('C', 'M', 'e', 'm')) { - res = read_memstate(&dest, chunklen); - } - else if (chunktype == IFFID('M', 'A', 'l', 'l')) { - res = read_heapstate(&dest, chunklen, true, &heapsumlen, &heapsumarr); - } - else if (chunktype == IFFID('S', 't', 'k', 's')) { - res = read_stackstate(&dest, chunklen, true); - } - else { - /* Unknown chunk type. Skip it. */ - for (lx=0; res==0 && lx<chunklen; lx++) { - res = read_byte(&dest, &dummy); - } - } - - if (chunkstart+chunklen != dest.pos) { - /* ### funny chunk length */ - return 1; - } - - if ((chunklen & 1) != 0) { - if (res == 0) { - res = read_byte(&dest, &dummy); - } - } - } - - if (res == 0) { - if (heapsumarr) { - /* The summary might have come from any interpreter, so it could - be out of order. We'll sort it. */ - glulx_sort(heapsumarr+2, (heapsumlen-2)/2, 2*sizeof(uint), &sort_heap_summary); - res = heap_apply_summary(heapsumlen, heapsumarr); - } - } - - if (res) - return 1; - - return 0; + dest_t dest; + int ix; + uint lx, res, val; + uint filestart, filelen = 0; + uint heapsumlen = 0; + uint *heapsumarr = nullptr; + + /* If profiling is enabled and active then fail. */ +#if VM_PROFILING + if (profile_profiling_active()) + return 1; +#endif /* VM_PROFILING */ + + stream_get_iosys(&val, &lx); + if (val != 2 && !fromshell) { + /* Not using the Glk I/O system, so bail. This function only + knows how to read from a Glk stream. (But in the autorestore + case, iosys hasn't been set yet, so ignore this test.) */ + fatal_error("Streams are only available in Glk I/O system."); + } + + if (str == 0) + return 1; + + dest.ismem = false; + dest.size = 0; + dest.pos = 0; + dest.ptr = nullptr; + dest.str = str; + + res = 0; + + /* ### the format errors checked below should send error messages to + the current stream. */ + + if (res == 0) { + res = read_long(&dest, &val); + } + if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) { + /* ### bad header */ + return 1; + } + if (res == 0) { + res = read_long(&dest, &filelen); + } + filestart = dest.pos; + + if (res == 0) { + res = read_long(&dest, &val); + } + if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */ + /* ### bad header */ + return 1; + } + + while (res == 0 && dest.pos < filestart + filelen) { + /* Read a chunk and deal with it. */ + uint chunktype = 0, chunkstart = 0, chunklen = 0; + unsigned char dummy; + + if (res == 0) { + res = read_long(&dest, &chunktype); + } + if (res == 0) { + res = read_long(&dest, &chunklen); + } + chunkstart = dest.pos; + + if (chunktype == IFFID('I', 'F', 'h', 'd')) { + for (ix = 0; res == 0 && ix < 128; ix++) { + res = read_byte(&dest, &dummy); + if (res == 0 && Mem1(ix) != dummy) { + /* ### non-matching header */ + return 1; + } + } + } else if (chunktype == IFFID('C', 'M', 'e', 'm')) { + res = read_memstate(&dest, chunklen); + } else if (chunktype == IFFID('M', 'A', 'l', 'l')) { + res = read_heapstate(&dest, chunklen, true, &heapsumlen, &heapsumarr); + } else if (chunktype == IFFID('S', 't', 'k', 's')) { + res = read_stackstate(&dest, chunklen, true); + } else { + /* Unknown chunk type. Skip it. */ + for (lx = 0; res == 0 && lx < chunklen; lx++) { + res = read_byte(&dest, &dummy); + } + } + + if (chunkstart + chunklen != dest.pos) { + /* ### funny chunk length */ + return 1; + } + + if ((chunklen & 1) != 0) { + if (res == 0) { + res = read_byte(&dest, &dummy); + } + } + } + + if (res == 0) { + if (heapsumarr) { + /* The summary might have come from any interpreter, so it could + be out of order. We'll sort it. */ + glulx_sort(heapsumarr + 2, (heapsumlen - 2) / 2, 2 * sizeof(uint), &sort_heap_summary); + res = heap_apply_summary(heapsumlen, heapsumarr); + } + } + + if (res) + return 1; + + return 0; } int Glulxe::reposition_write(dest_t *dest, uint pos) { - if (dest->ismem) { - dest->pos = pos; - } else { - glk_stream_set_position(dest->str, pos, seekmode_Start); - dest->pos = pos; - } - - return 0; + if (dest->ismem) { + dest->pos = pos; + } else { + glk_stream_set_position(dest->str, pos, seekmode_Start); + dest->pos = pos; + } + + return 0; } int Glulxe::write_buffer(dest_t *dest, const byte *ptr, uint len) { - if (dest->ismem) { - if (dest->pos+len > dest->size) { - dest->size = dest->pos+len+1024; - if (!dest->ptr) { - dest->ptr = (byte *)glulx_malloc(dest->size); - } else { - dest->ptr = (byte *)glulx_realloc(dest->ptr, dest->size); - } - if (!dest->ptr) - return 1; - } - memcpy(dest->ptr+dest->pos, ptr, len); - } - else { - glk_put_buffer_stream(dest->str, (char *)ptr, len); - } - - dest->pos += len; - - return 0; + if (dest->ismem) { + if (dest->pos + len > dest->size) { + dest->size = dest->pos + len + 1024; + if (!dest->ptr) { + dest->ptr = (byte *)glulx_malloc(dest->size); + } else { + dest->ptr = (byte *)glulx_realloc(dest->ptr, dest->size); + } + if (!dest->ptr) + return 1; + } + memcpy(dest->ptr + dest->pos, ptr, len); + } else { + glk_put_buffer_stream(dest->str, (char *)ptr, len); + } + + dest->pos += len; + + return 0; } int Glulxe::read_buffer(dest_t *dest, byte *ptr, uint len) { - uint newlen; + uint newlen; - if (dest->ismem) { - memcpy(ptr, dest->ptr+dest->pos, len); - } - else { - newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len); - if (newlen != len) - return 1; - } + if (dest->ismem) { + memcpy(ptr, dest->ptr + dest->pos, len); + } else { + newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len); + if (newlen != len) + return 1; + } - dest->pos += len; + dest->pos += len; - return 0; + return 0; } int Glulxe::write_long(dest_t *dest, uint val) { - unsigned char buf[4]; - Write4(buf, val); - return write_buffer(dest, buf, 4); + unsigned char buf[4]; + Write4(buf, val); + return write_buffer(dest, buf, 4); } int Glulxe::write_short(dest_t *dest, uint16 val) { - unsigned char buf[2]; - Write2(buf, val); - return write_buffer(dest, buf, 2); + unsigned char buf[2]; + Write2(buf, val); + return write_buffer(dest, buf, 2); } int Glulxe::write_byte(dest_t *dest, byte val) { - return write_buffer(dest, &val, 1); + return write_buffer(dest, &val, 1); } int Glulxe::read_long(dest_t *dest, uint *val) { - unsigned char buf[4]; - int res = read_buffer(dest, buf, 4); - if (res) - return res; - *val = Read4(buf); - return 0; + unsigned char buf[4]; + int res = read_buffer(dest, buf, 4); + if (res) + return res; + *val = Read4(buf); + return 0; } int Glulxe::read_short(dest_t *dest, uint16 *val) { - unsigned char buf[2]; - int res = read_buffer(dest, buf, 2); - if (res) - return res; - *val = Read2(buf); - return 0; + unsigned char buf[2]; + int res = read_buffer(dest, buf, 2); + if (res) + return res; + *val = Read2(buf); + return 0; } int Glulxe::read_byte(dest_t *dest, byte *val) { - return read_buffer(dest, val, 1); + return read_buffer(dest, val, 1); } uint Glulxe::write_memstate(dest_t *dest) { - uint res, pos; - int val; - int runlen; - unsigned char ch; + uint res, pos; + int val; + int runlen; + unsigned char ch; #ifdef SERIALIZE_CACHE_RAM - uint cachepos; + uint cachepos; #endif /* SERIALIZE_CACHE_RAM */ - res = write_long(dest, endmem); - if (res) - return res; + res = write_long(dest, endmem); + if (res) + return res; - runlen = 0; + runlen = 0; #ifdef SERIALIZE_CACHE_RAM - cachepos = 0; + cachepos = 0; #else /* SERIALIZE_CACHE_RAM */ - _gameFile.seek(gamefile_start + ramstart); + _gameFile.seek(gamefile_start + ramstart); #endif /* SERIALIZE_CACHE_RAM */ - for (pos=ramstart; pos<endmem; pos++) { - ch = Mem1(pos); - if (pos < endgamefile) { + for (pos = ramstart; pos < endmem; pos++) { + ch = Mem1(pos); + if (pos < endgamefile) { #ifdef SERIALIZE_CACHE_RAM - val = ramcache[cachepos]; - cachepos++; + val = ramcache[cachepos]; + cachepos++; #else /* SERIALIZE_CACHE_RAM */ - val = glk_get_char_stream(gamefile); - if (val == -1) { - fatal_error("The game file ended unexpectedly while saving."); - } + val = glk_get_char_stream(gamefile); + if (val == -1) { + fatal_error("The game file ended unexpectedly while saving."); + } #endif /* SERIALIZE_CACHE_RAM */ - ch ^= (unsigned char)val; - } - if (ch == 0) { - runlen++; - } - else { - /* Write any run we've got. */ - while (runlen) { - if (runlen >= 0x100) - val = 0x100; - else - val = runlen; - res = write_byte(dest, 0); - if (res) - return res; - res = write_byte(dest, (val-1)); - if (res) - return res; - runlen -= val; - } - /* Write the byte we got. */ - res = write_byte(dest, ch); - if (res) - return res; - } - } - /* It's possible we've got a run left over, but we don't write it. */ - - return 0; + ch ^= (unsigned char)val; + } + if (ch == 0) { + runlen++; + } else { + /* Write any run we've got. */ + while (runlen) { + if (runlen >= 0x100) + val = 0x100; + else + val = runlen; + res = write_byte(dest, 0); + if (res) + return res; + res = write_byte(dest, (val - 1)); + if (res) + return res; + runlen -= val; + } + /* Write the byte we got. */ + res = write_byte(dest, ch); + if (res) + return res; + } + } + /* It's possible we've got a run left over, but we don't write it. */ + + return 0; } uint Glulxe::read_memstate(dest_t *dest, uint chunklen) { - uint chunkend = dest->pos + chunklen; - uint newlen; - uint res, pos; - int val; - int runlen; - unsigned char ch, ch2; + uint chunkend = dest->pos + chunklen; + uint newlen; + uint res, pos; + int val; + int runlen; + unsigned char ch, ch2; #ifdef SERIALIZE_CACHE_RAM - uint cachepos; + uint cachepos; #endif /* SERIALIZE_CACHE_RAM */ - heap_clear(); + heap_clear(); - res = read_long(dest, &newlen); - if (res) - return res; + res = read_long(dest, &newlen); + if (res) + return res; - res = change_memsize(newlen, false); - if (res) - return res; + res = change_memsize(newlen, false); + if (res) + return res; - runlen = 0; + runlen = 0; #ifdef SERIALIZE_CACHE_RAM - cachepos = 0; + cachepos = 0; #else /* SERIALIZE_CACHE_RAM */ - _gameFile.seek(gamefile_start + ramstart); + _gameFile.seek(gamefile_start + ramstart); #endif /* SERIALIZE_CACHE_RAM */ - for (pos=ramstart; pos<endmem; pos++) { - if (pos < endgamefile) { + for (pos = ramstart; pos < endmem; pos++) { + if (pos < endgamefile) { #ifdef SERIALIZE_CACHE_RAM - val = ramcache[cachepos]; - cachepos++; + val = ramcache[cachepos]; + cachepos++; #else /* SERIALIZE_CACHE_RAM */ - if (_gameFile.pos() >= _gameFile.size()) { - fatal_error("The game file ended unexpectedly while restoring."); - val = _gameFile.readByte(); - } + if (_gameFile.pos() >= _gameFile.size()) { + fatal_error("The game file ended unexpectedly while restoring."); + val = _gameFile.readByte(); + } #endif /* SERIALIZE_CACHE_RAM */ - ch = (unsigned char)val; - } else { - ch = 0; - } - - if (dest->pos >= chunkend) { - /* we're into the final, unstored run. */ - } - else if (runlen) { - runlen--; - } - else { - res = read_byte(dest, &ch2); - if (res) - return res; - if (ch2 == 0) { - res = read_byte(dest, &ch2); - if (res) - return res; - runlen = (uint)ch2; - } - else { - ch ^= ch2; - } - } - - if (pos >= protectstart && pos < protectend) - continue; - - MemW1(pos, ch); - } - - return 0; + ch = (unsigned char)val; + } else { + ch = 0; + } + + if (dest->pos >= chunkend) { + /* we're into the final, unstored run. */ + } else if (runlen) { + runlen--; + } else { + res = read_byte(dest, &ch2); + if (res) + return res; + if (ch2 == 0) { + res = read_byte(dest, &ch2); + if (res) + return res; + runlen = (uint)ch2; + } else { + ch ^= ch2; + } + } + + if (pos >= protectstart && pos < protectend) + continue; + + MemW1(pos, ch); + } + + return 0; } uint Glulxe::write_heapstate(dest_t *dest, int portable) { - uint res; - uint sumlen; - uint *sumarray; + uint res; + uint sumlen; + uint *sumarray; - res = heap_get_summary(&sumlen, &sumarray); - if (res) - return res; + res = heap_get_summary(&sumlen, &sumarray); + if (res) + return res; - if (!sumarray) - return 0; /* no heap */ + if (!sumarray) + return 0; /* no heap */ - res = write_heapstate_sub(sumlen, sumarray, dest, portable); + res = write_heapstate_sub(sumlen, sumarray, dest, portable); - glulx_free(sumarray); - return res; + glulx_free(sumarray); + return res; } uint Glulxe::write_heapstate_sub(uint sumlen, uint *sumarray, dest_t *dest, int portable) { - uint res, lx; - - /* If we're storing for the purpose of undo, we don't need to do any - byte-swapping, because the result will only be used by this session. */ - if (!portable) { - res = write_buffer(dest, (const byte *)sumarray, sumlen * sizeof(uint)); - if (res) - return res; - return 0; - } - - for (lx=0; lx<sumlen; lx++) { - res = write_long(dest, sumarray[lx]); - if (res) - return res; - } - - return 0; + uint res, lx; + + /* If we're storing for the purpose of undo, we don't need to do any + byte-swapping, because the result will only be used by this session. */ + if (!portable) { + res = write_buffer(dest, (const byte *)sumarray, sumlen * sizeof(uint)); + if (res) + return res; + return 0; + } + + for (lx = 0; lx < sumlen; lx++) { + res = write_long(dest, sumarray[lx]); + if (res) + return res; + } + + return 0; } int Glulxe::sort_heap_summary(const void *p1, const void *p2) { - uint v1 = *(uint *)p1; - uint v2 = *(uint *)p2; - - if (v1 < v2) - return -1; - if (v1 > v2) - return 1; - return 0; + uint v1 = *(uint *)p1; + uint v2 = *(uint *)p2; + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; } uint Glulxe::read_heapstate(dest_t *dest, uint chunklen, int portable, uint *sumlen, uint **summary) { - uint res, count, lx; - uint *arr; + uint res, count, lx; + uint *arr; + + *sumlen = 0; + *summary = nullptr; - *sumlen = 0; - *summary = nullptr; + if (chunklen == 0) + return 0; /* no heap */ - if (chunklen == 0) - return 0; /* no heap */ + if (!portable) { + count = chunklen / sizeof(uint); - if (!portable) { - count = chunklen / sizeof(uint); + arr = (uint *)glulx_malloc(chunklen); + if (!arr) + return 1; - arr = (uint *)glulx_malloc(chunklen); - if (!arr) - return 1; + res = read_buffer(dest, (byte *)arr, chunklen); + if (res) + return res; - res = read_buffer(dest, (byte *)arr, chunklen); - if (res) - return res; + *sumlen = count; + *summary = arr; - *sumlen = count; - *summary = arr; + return 0; + } - return 0; - } + count = chunklen / 4; - count = chunklen / 4; + arr = (uint *)glulx_malloc(count * sizeof(uint)); + if (!arr) + return 1; - arr = (uint *)glulx_malloc(count * sizeof(uint)); - if (!arr) - return 1; - - for (lx=0; lx<count; lx++) { - res = read_long(dest, arr+lx); - if (res) - return res; - } + for (lx = 0; lx < count; lx++) { + res = read_long(dest, arr + lx); + if (res) + return res; + } - *sumlen = count; - *summary = arr; + *sumlen = count; + *summary = arr; - return 0; + return 0; } uint Glulxe::write_stackstate(dest_t *dest, int portable) { - uint res; - uint lx; - uint lastframe; - - /* If we're storing for the purpose of undo, we don't need to do any - byte-swapping, because the result will only be used by this session. */ - if (!portable) { - res = write_buffer(dest, stack, stackptr); - if (res) - return res; - return 0; - } - - /* Write a portable stack image. To do this, we have to write stack - frames in order, bottom to top. Remember that the last word of - every stack frame is a pointer to the beginning of that stack frame. - (This includes the last frame, because the save opcode pushes on - a call stub before it calls perform_save().) */ - - lastframe = (uint)(-1); - while (1) { - uint frameend, frm, frm2, frm3; - unsigned char loctype, loccount; - uint numlocals, frlen, locpos; - - /* Find the next stack frame (after the one in lastframe). Sadly, - this requires searching the stack from the top down. We have to - do this for *every* frame, which takes N^2 time overall. But - save routines usually aren't nested very deep. - If it becomes a practical problem, we can build a stack-frame - array, which requires dynamic allocation. */ - for (frm = stackptr, frameend = stackptr; - frm != 0 && (frm2 = Stk4(frm-4)) != lastframe; - frameend = frm, frm = frm2) { }; - - /* Write out the frame. */ - frm2 = frm; - - frlen = Stk4(frm2); - frm2 += 4; - res = write_long(dest, frlen); - if (res) - return res; - locpos = Stk4(frm2); - frm2 += 4; - res = write_long(dest, locpos); - if (res) - return res; - - frm3 = frm2; - - numlocals = 0; - while (1) { - loctype = Stk1(frm2); - frm2 += 1; - loccount = Stk1(frm2); - frm2 += 1; - - res = write_byte(dest, loctype); - if (res) - return res; - res = write_byte(dest, loccount); - if (res) - return res; - - if (loctype == 0 && loccount == 0) - break; - - numlocals++; - } - - if ((numlocals & 1) == 0) { - res = write_byte(dest, 0); - if (res) - return res; - res = write_byte(dest, 0); - if (res) - return res; - frm2 += 2; - } - - if (frm2 != frm+locpos) - fatal_error("Inconsistent stack frame during save."); - - /* Write out the locals. */ - for (lx=0; lx<numlocals; lx++) { - loctype = Stk1(frm3); - frm3 += 1; - loccount = Stk1(frm3); - frm3 += 1; - - if (loctype == 0 && loccount == 0) - break; - - /* Put in up to 0, 1, or 3 bytes of padding, depending on loctype. */ - while (frm2 & (loctype-1)) { - res = write_byte(dest, 0); - if (res) - return res; - frm2 += 1; - } - - /* Put in this set of locals. */ - switch (loctype) { - - case 1: - do { - res = write_byte(dest, Stk1(frm2)); - if (res) - return res; - frm2 += 1; - loccount--; - } while (loccount); - break; - - case 2: - do { - res = write_short(dest, Stk2(frm2)); - if (res) - return res; - frm2 += 2; - loccount--; - } while (loccount); - break; - - case 4: - do { - res = write_long(dest, Stk4(frm2)); - if (res) - return res; - frm2 += 4; - loccount--; - } while (loccount); - break; - - } - } - - if (frm2 != frm+frlen) - fatal_error("Inconsistent stack frame during save."); - - while (frm2 < frameend) { - res = write_long(dest, Stk4(frm2)); - if (res) - return res; - frm2 += 4; - } - - /* Go on to the next frame. */ - if (frameend == stackptr) - break; /* All done. */ - lastframe = frm; - } - - return 0; + uint res; + uint lx; + uint lastframe; + + /* If we're storing for the purpose of undo, we don't need to do any + byte-swapping, because the result will only be used by this session. */ + if (!portable) { + res = write_buffer(dest, stack, stackptr); + if (res) + return res; + return 0; + } + + /* Write a portable stack image. To do this, we have to write stack + frames in order, bottom to top. Remember that the last word of + every stack frame is a pointer to the beginning of that stack frame. + (This includes the last frame, because the save opcode pushes on + a call stub before it calls perform_save().) */ + + lastframe = (uint)(-1); + while (1) { + uint frameend, frm, frm2, frm3; + unsigned char loctype, loccount; + uint numlocals, frlen, locpos; + + /* Find the next stack frame (after the one in lastframe). Sadly, + this requires searching the stack from the top down. We have to + do this for *every* frame, which takes N^2 time overall. But + save routines usually aren't nested very deep. + If it becomes a practical problem, we can build a stack-frame + array, which requires dynamic allocation. */ + for (frm = stackptr, frameend = stackptr; + frm != 0 && (frm2 = Stk4(frm - 4)) != lastframe; + frameend = frm, frm = frm2) { }; + + /* Write out the frame. */ + frm2 = frm; + + frlen = Stk4(frm2); + frm2 += 4; + res = write_long(dest, frlen); + if (res) + return res; + locpos = Stk4(frm2); + frm2 += 4; + res = write_long(dest, locpos); + if (res) + return res; + + frm3 = frm2; + + numlocals = 0; + while (1) { + loctype = Stk1(frm2); + frm2 += 1; + loccount = Stk1(frm2); + frm2 += 1; + + res = write_byte(dest, loctype); + if (res) + return res; + res = write_byte(dest, loccount); + if (res) + return res; + + if (loctype == 0 && loccount == 0) + break; + + numlocals++; + } + + if ((numlocals & 1) == 0) { + res = write_byte(dest, 0); + if (res) + return res; + res = write_byte(dest, 0); + if (res) + return res; + frm2 += 2; + } + + if (frm2 != frm + locpos) + fatal_error("Inconsistent stack frame during save."); + + /* Write out the locals. */ + for (lx = 0; lx < numlocals; lx++) { + loctype = Stk1(frm3); + frm3 += 1; + loccount = Stk1(frm3); + frm3 += 1; + + if (loctype == 0 && loccount == 0) + break; + + /* Put in up to 0, 1, or 3 bytes of padding, depending on loctype. */ + while (frm2 & (loctype - 1)) { + res = write_byte(dest, 0); + if (res) + return res; + frm2 += 1; + } + + /* Put in this set of locals. */ + switch (loctype) { + + case 1: + do { + res = write_byte(dest, Stk1(frm2)); + if (res) + return res; + frm2 += 1; + loccount--; + } while (loccount); + break; + + case 2: + do { + res = write_short(dest, Stk2(frm2)); + if (res) + return res; + frm2 += 2; + loccount--; + } while (loccount); + break; + + case 4: + do { + res = write_long(dest, Stk4(frm2)); + if (res) + return res; + frm2 += 4; + loccount--; + } while (loccount); + break; + + } + } + + if (frm2 != frm + frlen) + fatal_error("Inconsistent stack frame during save."); + + while (frm2 < frameend) { + res = write_long(dest, Stk4(frm2)); + if (res) + return res; + frm2 += 4; + } + + /* Go on to the next frame. */ + if (frameend == stackptr) + break; /* All done. */ + lastframe = frm; + } + + return 0; } uint Glulxe::read_stackstate(dest_t *dest, uint chunklen, int portable) { - uint res; - uint frameend, frm, frm2, frm3, locpos, frlen, numlocals; - - if (chunklen > stacksize) - return 1; - - stackptr = chunklen; - frameptr = 0; - valstackbase = 0; - localsbase = 0; - - if (!portable) { - res = read_buffer(dest, stack, stackptr); - if (res) - return res; - return 0; - } - - /* This isn't going to be pleasant; we're going to read the data in - as a block, and then convert it in-place. */ - res = read_buffer(dest, stack, stackptr); - if (res) - return res; - - frameend = stackptr; - while (frameend != 0) { - /* Read the beginning-of-frame pointer. Remember, right now, the - whole frame is stored big-endian. So we have to read with the - Read*() macros, and then write with the StkW*() macros. */ - frm = Read4(stack+(frameend-4)); - - frm2 = frm; - - frlen = Read4(stack+frm2); - StkW4(frm2, frlen); - frm2 += 4; - locpos = Read4(stack+frm2); - StkW4(frm2, locpos); - frm2 += 4; - - /* The locals-format list is in bytes, so we don't have to convert it. */ - frm3 = frm2; - frm2 = frm+locpos; - - numlocals = 0; - - while (1) { - unsigned char loctype, loccount; - loctype = Read1(stack+frm3); - frm3 += 1; - loccount = Read1(stack+frm3); - frm3 += 1; - - if (loctype == 0 && loccount == 0) - break; - - /* Skip up to 0, 1, or 3 bytes of padding, depending on loctype. */ - while (frm2 & (loctype-1)) { - StkW1(frm2, 0); - frm2++; - } - - /* Convert this set of locals. */ - switch (loctype) { - - case 1: - do { - /* Don't need to convert bytes. */ - frm2 += 1; - loccount--; - } while (loccount); - break; - - case 2: - do { - uint16 loc = Read2(stack+frm2); - StkW2(frm2, loc); - frm2 += 2; - loccount--; - } while (loccount); - break; - - case 4: - do { - uint loc = Read4(stack+frm2); - StkW4(frm2, loc); - frm2 += 4; - loccount--; - } while (loccount); - break; - - } - - numlocals++; - } - - if ((numlocals & 1) == 0) { - StkW1(frm3, 0); - frm3++; - StkW1(frm3, 0); - frm3++; - } - - if (frm3 != frm+locpos) { - return 1; - } - - while (frm2 & 3) { - StkW1(frm2, 0); - frm2++; - } - - if (frm2 != frm+frlen) { - return 1; - } - - /* Now, the values pushed on the stack after the call frame itself. - This includes the stub. */ - while (frm2 < frameend) { - uint loc = Read4(stack+frm2); - StkW4(frm2, loc); - frm2 += 4; - } - - frameend = frm; - } - - return 0; + uint res; + uint frameend, frm, frm2, frm3, locpos, frlen, numlocals; + + if (chunklen > stacksize) + return 1; + + stackptr = chunklen; + frameptr = 0; + valstackbase = 0; + localsbase = 0; + + if (!portable) { + res = read_buffer(dest, stack, stackptr); + if (res) + return res; + return 0; + } + + /* This isn't going to be pleasant; we're going to read the data in + as a block, and then convert it in-place. */ + res = read_buffer(dest, stack, stackptr); + if (res) + return res; + + frameend = stackptr; + while (frameend != 0) { + /* Read the beginning-of-frame pointer. Remember, right now, the + whole frame is stored big-endian. So we have to read with the + Read*() macros, and then write with the StkW*() macros. */ + frm = Read4(stack + (frameend - 4)); + + frm2 = frm; + + frlen = Read4(stack + frm2); + StkW4(frm2, frlen); + frm2 += 4; + locpos = Read4(stack + frm2); + StkW4(frm2, locpos); + frm2 += 4; + + /* The locals-format list is in bytes, so we don't have to convert it. */ + frm3 = frm2; + frm2 = frm + locpos; + + numlocals = 0; + + while (1) { + unsigned char loctype, loccount; + loctype = Read1(stack + frm3); + frm3 += 1; + loccount = Read1(stack + frm3); + frm3 += 1; + + if (loctype == 0 && loccount == 0) + break; + + /* Skip up to 0, 1, or 3 bytes of padding, depending on loctype. */ + while (frm2 & (loctype - 1)) { + StkW1(frm2, 0); + frm2++; + } + + /* Convert this set of locals. */ + switch (loctype) { + + case 1: + do { + /* Don't need to convert bytes. */ + frm2 += 1; + loccount--; + } while (loccount); + break; + + case 2: + do { + uint16 loc = Read2(stack + frm2); + StkW2(frm2, loc); + frm2 += 2; + loccount--; + } while (loccount); + break; + + case 4: + do { + uint loc = Read4(stack + frm2); + StkW4(frm2, loc); + frm2 += 4; + loccount--; + } while (loccount); + break; + + } + + numlocals++; + } + + if ((numlocals & 1) == 0) { + StkW1(frm3, 0); + frm3++; + StkW1(frm3, 0); + frm3++; + } + + if (frm3 != frm + locpos) { + return 1; + } + + while (frm2 & 3) { + StkW1(frm2, 0); + frm2++; + } + + if (frm2 != frm + frlen) { + return 1; + } + + /* Now, the values pushed on the stack after the call frame itself. + This includes the stub. */ + while (frm2 < frameend) { + uint loc = Read4(stack + frm2); + StkW4(frm2, loc); + frm2 += 4; + } + + frameend = frm; + } + + return 0; } uint Glulxe::perform_verify() { - uint len, chksum = 0, newlen; - unsigned char buf[4]; - uint val, newsum, ix; - - len = gamefile_len; - - if (len < 256 || (len & 0xFF) != 0) - return 1; - - _gameFile.seek(gamefile_start); - newsum = 0; - - /* Read the header */ - for (ix=0; ix<9; ix++) { - newlen = _gameFile.read(buf, 4); - if (newlen != 4) - return 1; - val = Read4(buf); - if (ix == 3) { - if (len != val) - return 1; - } - if (ix == 8) - chksum = val; - else - newsum += val; - } - - /* Read everything else */ - for (; ix < len/4; ix++) { - newlen = _gameFile.read(buf, 4); - if (newlen != 4) - return 1; - val = Read4(buf); - newsum += val; - } - - if (newsum != chksum) - return 1; - - return 0; + uint len, chksum = 0, newlen; + unsigned char buf[4]; + uint val, newsum, ix; + + len = gamefile_len; + + if (len < 256 || (len & 0xFF) != 0) + return 1; + + _gameFile.seek(gamefile_start); + newsum = 0; + + /* Read the header */ + for (ix = 0; ix < 9; ix++) { + newlen = _gameFile.read(buf, 4); + if (newlen != 4) + return 1; + val = Read4(buf); + if (ix == 3) { + if (len != val) + return 1; + } + if (ix == 8) + chksum = val; + else + newsum += val; + } + + /* Read everything else */ + for (; ix < len / 4; ix++) { + newlen = _gameFile.read(buf, 4); + if (newlen != 4) + return 1; + val = Read4(buf); + newsum += val; + } + + if (newsum != chksum) + return 1; + + return 0; } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/string.cpp b/engines/glk/glulxe/string.cpp index 67a869edaf..a24a9b8b2f 100644 --- a/engines/glk/glulxe/string.cpp +++ b/engines/glk/glulxe/string.cpp @@ -26,50 +26,50 @@ namespace Glk { namespace Glulxe { void Glulxe::stream_get_iosys(uint *mode, uint *rock) { - *mode = iosys_mode; - *rock = iosys_rock; + *mode = iosys_mode; + *rock = iosys_rock; } void Glulxe::stream_setup_unichar() { #ifdef GLK_MODULE_UNICODE - if (glk_gestalt(gestalt_Unicode, 0)) - glkio_unichar_han_ptr = &Glulxe::glk_put_char_uni; - else - glkio_unichar_han_ptr = &Glulxe::glkio_unichar_nouni_han; + if (glk_gestalt(gestalt_Unicode, 0)) + glkio_unichar_han_ptr = &Glulxe::glk_put_char_uni; + else + glkio_unichar_han_ptr = &Glulxe::glkio_unichar_nouni_han; #else /* GLK_MODULE_UNICODE */ - glkio_unichar_han_ptr = glkio_unichar_nouni_han; + glkio_unichar_han_ptr = glkio_unichar_nouni_han; #endif /* GLK_MODULE_UNICODE */ } void Glulxe::stream_set_iosys(uint mode, uint rock) { - switch (mode) { - default: - mode = 0; - /* ...and fall through to next case (no-op I/O). */ - case iosys_None: - rock = 0; - stream_char_handler = &Glulxe::nopio_char_han; - stream_unichar_handler = &Glulxe::nopio_unichar_han; - break; - case iosys_Filter: - stream_char_handler = &Glulxe::filio_char_han; - stream_unichar_handler = &Glulxe::filio_unichar_han; - break; - case iosys_Glk: - if (!glkio_unichar_han_ptr) - stream_setup_unichar(); - rock = 0; - stream_char_handler = &Glulxe::glk_put_char; - stream_unichar_handler = glkio_unichar_han_ptr; - break; - } - - iosys_mode = mode; - iosys_rock = rock; + switch (mode) { + default: + mode = 0; + /* ...and fall through to next case (no-op I/O). */ + case iosys_None: + rock = 0; + stream_char_handler = &Glulxe::nopio_char_han; + stream_unichar_handler = &Glulxe::nopio_unichar_han; + break; + case iosys_Filter: + stream_char_handler = &Glulxe::filio_char_han; + stream_unichar_handler = &Glulxe::filio_unichar_han; + break; + case iosys_Glk: + if (!glkio_unichar_han_ptr) + stream_setup_unichar(); + rock = 0; + stream_char_handler = &Glulxe::glk_put_char; + stream_unichar_handler = glkio_unichar_han_ptr; + break; + } + + iosys_mode = mode; + iosys_rock = rock; } void Glulxe::nopio_char_han(unsigned char ch) { @@ -79,749 +79,728 @@ void Glulxe::nopio_unichar_han(uint ch) { } void Glulxe::filio_char_han(unsigned char ch) { - uint val = ch; - push_callstub(0, 0); - enter_function(iosys_rock, 1, &val); + uint val = ch; + push_callstub(0, 0); + enter_function(iosys_rock, 1, &val); } void Glulxe::filio_unichar_han(uint val) { - push_callstub(0, 0); - enter_function(iosys_rock, 1, &val); + push_callstub(0, 0); + enter_function(iosys_rock, 1, &val); } void Glulxe::glkio_unichar_nouni_han(uint val) { - /* Only used if the Glk library has no Unicode functions */ - if (val > 0xFF) - val = '?'; - glk_put_char(val); + /* Only used if the Glk library has no Unicode functions */ + if (val > 0xFF) + val = '?'; + glk_put_char(val); } void Glulxe::stream_num(int val, int inmiddle, int charnum) { - int ix = 0; - int res, jx; - char buf[16]; - uint ival; - - if (val == 0) { - buf[ix] = '0'; - ix++; - } - else { - if (val < 0) - ival = -val; - else - ival = val; - - while (ival != 0) { - buf[ix] = (ival % 10) + '0'; - ix++; - ival /= 10; - } - - if (val < 0) { - buf[ix] = '-'; - ix++; - } - } - - switch (iosys_mode) { - - case iosys_Glk: - ix -= charnum; - while (ix > 0) { - ix--; - glk_put_char(buf[ix]); - } - break; - - case iosys_Filter: - if (!inmiddle) { - push_callstub(0x11, 0); - inmiddle = true; - } - if (charnum < ix) { - ival = buf[(ix-1)-charnum] & 0xFF; - pc = val; - push_callstub(0x12, charnum+1); - enter_function(iosys_rock, 1, &ival); - return; - } - break; - - default: - break; - - } - - if (inmiddle) { - res = pop_callstub_string(&jx); - if (res) - fatal_error("String-on-string call stub while printing number."); - } + int ix = 0; + int res, jx; + char buf[16]; + uint ival; + + if (val == 0) { + buf[ix] = '0'; + ix++; + } else { + if (val < 0) + ival = -val; + else + ival = val; + + while (ival != 0) { + buf[ix] = (ival % 10) + '0'; + ix++; + ival /= 10; + } + + if (val < 0) { + buf[ix] = '-'; + ix++; + } + } + + switch (iosys_mode) { + + case iosys_Glk: + ix -= charnum; + while (ix > 0) { + ix--; + glk_put_char(buf[ix]); + } + break; + + case iosys_Filter: + if (!inmiddle) { + push_callstub(0x11, 0); + inmiddle = true; + } + if (charnum < ix) { + ival = buf[(ix - 1) - charnum] & 0xFF; + pc = val; + push_callstub(0x12, charnum + 1); + enter_function(iosys_rock, 1, &ival); + return; + } + break; + + default: + break; + + } + + if (inmiddle) { + res = pop_callstub_string(&jx); + if (res) + fatal_error("String-on-string call stub while printing number."); + } } void Glulxe::stream_string(uint addr, int inmiddle, int bitnum) { - int ch; - int type; - int alldone = false; - int substring = (inmiddle != 0); - uint ival; - - if (!addr) - fatal_error("Called stream_string with null address."); - - while (!alldone) { - - if (inmiddle == 0) { - type = Mem1(addr); - if (type == 0xE2) - addr+=4; - else - addr++; - bitnum = 0; - } - else { - type = inmiddle; - } - - if (type == 0xE1) { - if (tablecache_valid) { - int bits, numbits; - int readahead; - uint tmpaddr; - cacheblock_t *cablist; - int done = 0; - - /* bitnum is already set right */ - bits = Mem1(addr); - if (bitnum) - bits >>= bitnum; - numbits = (8 - bitnum); - readahead = false; - - if (tablecache.type != 0) { - /* This is a bit of a cheat. If the top-level block is not - a branch, then it must be a string-terminator -- otherwise - the string would be an infinite repetition of that block. - We check for this case and bail immediately. */ - done = 1; - } - - cablist = tablecache.u.branches; - while (!done) { - cacheblock_t *cab; - - if (numbits < CACHEBITS) { - /* readahead is certainly false */ - int newbyte = Mem1(addr+1); - bits |= (newbyte << numbits); - numbits += 8; - readahead = true; - } - - cab = &(cablist[bits & CACHEMASK]); - numbits -= cab->depth; - bits >>= cab->depth; - bitnum += cab->depth; - if (bitnum >= 8) { - addr += 1; - bitnum -= 8; - if (readahead) { - readahead = false; - } - else { - int newbyte = Mem1(addr); - bits |= (newbyte << numbits); - numbits += 8; - } - } - - switch (cab->type) { - case 0x00: /* non-leaf node */ - cablist = cab->u.branches; - break; - case 0x01: /* string terminator */ - done = 1; - break; - case 0x02: /* single character */ - switch (iosys_mode) { - case iosys_Glk: - glk_put_char(cab->u.ch); - break; - case iosys_Filter: - ival = cab->u.ch & 0xFF; - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - enter_function(iosys_rock, 1, &ival); - return; - } - cablist = tablecache.u.branches; - break; - case 0x04: /* single Unicode character */ - switch (iosys_mode) { - case iosys_Glk: - (this->*glkio_unichar_han_ptr)(cab->u.uch); - break; - case iosys_Filter: - ival = cab->u.uch; - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - enter_function(iosys_rock, 1, &ival); - return; - } - cablist = tablecache.u.branches; - break; - case 0x03: /* C string */ - switch (iosys_mode) { - case iosys_Glk: - for (tmpaddr=cab->u.addr; (ch=Mem1(tmpaddr)) != '\0'; tmpaddr++) - glk_put_char(ch); - cablist = tablecache.u.branches; - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - inmiddle = 0xE0; - addr = cab->u.addr; - done = 2; - break; - default: - cablist = tablecache.u.branches; - break; - } - break; - case 0x05: /* C Unicode string */ - switch (iosys_mode) { - case iosys_Glk: - for (tmpaddr=cab->u.addr; (ival=Mem4(tmpaddr)) != 0; tmpaddr+=4) - (this->*glkio_unichar_han_ptr)(ival); - cablist = tablecache.u.branches; - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - inmiddle = 0xE2; - addr = cab->u.addr; - done = 2; - break; - default: - cablist = tablecache.u.branches; - break; - } - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x0B: - { - uint oaddr; - int otype; - oaddr = cab->u.addr; - if (cab->type >= 0x09) - oaddr = Mem4(oaddr); - if (cab->type == 0x0B) - oaddr = Mem4(oaddr); - otype = Mem1(oaddr); - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - if (otype >= 0xE0 && otype <= 0xFF) { - pc = addr; - push_callstub(0x10, bitnum); - inmiddle = 0; - addr = oaddr; - done = 2; - } - else if (otype >= 0xC0 && otype <= 0xDF) { - uint argc; - uint *argv; - if (cab->type == 0x0A || cab->type == 0x0B) { - argc = Mem4(cab->u.addr+4); - argv = pop_arguments(argc, cab->u.addr+8); - } - else { - argc = 0; - argv = nullptr; - } - pc = addr; - push_callstub(0x10, bitnum); - enter_function(oaddr, argc, argv); - return; - } - else { - fatal_error("Unknown object while decoding string indirect reference."); - } - } - break; - default: - fatal_error("Unknown entity in string decoding (cached)."); - break; - } - } - if (done > 1) { - continue; /* restart the top-level loop */ - } - } - else { /* tablecache not valid */ - uint node; - int byte1; - int nodetype; - int done = 0; - - if (!stringtable) - fatal_error("Attempted to print a compressed string with no table set."); - /* bitnum is already set right */ - byte1 = Mem1(addr); - if (bitnum) - byte1 >>= bitnum; - node = Mem4(stringtable+8); - while (!done) { - nodetype = Mem1(node); - node++; - switch (nodetype) { - case 0x00: /* non-leaf node */ - if (byte1 & 1) - node = Mem4(node+4); - else - node = Mem4(node+0); - if (bitnum == 7) { - bitnum = 0; - addr++; - byte1 = Mem1(addr); - } - else { - bitnum++; - byte1 >>= 1; - } - break; - case 0x01: /* string terminator */ - done = 1; - break; - case 0x02: /* single character */ - ch = Mem1(node); - switch (iosys_mode) { - case iosys_Glk: - glk_put_char(ch); - break; - case iosys_Filter: - ival = ch & 0xFF; - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - enter_function(iosys_rock, 1, &ival); - return; - } - node = Mem4(stringtable+8); - break; - case 0x04: /* single Unicode character */ - ival = Mem4(node); - switch (iosys_mode) { - case iosys_Glk: - (this->*glkio_unichar_han_ptr)(ival); - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - enter_function(iosys_rock, 1, &ival); - return; - } - node = Mem4(stringtable+8); - break; - case 0x03: /* C string */ - switch (iosys_mode) { - case iosys_Glk: - for (; (ch=Mem1(node)) != '\0'; node++) - glk_put_char(ch); - node = Mem4(stringtable+8); - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - inmiddle = 0xE0; - addr = node; - done = 2; - break; - default: - node = Mem4(stringtable+8); - break; - } - break; - case 0x05: /* C Unicode string */ - switch (iosys_mode) { - case iosys_Glk: - for (; (ival=Mem4(node)) != 0; node+=4) - (this->*glkio_unichar_han_ptr)(ival); - node = Mem4(stringtable+8); - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - pc = addr; - push_callstub(0x10, bitnum); - inmiddle = 0xE2; - addr = node; - done = 2; - break; - default: - node = Mem4(stringtable+8); - break; - } - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x0B: - { - uint oaddr; - int otype; - oaddr = Mem4(node); - if (nodetype == 0x09 || nodetype == 0x0B) - oaddr = Mem4(oaddr); - otype = Mem1(oaddr); - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - if (otype >= 0xE0 && otype <= 0xFF) { - pc = addr; - push_callstub(0x10, bitnum); - inmiddle = 0; - addr = oaddr; - done = 2; - } - else if (otype >= 0xC0 && otype <= 0xDF) { - uint argc; - uint *argv; - if (nodetype == 0x0A || nodetype == 0x0B) { - argc = Mem4(node+4); - argv = pop_arguments(argc, node+8); - } - else { - argc = 0; - argv = nullptr; - } - pc = addr; - push_callstub(0x10, bitnum); - enter_function(oaddr, argc, argv); - return; - } - else { - fatal_error("Unknown object while decoding string indirect reference."); - } - } - break; - default: - fatal_error("Unknown entity in string decoding."); - break; - } - } - if (done > 1) { - continue; /* restart the top-level loop */ - } - } - } - else if (type == 0xE0) { - switch (iosys_mode) { - case iosys_Glk: - while (1) { - ch = Mem1(addr); - addr++; - if (ch == '\0') - break; - glk_put_char(ch); - } - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - ch = Mem1(addr); - addr++; - if (ch != '\0') { - ival = ch & 0xFF; - pc = addr; - push_callstub(0x13, 0); - enter_function(iosys_rock, 1, &ival); - return; - } - break; - } - } - else if (type == 0xE2) { - switch (iosys_mode) { - case iosys_Glk: - while (1) { - ival = Mem4(addr); - addr+=4; - if (ival == 0) - break; - (this->*glkio_unichar_han_ptr)(ival); - } - break; - case iosys_Filter: - if (!substring) { - push_callstub(0x11, 0); - substring = true; - } - ival = Mem4(addr); - addr+=4; - if (ival != 0) { - pc = addr; - push_callstub(0x14, 0); - enter_function(iosys_rock, 1, &ival); - return; - } - break; - } - } - else if (type >= 0xE0 && type <= 0xFF) { - fatal_error("Attempt to print unknown type of string."); - } - else { - fatal_error("Attempt to print non-string."); - } - - if (!substring) { - /* Just get straight out. */ - alldone = true; - } - else { - /* Pop a stub and see what's to be done. */ - addr = pop_callstub_string(&bitnum); - if (addr == 0) { - alldone = true; - } - else { - inmiddle = 0xE1; - } - } - } + int ch; + int type; + int alldone = false; + int substring = (inmiddle != 0); + uint ival; + + if (!addr) + fatal_error("Called stream_string with null address."); + + while (!alldone) { + + if (inmiddle == 0) { + type = Mem1(addr); + if (type == 0xE2) + addr += 4; + else + addr++; + bitnum = 0; + } else { + type = inmiddle; + } + + if (type == 0xE1) { + if (tablecache_valid) { + int bits, numbits; + int readahead; + uint tmpaddr; + cacheblock_t *cablist; + int done = 0; + + /* bitnum is already set right */ + bits = Mem1(addr); + if (bitnum) + bits >>= bitnum; + numbits = (8 - bitnum); + readahead = false; + + if (tablecache.type != 0) { + /* This is a bit of a cheat. If the top-level block is not + a branch, then it must be a string-terminator -- otherwise + the string would be an infinite repetition of that block. + We check for this case and bail immediately. */ + done = 1; + } + + cablist = tablecache.u.branches; + while (!done) { + cacheblock_t *cab; + + if (numbits < CACHEBITS) { + /* readahead is certainly false */ + int newbyte = Mem1(addr + 1); + bits |= (newbyte << numbits); + numbits += 8; + readahead = true; + } + + cab = &(cablist[bits & CACHEMASK]); + numbits -= cab->depth; + bits >>= cab->depth; + bitnum += cab->depth; + if (bitnum >= 8) { + addr += 1; + bitnum -= 8; + if (readahead) { + readahead = false; + } else { + int newbyte = Mem1(addr); + bits |= (newbyte << numbits); + numbits += 8; + } + } + + switch (cab->type) { + case 0x00: /* non-leaf node */ + cablist = cab->u.branches; + break; + case 0x01: /* string terminator */ + done = 1; + break; + case 0x02: /* single character */ + switch (iosys_mode) { + case iosys_Glk: + glk_put_char(cab->u.ch); + break; + case iosys_Filter: + ival = cab->u.ch & 0xFF; + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + enter_function(iosys_rock, 1, &ival); + return; + } + cablist = tablecache.u.branches; + break; + case 0x04: /* single Unicode character */ + switch (iosys_mode) { + case iosys_Glk: + (this->*glkio_unichar_han_ptr)(cab->u.uch); + break; + case iosys_Filter: + ival = cab->u.uch; + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + enter_function(iosys_rock, 1, &ival); + return; + } + cablist = tablecache.u.branches; + break; + case 0x03: /* C string */ + switch (iosys_mode) { + case iosys_Glk: + for (tmpaddr = cab->u.addr; (ch = Mem1(tmpaddr)) != '\0'; tmpaddr++) + glk_put_char(ch); + cablist = tablecache.u.branches; + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + inmiddle = 0xE0; + addr = cab->u.addr; + done = 2; + break; + default: + cablist = tablecache.u.branches; + break; + } + break; + case 0x05: /* C Unicode string */ + switch (iosys_mode) { + case iosys_Glk: + for (tmpaddr = cab->u.addr; (ival = Mem4(tmpaddr)) != 0; tmpaddr += 4) + (this->*glkio_unichar_han_ptr)(ival); + cablist = tablecache.u.branches; + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + inmiddle = 0xE2; + addr = cab->u.addr; + done = 2; + break; + default: + cablist = tablecache.u.branches; + break; + } + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: { + uint oaddr; + int otype; + oaddr = cab->u.addr; + if (cab->type >= 0x09) + oaddr = Mem4(oaddr); + if (cab->type == 0x0B) + oaddr = Mem4(oaddr); + otype = Mem1(oaddr); + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + if (otype >= 0xE0 && otype <= 0xFF) { + pc = addr; + push_callstub(0x10, bitnum); + inmiddle = 0; + addr = oaddr; + done = 2; + } else if (otype >= 0xC0 && otype <= 0xDF) { + uint argc; + uint *argv; + if (cab->type == 0x0A || cab->type == 0x0B) { + argc = Mem4(cab->u.addr + 4); + argv = pop_arguments(argc, cab->u.addr + 8); + } else { + argc = 0; + argv = nullptr; + } + pc = addr; + push_callstub(0x10, bitnum); + enter_function(oaddr, argc, argv); + return; + } else { + fatal_error("Unknown object while decoding string indirect reference."); + } + } + break; + default: + fatal_error("Unknown entity in string decoding (cached)."); + break; + } + } + if (done > 1) { + continue; /* restart the top-level loop */ + } + } else { /* tablecache not valid */ + uint node; + int byte1; + int nodetype; + int done = 0; + + if (!stringtable) + fatal_error("Attempted to print a compressed string with no table set."); + /* bitnum is already set right */ + byte1 = Mem1(addr); + if (bitnum) + byte1 >>= bitnum; + node = Mem4(stringtable + 8); + while (!done) { + nodetype = Mem1(node); + node++; + switch (nodetype) { + case 0x00: /* non-leaf node */ + if (byte1 & 1) + node = Mem4(node + 4); + else + node = Mem4(node + 0); + if (bitnum == 7) { + bitnum = 0; + addr++; + byte1 = Mem1(addr); + } else { + bitnum++; + byte1 >>= 1; + } + break; + case 0x01: /* string terminator */ + done = 1; + break; + case 0x02: /* single character */ + ch = Mem1(node); + switch (iosys_mode) { + case iosys_Glk: + glk_put_char(ch); + break; + case iosys_Filter: + ival = ch & 0xFF; + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + enter_function(iosys_rock, 1, &ival); + return; + } + node = Mem4(stringtable + 8); + break; + case 0x04: /* single Unicode character */ + ival = Mem4(node); + switch (iosys_mode) { + case iosys_Glk: + (this->*glkio_unichar_han_ptr)(ival); + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + enter_function(iosys_rock, 1, &ival); + return; + } + node = Mem4(stringtable + 8); + break; + case 0x03: /* C string */ + switch (iosys_mode) { + case iosys_Glk: + for (; (ch = Mem1(node)) != '\0'; node++) + glk_put_char(ch); + node = Mem4(stringtable + 8); + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + inmiddle = 0xE0; + addr = node; + done = 2; + break; + default: + node = Mem4(stringtable + 8); + break; + } + break; + case 0x05: /* C Unicode string */ + switch (iosys_mode) { + case iosys_Glk: + for (; (ival = Mem4(node)) != 0; node += 4) + (this->*glkio_unichar_han_ptr)(ival); + node = Mem4(stringtable + 8); + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + pc = addr; + push_callstub(0x10, bitnum); + inmiddle = 0xE2; + addr = node; + done = 2; + break; + default: + node = Mem4(stringtable + 8); + break; + } + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: { + uint oaddr; + int otype; + oaddr = Mem4(node); + if (nodetype == 0x09 || nodetype == 0x0B) + oaddr = Mem4(oaddr); + otype = Mem1(oaddr); + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + if (otype >= 0xE0 && otype <= 0xFF) { + pc = addr; + push_callstub(0x10, bitnum); + inmiddle = 0; + addr = oaddr; + done = 2; + } else if (otype >= 0xC0 && otype <= 0xDF) { + uint argc; + uint *argv; + if (nodetype == 0x0A || nodetype == 0x0B) { + argc = Mem4(node + 4); + argv = pop_arguments(argc, node + 8); + } else { + argc = 0; + argv = nullptr; + } + pc = addr; + push_callstub(0x10, bitnum); + enter_function(oaddr, argc, argv); + return; + } else { + fatal_error("Unknown object while decoding string indirect reference."); + } + } + break; + default: + fatal_error("Unknown entity in string decoding."); + break; + } + } + if (done > 1) { + continue; /* restart the top-level loop */ + } + } + } else if (type == 0xE0) { + switch (iosys_mode) { + case iosys_Glk: + while (1) { + ch = Mem1(addr); + addr++; + if (ch == '\0') + break; + glk_put_char(ch); + } + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + ch = Mem1(addr); + addr++; + if (ch != '\0') { + ival = ch & 0xFF; + pc = addr; + push_callstub(0x13, 0); + enter_function(iosys_rock, 1, &ival); + return; + } + break; + } + } else if (type == 0xE2) { + switch (iosys_mode) { + case iosys_Glk: + while (1) { + ival = Mem4(addr); + addr += 4; + if (ival == 0) + break; + (this->*glkio_unichar_han_ptr)(ival); + } + break; + case iosys_Filter: + if (!substring) { + push_callstub(0x11, 0); + substring = true; + } + ival = Mem4(addr); + addr += 4; + if (ival != 0) { + pc = addr; + push_callstub(0x14, 0); + enter_function(iosys_rock, 1, &ival); + return; + } + break; + } + } else if (type >= 0xE0 && type <= 0xFF) { + fatal_error("Attempt to print unknown type of string."); + } else { + fatal_error("Attempt to print non-string."); + } + + if (!substring) { + /* Just get straight out. */ + alldone = true; + } else { + /* Pop a stub and see what's to be done. */ + addr = pop_callstub_string(&bitnum); + if (addr == 0) { + alldone = true; + } else { + inmiddle = 0xE1; + } + } + } } uint Glulxe::stream_get_table() { - return stringtable; + return stringtable; } void Glulxe::stream_set_table(uint addr) { - if (stringtable == addr) - return; - - /* Drop cache. */ - if (tablecache_valid) { - if (tablecache.type == 0) - dropcache(tablecache.u.branches); - tablecache.u.branches = nullptr; - tablecache_valid = false; - } - - stringtable = addr; - - if (stringtable) { - /* Build cache. We can only do this if the table is entirely in ROM. */ - uint tablelen = Mem4(stringtable); - uint rootaddr = Mem4(stringtable+8); - int cache_stringtable = (stringtable+tablelen <= ramstart); - /* cache_stringtable = true; ...for testing only */ - /* cache_stringtable = false; ...for testing only */ - if (cache_stringtable) { - buildcache(&tablecache, rootaddr, CACHEBITS, 0); - /* dumpcache(&tablecache, 1, 0); */ - tablecache_valid = true; - } - } + if (stringtable == addr) + return; + + /* Drop cache. */ + if (tablecache_valid) { + if (tablecache.type == 0) + dropcache(tablecache.u.branches); + tablecache.u.branches = nullptr; + tablecache_valid = false; + } + + stringtable = addr; + + if (stringtable) { + /* Build cache. We can only do this if the table is entirely in ROM. */ + uint tablelen = Mem4(stringtable); + uint rootaddr = Mem4(stringtable + 8); + int cache_stringtable = (stringtable + tablelen <= ramstart); + /* cache_stringtable = true; ...for testing only */ + /* cache_stringtable = false; ...for testing only */ + if (cache_stringtable) { + buildcache(&tablecache, rootaddr, CACHEBITS, 0); + /* dumpcache(&tablecache, 1, 0); */ + tablecache_valid = true; + } + } } void Glulxe::buildcache(cacheblock_t *cablist, uint nodeaddr, int depth, int mask) { - int ix, type; - - type = Mem1(nodeaddr); - - if (type == 0 && depth == CACHEBITS) { - cacheblock_t *list, *cab; - list = (cacheblock_t *)glulx_malloc(sizeof(cacheblock_t) * CACHESIZE); - buildcache(list, nodeaddr, 0, 0); - cab = &(cablist[mask]); - cab->type = 0; - cab->depth = CACHEBITS; - cab->u.branches = list; - return; - } - - if (type == 0) { - uint leftaddr = Mem4(nodeaddr+1); - uint rightaddr = Mem4(nodeaddr+5); - buildcache(cablist, leftaddr, depth+1, mask); - buildcache(cablist, rightaddr, depth+1, (mask | (1 << depth))); - return; - } - - /* Leaf node. */ - nodeaddr++; - for (ix = mask; ix < CACHESIZE; ix += (1 << depth)) { - cacheblock_t *cab = &(cablist[ix]); - cab->type = type; - cab->depth = depth; - switch (type) { - case 0x02: - cab->u.ch = Mem1(nodeaddr); - break; - case 0x04: - cab->u.uch = Mem4(nodeaddr); - break; - case 0x03: - case 0x05: - case 0x0A: - case 0x0B: - cab->u.addr = nodeaddr; - break; - case 0x08: - case 0x09: - cab->u.addr = Mem4(nodeaddr); - break; - } - } + int ix, type; + + type = Mem1(nodeaddr); + + if (type == 0 && depth == CACHEBITS) { + cacheblock_t *list, *cab; + list = (cacheblock_t *)glulx_malloc(sizeof(cacheblock_t) * CACHESIZE); + buildcache(list, nodeaddr, 0, 0); + cab = &(cablist[mask]); + cab->type = 0; + cab->depth = CACHEBITS; + cab->u.branches = list; + return; + } + + if (type == 0) { + uint leftaddr = Mem4(nodeaddr + 1); + uint rightaddr = Mem4(nodeaddr + 5); + buildcache(cablist, leftaddr, depth + 1, mask); + buildcache(cablist, rightaddr, depth + 1, (mask | (1 << depth))); + return; + } + + /* Leaf node. */ + nodeaddr++; + for (ix = mask; ix < CACHESIZE; ix += (1 << depth)) { + cacheblock_t *cab = &(cablist[ix]); + cab->type = type; + cab->depth = depth; + switch (type) { + case 0x02: + cab->u.ch = Mem1(nodeaddr); + break; + case 0x04: + cab->u.uch = Mem4(nodeaddr); + break; + case 0x03: + case 0x05: + case 0x0A: + case 0x0B: + cab->u.addr = nodeaddr; + break; + case 0x08: + case 0x09: + cab->u.addr = Mem4(nodeaddr); + break; + } + } } #if 0 #include <stdio.h> void Glulxe::dumpcache(cacheblock_t *cablist, int count, int indent) { - int ix, jx; - - for (ix=0; ix<count; ix++) { - cacheblock_t *cab = &(cablist[ix]); - for (jx=0; jx<indent; jx++) - printf(" "); - printf("%X: ", ix); - switch (cab->type) { - case 0: - printf("...\n"); - dumpcache(cab->u.branches, CACHESIZE, indent+1); - break; - case 1: - printf("<EOS>\n"); - break; - case 2: - printf("0x%02X", cab->u.ch); - if (cab->u.ch < 32) - printf(" ''\n"); - else - printf(" '%c'\n", cab->u.ch); - break; - default: - printf("type %02X, address %06lX\n", cab->type, cab->u.addr); - break; - } - } + int ix, jx; + + for (ix = 0; ix < count; ix++) { + cacheblock_t *cab = &(cablist[ix]); + for (jx = 0; jx < indent; jx++) + printf(" "); + printf("%X: ", ix); + switch (cab->type) { + case 0: + printf("...\n"); + dumpcache(cab->u.branches, CACHESIZE, indent + 1); + break; + case 1: + printf("<EOS>\n"); + break; + case 2: + printf("0x%02X", cab->u.ch); + if (cab->u.ch < 32) + printf(" ''\n"); + else + printf(" '%c'\n", cab->u.ch); + break; + default: + printf("type %02X, address %06lX\n", cab->type, cab->u.addr); + break; + } + } } #endif /* 0 */ void Glulxe::dropcache(cacheblock_t *cablist) { - int ix; - for (ix=0; ix<CACHESIZE; ix++) { - cacheblock_t *cab = &(cablist[ix]); - if (cab->type == 0) { - dropcache(cab->u.branches); - cab->u.branches = nullptr; - } - } - glulx_free(cablist); + int ix; + for (ix = 0; ix < CACHESIZE; ix++) { + cacheblock_t *cab = &(cablist[ix]); + if (cab->type == 0) { + dropcache(cab->u.branches); + cab->u.branches = nullptr; + } + } + glulx_free(cablist); } char *Glulxe::make_temp_string(uint addr) { - int ix, len; - uint addr2; - char *res; - - if (Mem1(addr) != 0xE0) - fatal_error("String argument to a Glk call must be unencoded."); - addr++; - - for (addr2=addr; Mem1(addr2); addr2++) { }; - len = (addr2 - addr); - if (len < STATIC_TEMP_BUFSIZE) { - res = temp_buf; - } - else { - res = (char *)glulx_malloc(len+1); - if (!res) - fatal_error("Unable to allocate space for string argument to Glk call."); - } - - for (ix=0, addr2=addr; ix<len; ix++, addr2++) { - res[ix] = Mem1(addr2); - } - res[len] = '\0'; - - return res; + int ix, len; + uint addr2; + char *res; + + if (Mem1(addr) != 0xE0) + fatal_error("String argument to a Glk call must be unencoded."); + addr++; + + for (addr2 = addr; Mem1(addr2); addr2++) { }; + len = (addr2 - addr); + if (len < STATIC_TEMP_BUFSIZE) { + res = temp_buf; + } else { + res = (char *)glulx_malloc(len + 1); + if (!res) + fatal_error("Unable to allocate space for string argument to Glk call."); + } + + for (ix = 0, addr2 = addr; ix < len; ix++, addr2++) { + res[ix] = Mem1(addr2); + } + res[len] = '\0'; + + return res; } uint *Glulxe::make_temp_ustring(uint addr) { - int ix, len; - uint addr2; - uint *res; - - if (Mem1(addr) != 0xE2) - fatal_error("Ustring argument to a Glk call must be unencoded."); - addr+=4; - - for (addr2=addr; Mem4(addr2); addr2+=4) { }; - len = (addr2 - addr) / 4; - if ((len+1)*4 < STATIC_TEMP_BUFSIZE) { - res = (uint *)temp_buf; - } - else { - res = (uint *)glulx_malloc((len+1)*4); - if (!res) - fatal_error("Unable to allocate space for ustring argument to Glk call."); - } - - for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) { - res[ix] = Mem4(addr2); - } - res[len] = 0; - - return res; + int ix, len; + uint addr2; + uint *res; + + if (Mem1(addr) != 0xE2) + fatal_error("Ustring argument to a Glk call must be unencoded."); + addr += 4; + + for (addr2 = addr; Mem4(addr2); addr2 += 4) { }; + len = (addr2 - addr) / 4; + if ((len + 1) * 4 < STATIC_TEMP_BUFSIZE) { + res = (uint *)temp_buf; + } else { + res = (uint *)glulx_malloc((len + 1) * 4); + if (!res) + fatal_error("Unable to allocate space for ustring argument to Glk call."); + } + + for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) { + res[ix] = Mem4(addr2); + } + res[len] = 0; + + return res; } void Glulxe::free_temp_string(char *str) { - if (str && str != temp_buf) - glulx_free(str); + if (str && str != temp_buf) + glulx_free(str); } void Glulxe::free_temp_ustring(uint *str) { - if (str && str != (uint *)temp_buf) - glulx_free(str); + if (str && str != (uint *)temp_buf) + glulx_free(str); } } // End of namespace Glulxe diff --git a/engines/glk/glulxe/vm.cpp b/engines/glk/glulxe/vm.cpp index 194152ad65..44ea106e69 100644 --- a/engines/glk/glulxe/vm.cpp +++ b/engines/glk/glulxe/vm.cpp @@ -26,295 +26,291 @@ namespace Glk { namespace Glulxe { void Glulxe::setup_vm() { - byte buf[4 * 7]; - - pc = 0; // Clear this, so that error messages are cleaner. - prevpc = 0; - - // Read in all the size constants from the game file header - stream_char_handler = nullptr; - stream_unichar_handler = nullptr; - - _gameFile.seek(0); - if (_gameFile.read(buf, 4 * 7) != (4 * 7)) - fatal_error("The game file header is too short."); - - ramstart = Read4(buf + 0); - endgamefile = Read4(buf + 4); - origendmem = Read4(buf + 8); - stacksize = Read4(buf + 12); - startfuncaddr = Read4(buf + 16); - origstringtable = Read4(buf + 20); - checksum = Read4(buf + 24); - - // Set the protection range to (0, 0), meaning "off". - protectstart = 0; - protectend = 0; - - // Do a few sanity checks. - if ((ramstart & 0xFF) - || (endgamefile & 0xFF) - || (origendmem & 0xFF) - || (stacksize & 0xFF)) { - nonfatal_warning("One of the segment boundaries in the header is not " - "256-byte aligned."); - } - - if (endgamefile != gamefile_len) { - nonfatal_warning("The gamefile length does not match the header " - "endgamefile length."); - } - - if (ramstart < 0x100 || endgamefile < ramstart || origendmem < endgamefile) { - fatal_error("The segment boundaries in the header are in an impossible " - "order."); - } - if (stacksize < 0x100) { - fatal_error("The stack size in the header is too small."); - } - - /* Allocate main memory and the stack. This is where memory allocation - errors are most likely to occur. */ - endmem = origendmem; - memmap = (byte *)glulx_malloc(origendmem); - if (!memmap) { - fatal_error("Unable to allocate Glulx memory space."); - } - stack = (byte *)glulx_malloc(stacksize); - if (!stack) { - glulx_free(memmap); - memmap = nullptr; - fatal_error("Unable to allocate Glulx stack space."); - } - stringtable = 0; - - // Initialize various other things in the terp. - init_operands(); - init_serial(); - - // Set up the initial machine state. - vm_restart(); - - /* If the debugger is compiled in, check that the debug data matches - the game. (This only prints warnings for mismatch.) */ - debugger_check_story_file(); - - /* Also, set up any start-time debugger state. This may do a block- - and-debug, if the user has requested that. */ - debugger_setup_start_state(); + byte buf[4 * 7]; + + pc = 0; // Clear this, so that error messages are cleaner. + prevpc = 0; + + // Read in all the size constants from the game file header + stream_char_handler = nullptr; + stream_unichar_handler = nullptr; + + _gameFile.seek(0); + if (_gameFile.read(buf, 4 * 7) != (4 * 7)) + fatal_error("The game file header is too short."); + + ramstart = Read4(buf + 0); + endgamefile = Read4(buf + 4); + origendmem = Read4(buf + 8); + stacksize = Read4(buf + 12); + startfuncaddr = Read4(buf + 16); + origstringtable = Read4(buf + 20); + checksum = Read4(buf + 24); + + // Set the protection range to (0, 0), meaning "off". + protectstart = 0; + protectend = 0; + + // Do a few sanity checks. + if ((ramstart & 0xFF) + || (endgamefile & 0xFF) + || (origendmem & 0xFF) + || (stacksize & 0xFF)) { + nonfatal_warning("One of the segment boundaries in the header is not " + "256-byte aligned."); + } + + if (endgamefile != gamefile_len) { + nonfatal_warning("The gamefile length does not match the header " + "endgamefile length."); + } + + if (ramstart < 0x100 || endgamefile < ramstart || origendmem < endgamefile) { + fatal_error("The segment boundaries in the header are in an impossible " + "order."); + } + if (stacksize < 0x100) { + fatal_error("The stack size in the header is too small."); + } + + /* Allocate main memory and the stack. This is where memory allocation + errors are most likely to occur. */ + endmem = origendmem; + memmap = (byte *)glulx_malloc(origendmem); + if (!memmap) { + fatal_error("Unable to allocate Glulx memory space."); + } + stack = (byte *)glulx_malloc(stacksize); + if (!stack) { + glulx_free(memmap); + memmap = nullptr; + fatal_error("Unable to allocate Glulx stack space."); + } + stringtable = 0; + + // Initialize various other things in the terp. + init_operands(); + init_serial(); + + // Set up the initial machine state. + vm_restart(); + + /* If the debugger is compiled in, check that the debug data matches + the game. (This only prints warnings for mismatch.) */ + debugger_check_story_file(); + + /* Also, set up any start-time debugger state. This may do a block- + and-debug, if the user has requested that. */ + debugger_setup_start_state(); } void Glulxe::finalize_vm() { - stream_set_table(0); - - if (memmap) { - glulx_free(memmap); - memmap = nullptr; - } - if (stack) { - glulx_free(stack); - stack = nullptr; - } - - final_serial(); + stream_set_table(0); + + if (memmap) { + glulx_free(memmap); + memmap = nullptr; + } + if (stack) { + glulx_free(stack); + stack = nullptr; + } + + final_serial(); } void Glulxe::vm_restart() { - uint lx; - int res; - int bufpos; - char buf[0x100]; - - /* Deactivate the heap (if it was active). */ - heap_clear(); - - /* Reset memory to the original size. */ - lx = change_memsize(origendmem, false); - if (lx) - fatal_error("Memory could not be reset to its original size."); - - /* Load in all of main memory. We do this in 256-byte chunks, because - why rely on OS stream buffering? */ - _gameFile.seek(gamefile_start); - bufpos = 0x100; - - for (lx=0; lx<endgamefile; lx++) { - if (bufpos >= 0x100) { - int count = _gameFile.read(buf, 0x100); - if (count != 0x100) { - fatal_error("The game file ended unexpectedly."); - } - bufpos = 0; - } - - res = buf[bufpos++]; - if (lx >= protectstart && lx < protectend) - continue; - memmap[lx] = res; - } - for (lx=endgamefile; lx<origendmem; lx++) { - memmap[lx] = 0; - } - - /* Reset all the registers */ - stackptr = 0; - frameptr = 0; - pc = 0; - prevpc = 0; - stream_set_iosys(0, 0); - stream_set_table(origstringtable); - valstackbase = 0; - localsbase = 0; - - /* Note that we do not reset the protection range. */ - - /* Push the first function call. (No arguments.) */ - enter_function(startfuncaddr, 0, nullptr); - - /* We're now ready to execute. */ + uint lx; + int res; + int bufpos; + char buf[0x100]; + + /* Deactivate the heap (if it was active). */ + heap_clear(); + + /* Reset memory to the original size. */ + lx = change_memsize(origendmem, false); + if (lx) + fatal_error("Memory could not be reset to its original size."); + + /* Load in all of main memory. We do this in 256-byte chunks, because + why rely on OS stream buffering? */ + _gameFile.seek(gamefile_start); + bufpos = 0x100; + + for (lx = 0; lx < endgamefile; lx++) { + if (bufpos >= 0x100) { + int count = _gameFile.read(buf, 0x100); + if (count != 0x100) { + fatal_error("The game file ended unexpectedly."); + } + bufpos = 0; + } + + res = buf[bufpos++]; + if (lx >= protectstart && lx < protectend) + continue; + memmap[lx] = res; + } + for (lx = endgamefile; lx < origendmem; lx++) { + memmap[lx] = 0; + } + + /* Reset all the registers */ + stackptr = 0; + frameptr = 0; + pc = 0; + prevpc = 0; + stream_set_iosys(0, 0); + stream_set_table(origstringtable); + valstackbase = 0; + localsbase = 0; + + /* Note that we do not reset the protection range. */ + + /* Push the first function call. (No arguments.) */ + enter_function(startfuncaddr, 0, nullptr); + + /* We're now ready to execute. */ } uint Glulxe::change_memsize(uint newlen, bool internal) { - uint lx; - unsigned char *newmemmap; + uint lx; + unsigned char *newmemmap; - if (newlen == endmem) - return 0; + if (newlen == endmem) + return 0; #ifdef FIXED_MEMSIZE - return 1; + return 1; #else /* FIXED_MEMSIZE */ - if ((!internal) && heap_is_active()) - fatal_error("Cannot resize Glulx memory space while heap is active."); + if ((!internal) && heap_is_active()) + fatal_error("Cannot resize Glulx memory space while heap is active."); - if (newlen < origendmem) - fatal_error("Cannot resize Glulx memory space smaller than it started."); + if (newlen < origendmem) + fatal_error("Cannot resize Glulx memory space smaller than it started."); - if (newlen & 0xFF) - fatal_error("Can only resize Glulx memory space to a 256-byte boundary."); - - newmemmap = (unsigned char *)glulx_realloc(memmap, newlen); - if (!newmemmap) { - /* The old block is still in place, unchanged. */ - return 1; - } - memmap = newmemmap; + if (newlen & 0xFF) + fatal_error("Can only resize Glulx memory space to a 256-byte boundary."); - if (newlen > endmem) { - for (lx=endmem; lx<newlen; lx++) { - memmap[lx] = 0; - } - } + newmemmap = (unsigned char *)glulx_realloc(memmap, newlen); + if (!newmemmap) { + /* The old block is still in place, unchanged. */ + return 1; + } + memmap = newmemmap; - endmem = newlen; + if (newlen > endmem) { + for (lx = endmem; lx < newlen; lx++) { + memmap[lx] = 0; + } + } - return 0; + endmem = newlen; + + return 0; #endif /* FIXED_MEMSIZE */ } uint *Glulxe::pop_arguments(uint count, uint addr) { - uint ix; - uint argptr; - uint *array; - - #define MAXARGS (32) - static uint statarray[MAXARGS]; - static uint *dynarray = nullptr; - static uint dynarray_size = 0; - - if (count == 0) - return nullptr; - - if (count <= MAXARGS) { - /* Store in the static array. */ - array = statarray; - } - else { - if (!dynarray) { - dynarray_size = count+8; - dynarray = (uint *)glulx_malloc(sizeof(uint) * dynarray_size); - if (!dynarray) - fatal_error("Unable to allocate function arguments."); - array = dynarray; - } - else { - if (dynarray_size >= count) { - /* It fits. */ - array = dynarray; - } - else { - dynarray_size = count+8; - dynarray = (uint *)glulx_realloc(dynarray, sizeof(uint) * dynarray_size); - if (!dynarray) - fatal_error("Unable to reallocate function arguments."); - array = dynarray; - } - } - } - - if (!addr) { - if (stackptr < valstackbase+4*count) - fatal_error("Stack underflow in arguments."); - stackptr -= 4*count; - for (ix=0; ix<count; ix++) { - argptr = stackptr+4*((count-1)-ix); - array[ix] = Stk4(argptr); - } - } - else { - for (ix=0; ix<count; ix++) { - array[ix] = Mem4(addr); - addr += 4; - } - } - - return array; + uint ix; + uint argptr; + uint *array; + +#define MAXARGS (32) + static uint statarray[MAXARGS]; + static uint *dynarray = nullptr; + static uint dynarray_size = 0; + + if (count == 0) + return nullptr; + + if (count <= MAXARGS) { + /* Store in the static array. */ + array = statarray; + } else { + if (!dynarray) { + dynarray_size = count + 8; + dynarray = (uint *)glulx_malloc(sizeof(uint) * dynarray_size); + if (!dynarray) + fatal_error("Unable to allocate function arguments."); + array = dynarray; + } else { + if (dynarray_size >= count) { + /* It fits. */ + array = dynarray; + } else { + dynarray_size = count + 8; + dynarray = (uint *)glulx_realloc(dynarray, sizeof(uint) * dynarray_size); + if (!dynarray) + fatal_error("Unable to reallocate function arguments."); + array = dynarray; + } + } + } + + if (!addr) { + if (stackptr < valstackbase + 4 * count) + fatal_error("Stack underflow in arguments."); + stackptr -= 4 * count; + for (ix = 0; ix < count; ix++) { + argptr = stackptr + 4 * ((count - 1) - ix); + array[ix] = Stk4(argptr); + } + } else { + for (ix = 0; ix < count; ix++) { + array[ix] = Mem4(addr); + addr += 4; + } + } + + return array; } void Glulxe::verify_address(uint addr, uint count) { - if (addr >= endmem) - fatal_error_i("Memory access out of range", addr); - if (count > 1) { - addr += (count-1); - if (addr >= endmem) - fatal_error_i("Memory access out of range", addr); - } + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + if (count > 1) { + addr += (count - 1); + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + } } void Glulxe::verify_address_write(uint addr, uint count) { - if (addr < ramstart) - fatal_error_i("Memory write to read-only address", addr); - if (addr >= endmem) - fatal_error_i("Memory access out of range", addr); - if (count > 1) { - addr += (count-1); - if (addr >= endmem) - fatal_error_i("Memory access out of range", addr); - } + if (addr < ramstart) + fatal_error_i("Memory write to read-only address", addr); + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + if (count > 1) { + addr += (count - 1); + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + } } void Glulxe::verify_array_addresses(uint addr, uint count, uint size) { - uint bytecount; - if (addr >= endmem) - fatal_error_i("Memory access out of range", addr); - - if (count == 0) - return; - bytecount = count*size; - - /* If just multiplying by the element size overflows, we have trouble. */ - if (bytecount < count) - fatal_error_i("Memory access way too long", addr); - - /* If the byte length by itself is too long, or if its end overflows, - we have trouble. */ - if (bytecount > endmem || addr+bytecount < addr) - fatal_error_i("Memory access much too long", addr); - /* The simple length test. */ - if (addr+bytecount > endmem) - fatal_error_i("Memory access too long", addr); + uint bytecount; + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + + if (count == 0) + return; + bytecount = count * size; + + /* If just multiplying by the element size overflows, we have trouble. */ + if (bytecount < count) + fatal_error_i("Memory access way too long", addr); + + /* If the byte length by itself is too long, or if its end overflows, + we have trouble. */ + if (bytecount > endmem || addr + bytecount < addr) + fatal_error_i("Memory access much too long", addr); + /* The simple length test. */ + if (addr + bytecount > endmem) + fatal_error_i("Memory access too long", addr); } } // End of namespace Glulxe |