diff options
author | Paul Gilbert | 2019-04-15 21:36:46 -0700 |
---|---|---|
committer | Paul Gilbert | 2019-04-17 20:46:06 -0700 |
commit | 5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d (patch) | |
tree | 2a57326f02bbd0c20b7f1790170d3ba527c55adc /engines/glk | |
parent | 1c4649018388e7df15bc0787ed66b6dc7755db1d (diff) | |
download | scummvm-rg350-5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d.tar.gz scummvm-rg350-5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d.tar.bz2 scummvm-rg350-5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d.zip |
GLK: GLULXE: Add string methods
Diffstat (limited to 'engines/glk')
-rw-r--r-- | engines/glk/glk_types.h | 2 | ||||
-rw-r--r-- | engines/glk/glulxe/exec.cpp | 4 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe.cpp | 4 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe.h | 103 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe_types.h | 25 | ||||
-rw-r--r-- | engines/glk/glulxe/string.cpp | 828 | ||||
-rw-r--r-- | engines/glk/module.mk | 1 |
7 files changed, 942 insertions, 25 deletions
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h index 7095334344..84d3e151a1 100644 --- a/engines/glk/glk_types.h +++ b/engines/glk/glk_types.h @@ -230,7 +230,7 @@ union gluniversal_union { byte _uch; ///< Cu int8 _sch; ///< Cs char _ch; ///< Cn - const char *_charstr; ///< S + char *_charstr; ///< S uint32 *_unicharstr; ///< U void *_array; ///< all # arguments uint32 _ptrflag; ///< [ ... ] or *? diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp index 766e8b6042..4fefe05e54 100644 --- a/engines/glk/glulxe/exec.cpp +++ b/engines/glk/glulxe/exec.cpp @@ -543,13 +543,13 @@ void Glulxe::execute_loop() { case op_streamchar: profile_in(0xE0000001, stackptr, false); value = inst[0].value & 0xFF; - (*stream_char_handler)(value); + (this->*stream_char_handler)(value); profile_out(stackptr); break; case op_streamunichar: profile_in(0xE0000002, stackptr, false); value = inst[0].value; - (*stream_unichar_handler)(value); + (this->*stream_unichar_handler)(value); profile_out(stackptr); break; case op_streamnum: diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp index 9db784c969..576b961872 100644 --- a/engines/glk/glulxe/glulxe.cpp +++ b/engines/glk/glulxe/glulxe.cpp @@ -42,7 +42,9 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, // 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) { + 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 8402a1b75e..4044074e37 100644 --- a/engines/glk/glulxe/glulxe.h +++ b/engines/glk/glulxe/glulxe.h @@ -30,11 +30,18 @@ namespace Glk { namespace Glulxe { +class Glulxe; +typedef void (Glulxe::*CharHandler)(unsigned char); +typedef void (Glulxe::*UnicharHandler)(uint); + /** * Glulxe game interpreter */ class Glulxe : public GlkAPI { public: + CharHandler stream_char_handler; + UnicharHandler stream_unichar_handler, glkio_unichar_han_ptr; + bool vm_exited_cleanly; strid_t gamefile; uint gamefile_start, gamefile_len; @@ -60,9 +67,6 @@ public: uint protectstart, protectend; uint prevpc; - void (*Glulxe::stream_char_handler)(unsigned char ch); - void (*Glulxe::stream_unichar_handler)(uint ch); - /** * \defgroup accel fields * @{ @@ -143,6 +147,26 @@ public: /**@}*/ + /** + * \defgroup string fields + * @{ + */ + + uint iosys_mode; + uint iosys_rock; + + /** + * The current string-decoding tables, broken out into a fast and easy-to-use form. + */ + bool tablecache_valid; + cacheblock_t tablecache; + + /* This misbehaves if a Glk function has more than one S argument. */ + #define STATIC_TEMP_BUFSIZE (127) + char temp_buf[STATIC_TEMP_BUFSIZE + 1]; + + /**@}*/ + protected: /** * \defgroup glkop fields @@ -312,6 +336,25 @@ protected: int write_buffer(dest_t *dest, const byte *ptr, uint len); /**@}*/ + + /** + * \defgroup string support methods + * @{ + */ + + void stream_setup_unichar(void); + + void nopio_char_han(unsigned char ch); + void filio_char_han(unsigned char ch); + void nopio_unichar_han(uint ch); + void filio_unichar_han(uint ch); + void glkio_unichar_nouni_han(uint val); + + void dropcache(cacheblock_t *cablist); + void buildcache(cacheblock_t *cablist, uint nodeaddr, int depth, int mask); + void dumpcache(cacheblock_t *cablist, int count, int indent); + + /**@}*/ public: /** * Constructor @@ -476,24 +519,6 @@ public: /**@}*/ /** - * \defgroup Strings access methods - * @{ - */ - - void stream_num(int val, int inmiddle, int charnum); - void stream_string(uint addr, int inmiddle, int bitnum); - uint stream_get_table(void); - void stream_set_table(uint addr); - void stream_get_iosys(uint *mode, uint *rock); - void stream_set_iosys(uint mode, uint rock); - char *make_temp_string(uint addr); - uint *make_temp_ustring(uint addr); - void free_temp_string(const char *str); - void free_temp_ustring(const uint *str); - - /**@}*/ - - /** * \defgroup Heap access methods * @{ */ @@ -838,6 +863,42 @@ public: uint perform_verify(); /**@}*/ + + + /** + * \defgroup Strings access methods + * @{ + */ + + /** + * Write a signed integer to the current output stream. + */ + void stream_num(int val, int inmiddle, int charnum); + + /** + * Write a Glulx string object to the current output stream. inmiddle is zero if we are beginning + * a new string, or nonzero if restarting one (E0/E1/E2, as appropriate for the string type). + */ + void stream_string(uint addr, int inmiddle, int bitnum); + + /** + * Get the current table address. + */ + uint stream_get_table(void); + + /** + * Set the current table address, and rebuild decoding cache. + */ + void stream_set_table(uint addr); + + void stream_get_iosys(uint *mode, uint *rock); + void stream_set_iosys(uint mode, uint rock); + char *make_temp_string(uint addr); + uint *make_temp_ustring(uint addr); + void free_temp_string(char *str); + void free_temp_ustring(uint *str); + + /**@}*/ }; extern Glulxe *g_vm; diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h index 5b14a6aaf8..9200ec27eb 100644 --- a/engines/glk/glulxe/glulxe_types.h +++ b/engines/glk/glulxe/glulxe_types.h @@ -379,6 +379,31 @@ struct dest_struct { }; typedef dest_struct dest_t; +/** + * These constants are defined in the Glulx spec. + */ +enum iosys { + iosys_None = 0, + iosys_Filter = 1, + iosys_Glk = 2 +}; + +#define CACHEBITS (4) +#define CACHESIZE (1 << CACHEBITS) +#define CACHEMASK (15) + +struct cacheblock_struct { + int depth; /* 1 to 4 */ + int type; + union { + struct cacheblock_struct *branches; + unsigned char ch; + uint uch; + uint addr; + } u; +}; +typedef cacheblock_struct cacheblock_t; + } // End of namespace Glulxe } // End of namespace Glk diff --git a/engines/glk/glulxe/string.cpp b/engines/glk/glulxe/string.cpp new file mode 100644 index 0000000000..2c95c3e851 --- /dev/null +++ b/engines/glk/glulxe/string.cpp @@ -0,0 +1,828 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "engines/glk/glulxe/glulxe.h" + +namespace Glk { +namespace Glulxe { + +void Glulxe::stream_get_iosys(uint *mode, uint *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; + +#else /* GLK_MODULE_UNICODE */ + + 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; +} + +void Glulxe::nopio_char_han(unsigned char ch) { +} + +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); +} + +void Glulxe::filio_unichar_han(uint 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); +} + +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."); + } +} + +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 = NULL; + } + 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 = NULL; + } + 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; +} + +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 = NULL; + 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; + } + } +} + +#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; + } + } +} +#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 = NULL; + } + } + 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; +} + +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; +} + +void Glulxe::free_temp_string(char *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); +} + +} // End of namespace Glulxe +} // End of namespace Glk diff --git a/engines/glk/module.mk b/engines/glk/module.mk index 6cd298e3fa..247f1d837e 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -69,6 +69,7 @@ MODULE_OBJS := \ glulxe/operand.o \ glulxe/search.o \ glulxe/serial.o \ + glulxe/string.o \ magnetic/detection.o \ magnetic/magnetic.o \ scott/detection.o \ |