405 lines
18 KiB
C++
405 lines
18 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/* Portable interfaces to OS-specific functions
|
|
*
|
|
* This file defines interfaces to certain functions that must be called
|
|
* from portable code, but which must have system-specific implementations.
|
|
*/
|
|
|
|
#ifndef GLK_TADS_TADS2_OS
|
|
#define GLK_TADS_TADS2_OS
|
|
|
|
#include "common/system.h"
|
|
#include "glk/tads/os_frob_tads.h"
|
|
#include "glk/tads/os_glk.h"
|
|
#include "glk/tads/tads2/lib.h"
|
|
#include "glk/tads/tads2/appctx.h"
|
|
|
|
namespace Glk {
|
|
namespace TADS {
|
|
namespace TADS2 {
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* A note on character sets:
|
|
*
|
|
* Except where noted, all character strings passed to and from the
|
|
* osxxx functions defined herein use the local operating system
|
|
* representation. On a Windows machine localized to Eastern Europe,
|
|
* for example, the character strings passed to and from the osxxx
|
|
* functions would use single-byte characters in the Windows code page
|
|
* 1250 representation.
|
|
*
|
|
* Callers that use multiple character sets must implement mappings to
|
|
* and from the local character set when calling the osxxx functions.
|
|
* The osxxx implementations are thus free to ignore any issues related
|
|
* to character set conversion or mapping.
|
|
*
|
|
* The osxxx implementations are specifically not permitted to use
|
|
* double-byte Unicode as the native character set, nor any other
|
|
* character set where a null byte could appear as part of a non-null
|
|
* character. In particular, callers may assume that null-terminated
|
|
* strings passed to and from the osxxx functions contain no embedded
|
|
* null bytes. Multi-byte character sets (i.e., character sets with
|
|
* mixed single-byte and double-byte characters) may be used as long as
|
|
* a null byte is never part of any multi-byte character, since this
|
|
* would guarantee that a null byte could always be taken as a null
|
|
* character without knowledge of the encoding or context.
|
|
*/
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* "Far" Pointers. Most platforms can ignore this. For platforms with
|
|
* mixed-mode addressing models, where pointers of different sizes can
|
|
* be used within a single program and hence some pointers require
|
|
* qualification to indicate that they use a non-default addressing
|
|
* model, the keyword OSFAR should be defined to the appropriate
|
|
* compiler-specific extension keyword.
|
|
*
|
|
* If you don't know what I'm talking about here, you should just ignore
|
|
* it, because your platform probably doesn't have anything this
|
|
* sinister. As of this writing, this applies only to MS-DOS, and then
|
|
* only to 16-bit implementations that must interact with other 16-bit
|
|
* programs via dynamic linking or other mechanisms.
|
|
*/
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* ANSI C99 exact-size integer types.
|
|
*
|
|
* C99 defines a set of integer types with exact bit sizes, named intXX_t
|
|
* for a signed integer with XX bits, and uintXX_t for unsigned. XX can be
|
|
* 8, 16, 32, or 64. TADS uses the 16- and 32-bit sizes, so each platform
|
|
* is responsible for defining the following types:
|
|
*
|
|
*. int16_t - a signed integer type storing EXACTLY 16 bits
|
|
*. uint16_t - an unsigned integer type storing EXACTLY 16 bits
|
|
*. int32_t - a signed integer type storing EXACTLY 32 bits
|
|
*. uint32_t - an unsigned integer type storing EXACTLY 32 bits
|
|
*
|
|
* Many modern compilers provide definitions for these types via the
|
|
* standard header stdint.h. Where stdint.h is provided, the platform code
|
|
* can merely #include <stdint.h>.
|
|
*
|
|
* For compilers where stdint.h isn't available, you must provide suitable
|
|
* typedefs. Note that the types must be defined with the exact bit sizes
|
|
* specified; it's not sufficient to use a bigger type, because we depend
|
|
* in some cases on overflow and sign extension behavior at the specific
|
|
* bit size.
|
|
*/
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* Thread-local storage (TLS).
|
|
*
|
|
* When TADS is compiled with threading support, it requires some variables
|
|
* to be "thread-local". This means that the variables have global scope
|
|
* (so they're not stored in "auto" variables on the stack), but each
|
|
* thread has a private copy of each such variable.
|
|
*
|
|
* Nearly all systems that support threads also support thread-local
|
|
* storage. Like threading support itself, though, TLS support is at
|
|
* present implemented only in non-portable OS APIs rather than standard C
|
|
* language features. TLS is a requirement if TADS is compiled with
|
|
* threading, but it's not needed for non-threaded builds. TADS only
|
|
* requires threading at present (version 3.1) for its network features;
|
|
* since these features are optional, systems that don't have threading and
|
|
* TLS support will simply need to disable the network features, which will
|
|
* allow all of the threading and TLS definitions in osifc to be omitted.
|
|
*
|
|
* There appear to be two common styles of TLS programming models. The
|
|
* first provides non-standard compiler syntax for declarative creation of
|
|
* thread-local variables. The Microsoft (on Windows) and Gnu compilers
|
|
* (on Linux and Unix) do this: they provide custom storage class modifiers
|
|
* for declaring thread locals (__declspec(thread) for MSVC, __thread for
|
|
* gcc). Compilers that support declarative thread locals handle the
|
|
* implementation details through code generation, so the program merely
|
|
* needs to add the special TLS storage class qualifier to an otherwise
|
|
* ordinary global variable declaration, and then can access the thread
|
|
* local as though it were an ordinary global.
|
|
*
|
|
* The second programming model is via explicit OS API calls to create,
|
|
* initialize, and access thread locals. pthreads provides such an API, as
|
|
* does Win32. In fact, when you use the declarative syntax with MSVC or
|
|
* gcc, the compiler generates the appropriate API calls, but the details
|
|
* are transparent to the program; in contrast, when using pthreads
|
|
* directly, the program must actively call the relevant APIs.
|
|
*
|
|
* It's probably the case that every system that has compiler-level support
|
|
* for declarative thread local creation also has procedural APIs, so the
|
|
* simplest way to abstract the platform differences would be to do
|
|
* everything in terms of APIs. However, it seems likely that compilers
|
|
* with declarative syntax might be able to generate more efficient code,
|
|
* since optimizers always benefit from declarative information. So we'd
|
|
* like to use declarative syntax whenever it's available, but fall back on
|
|
* explicit API calls when it's not. So our programming model is a union
|
|
* of the two styles:
|
|
*
|
|
* 1. For each thread local, declare the thread local:
|
|
*. OS_DECL_TLS(char *, my_local);
|
|
*
|
|
* 2. At main program startup (for the main thread only), initialize each
|
|
* thread local:
|
|
*. os_tls_create(my_local);
|
|
*
|
|
* 3. Never get or set the value of a thread local directly; instead, use
|
|
* the get/set functions:
|
|
*. char *x = os_tls_get(char *, my_local);
|
|
*. os_tls_set(my_local, "hello");
|
|
*
|
|
* One key feature of this implementation is that each thread local is
|
|
* stored as a (void *) value. We do it this way to allow a simple direct
|
|
* mapping to the pthreads APIs, since that's going to be the most common
|
|
* non-declarative implementation. This means that a thread local variable
|
|
* can contain any pointer type, but *only* a pointer type. The standard
|
|
* pattern for dealing with anything more ocmplex is the same as in
|
|
* pthreads: gather up the data into a structure, malloc() an instance of
|
|
* that structure at entry to each thread (including the main thread), and
|
|
* os_tls_set() the variable to contain a pointer to that structure. From
|
|
* then on, use os_tls_set(my_struct *, my_local)->member to access the
|
|
* member variables in the structure. And finally, each thread must delete
|
|
* the structure at thread exit.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Declare a thread local.
|
|
*
|
|
* - For compilers that support declarative TLS variables, the local OS
|
|
* headers should use the compiler support by #defining OS_DECL_TLS to the
|
|
* appropriate local declarative keyword.
|
|
*
|
|
* - For systems without declarative TLS support but with TLS APIs, the
|
|
* global declared by this macro actually stores the slot ID (what pthreads
|
|
* calls the "key") for the variable. This macro should therefore expand
|
|
* to a declaration of the appropriate API type for a slot ID; for example,
|
|
* on pthreads, #define OS_DECL_TLS(t, v) pthread_key_t v.
|
|
*
|
|
* - For builds with no thread support, simply #define this to declare the
|
|
* variable as an ordinary global: #define OS_DECL_TLS(t, v) t v.
|
|
*/
|
|
/* #define OS_DECL_TLS(typ, varname) __thread typ varname */
|
|
|
|
/*
|
|
* For API-based systems without declarative support in the compiler, the
|
|
* main program startup code must explicitly create a slot for each thread-
|
|
* local variable by calling os_tls_create(). The API returns a slot ID,
|
|
* which is shared among threads and therefore can be stored in an ordinary
|
|
* global variable. OS_DECL_TLS will have declared the global variable
|
|
* name in this case as an ordinary global of the slot ID type. The
|
|
* os_tls_create() macro should therefore expand to a call to the slot
|
|
* creation API, storing the new slot ID in the global.
|
|
*
|
|
* Correspondingly, before the main thread exits, it should delete each
|
|
* slot it created, b calling os_tls_delete().
|
|
*
|
|
* For declarative systems, there's no action required here, so these
|
|
* macros can be defined to empty.
|
|
*/
|
|
/* #define os_tls_create(varname) pthread_key_create(&varname, NULL) */
|
|
/* #define os_tls_delete(varname) pthread_key_delete(varname) */
|
|
|
|
|
|
/*
|
|
* On API-based systems, each access to get or set the thread local
|
|
* requires an API call, using the slot ID stored in the actual global to
|
|
* get the per-thread instance of the variable's storage.
|
|
*. #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
|
|
*. #define os_tls_set(varname, val) pthread_setspecific(varname, val)
|
|
*
|
|
* On declarative systems, the global variable itself is the thread local,
|
|
* so get/set can be implemented as direct access to the variable.
|
|
*. #define os_tls_get(typ, varname) varname
|
|
*. #define os_tls_set(varname, val) varname = (val)
|
|
*/
|
|
|
|
/*
|
|
* Common TLS definitions - declarative thread locals
|
|
*
|
|
* For systems with declarative TLS support in the compiler, the OS header
|
|
* can #define OS_DECLARATIVE_TLS to pick up suitable definitions for the
|
|
* os_tls_xxx() macros. The OS header must separately define OS_DECL_TLS
|
|
* as appropriate for the local system.
|
|
*/
|
|
#ifdef OS_DECLARATIVE_TLS
|
|
#define os_tls_create(varname)
|
|
#define os_tls_delete(varname)
|
|
#define os_tls_get(typ, varname) varname
|
|
#define os_tls_set(varname, val) varname = (val)
|
|
#endif
|
|
|
|
/*
|
|
* Common TLS definitions - pthreads
|
|
*
|
|
* For pthreads systems without declarative TLS support in the compiler,
|
|
* the OS header can simply #define OS_PTHREAD_TLS to pick up the standard
|
|
* definitions below.
|
|
*/
|
|
#ifdef OS_PTHREAD_TLS
|
|
#include <pthread.h>
|
|
#define OS_DECL_TLS(typ, varname) pthread_key_t varname
|
|
#define os_tls_create(varname) pthread_key_create(&varname, NULL)
|
|
#define os_tls_delete(varname) pthread_key_delete(varname)
|
|
#define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
|
|
#define os_tls_set(varname, val) pthread_setspecific(varname, val)
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* OS main entrypoint
|
|
*/
|
|
int os0main(int oargc, char **oargv,
|
|
int (*mainfn)(int, char **, char *),
|
|
const char *before, const char *config);
|
|
|
|
/*
|
|
* new-style OS main entrypoint - takes an application container context
|
|
*/
|
|
int os0main2(int oargc, char **oargv,
|
|
int (*mainfn)(int, char **, appctxdef *, char *),
|
|
const char *before, const char *config,
|
|
appctxdef *appctx);
|
|
|
|
|
|
/* open the error message file for reading */
|
|
osfildef *oserrop(const char *arg0);
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* Special "switch" statement optimization flags. These definitions are
|
|
* OPTIONAL - if a platform doesn't provide these definitions, suitable
|
|
* code that's fully portable will be used.
|
|
*
|
|
* On some compilers, the performance of a "switch" statement can be
|
|
* improved by fully populating the switch with all possible "case"
|
|
* values. When the compiler can safely assume that every possible "case"
|
|
* value is specifically called out in the switch, the compiler can
|
|
* generate somewhat faster code by omitting any range check for the
|
|
* controlling expression of the switch: a range check is unnecessary
|
|
* because the compiler knows that the value can never be outside the
|
|
* "case" table.
|
|
*
|
|
* This type of optimization doesn't apply to all compilers. When a given
|
|
* platform's compiler can be coerced to produce faster "switch"
|
|
* statements, though, there might be some benefit in defining these
|
|
* symbols according to local platform rules.
|
|
*
|
|
* First, #define OS_FILL_OUT_CASE_TABLES if you want this type of switch
|
|
* optimization at all. This symbol is merely a flag, so it doesn't need
|
|
* a value - #defining it is enough to activate the special code. If you
|
|
* don't define this symbol, then the code that cares about this will
|
|
* simply generate ordinary switches, leaving holes in the case table and
|
|
* using "default:" to cover them. If the platform's osxxx.h header does
|
|
* #define OS_FILL_OUT_CASE_TABLES, then the portable code will know to
|
|
* fill out case tables with all possible alternatives where it's possible
|
|
* and potentially beneficial to do so.
|
|
*
|
|
* Second, if you #define OS_FILL_OUT_CASE_TABLES, you MUST ALSO #define
|
|
* OS_IMPOSSIBLE_DEFAULT_CASE. The value for this symbol must be some
|
|
* code to insert into a "switch" statement at the point where a
|
|
* "default:" case would normally go. On some compilers, special
|
|
* extensions allow the program to explicitly indicate within a switch
|
|
* that all possible cases are covered, so that the compiler doesn't have
|
|
* to be relied upon to infer this for itself (which would be impossible
|
|
* for it to do in many cases anyway). You can use an empty definition
|
|
* for this symbol if your compiler doesn't have any special construct for
|
|
* indicating a fully-populated case table.
|
|
*
|
|
* Note that *most* switch statements in portable code won't care one way
|
|
* or the other about these definitions. There's a time/space trade-off
|
|
* in fully populating a switch's case table, so only the most
|
|
* time-critical code will bother trying.
|
|
*/
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/*
|
|
* TADS 2 swapping configuration. Define OS_DEFAULT_SWAP_ENABLED to 0
|
|
* if you want swapping turned off, 1 if you want it turned on. If we
|
|
* haven't defined a default swapping mode yet, turn swapping on by
|
|
* default.
|
|
*/
|
|
#ifndef OS_DEFAULT_SWAP_ENABLED
|
|
# define OS_DEFAULT_SWAP_ENABLED 1
|
|
#endif
|
|
|
|
/*
|
|
* If the system "long description" (for the banner) isn't defined, make
|
|
* it the same as the platform ID string.
|
|
*/
|
|
#ifndef OS_SYSTEM_LDESC
|
|
# define OS_SYSTEM_LDESC OS_SYSTEM_NAME
|
|
#endif
|
|
|
|
/*
|
|
* TADS 2 Usage Lines
|
|
*
|
|
* If the "usage" lines (i.e., the descriptive lines of text describing
|
|
* how to run the various programs) haven't been otherwise defined,
|
|
* define defaults for them here. Some platforms use names other than
|
|
* tc, tr, and tdb for the tools (for example, on Unix they're usually
|
|
* tadsc, tadsr, and tadsdb), so the usage lines should be adjusted
|
|
* accordingly; simply define them earlier than this point (in this file
|
|
* or in one of the files included by this file, such as osunixt.h) for
|
|
* non-default text.
|
|
*/
|
|
#ifndef OS_TC_USAGE
|
|
# define OS_TC_USAGE "usage: tc [options] file"
|
|
#endif
|
|
#ifndef OS_TR_USAGE
|
|
# define OS_TR_USAGE "usage: tr [options] file"
|
|
#endif
|
|
#ifndef OS_TDB_USAGE
|
|
# define OS_TDB_USAGE "usage: tdb [options] file"
|
|
#endif
|
|
|
|
/*
|
|
* Ports can define a special TDB startup message, which is displayed
|
|
* after the copyright/version banner. If it's not defined at this
|
|
* point, define it to an empty string.
|
|
*/
|
|
#ifndef OS_TDB_STARTUP_MSG
|
|
# define OS_TDB_STARTUP_MSG ""
|
|
#endif
|
|
|
|
/*
|
|
* If a system patch sub-level isn't defined, define it here as zero.
|
|
* The patch sub-level is used on some systems where a number of ports
|
|
* are derived from a base port (for example, a large number of ports
|
|
* are based on the generic Unix port). For platforms like the Mac,
|
|
* where the porting work applies only to that one platform, this
|
|
* sub-level isn't meaningful.
|
|
*/
|
|
#ifndef OS_SYSTEM_PATCHSUBLVL
|
|
# define OS_SYSTEM_PATCHSUBLVL "0"
|
|
#endif
|
|
|
|
} // End of namespace TADS2
|
|
} // End of namespace TADS
|
|
} // End of namespace Glk
|
|
|
|
#endif
|