Initial commit
This commit is contained in:
404
engines/glk/tads/tads2/os.h
Normal file
404
engines/glk/tads/tads2/os.h
Normal file
@@ -0,0 +1,404 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user