Notes on porting TADS ===================== Following are notes on changes made to the OS layer in recent versions, starting with the most recent changes. If you're updating a TADS port, you should refer to the changes made since the last working version on your system. At the end of the file, you'll find more general information on how the portability layer is designed and how to get TADS working on a new operating system. If you're porting TADS to a new system for the first time, you should look at the general notes at the end of this file. Quick Start Guide ----------------- To port TADS to a new platform, you should start by looking at osifc.h, which specifies the entire TADS Operating System Interface. You must provide an implementation of this interface for your platform. If you're porting TADS to a machine that resembles another platform where TADS already runs, you can probably use the OS Interface implementation for that existing platform as a starting point; if not, you should read the documentation in osifc.h to learn what each function must do. You'll also need to construct a makefile or equivalent for your platform, to compile the C files that make up TADS and link the results into executables for the TADS Interpreter, and optionally the TADS Compiler and other tools. You should refer to an existing makefile (such as the DOS or Unix makefile) as a starting point. A Note on Newlines ------------------ Different operating systems have a tendency to disagree on the most trivial details. One of these pointless variations that causes a lot of trouble is "newline" conventions: each OS has its own way of signaling the end of a line of text in files such as C source and header files. In almost every case, the end of a line of text is indicated by some combination of the special characters with ASCII values 10 and 13, known respectively as "line feed" or "LF", and "carriage return" or "CR". MS-DOS uses the sequence CR-LF; Unix uses just LF; Macintosh uses just CR; and some system somewhere probably uses LF-CR. In many cases, systems are tolerant of "foreign" conventions, but sometimes the wrong conventions will confuse compilers and other tools. If you run into any mysterious compiler errors, or your text editor or other tools have problems reading or displaying the TADS source files, you might want to try converting the files to your system's newline conventions. One of the easiest ways to do this is usually to use your UNZIP tool's "auto-convert text files" option when you unpack the TADS source archive - on command-line systems, this is usually the "unzip -a" option. Version 2.5.8 changes --------------------- * New OS_ATTR_xxx codes: In the past, the single text attribute was "highlighting," specified with OS_ATTR_HILITE. Several new attributes have been added. First, there are two new attributes with specific renderings: OS_ATTR_BOLD and OS_ATTR_ITALIC. The former should use bold-face if available, the latter an italic font. On most character-mode platforms, bold-face can be approximated with a brighter color, and italics can be ignored. Second, there are two new abstract attributes: OS_ATTR_EM and OS_ATTR_STRONG. These correspond to the HTML and attributes, so it's up to the platform to define the actual appearance. PORTS CAN IGNORE THE NEW ATTRIBUTES. These changes have been made in such a way that everything will continue to work as it has all along if a port ignores the changes. OS_ATTR_BOLD, OS_ATTR_HILITE, and OS_ATTR_EM will all map to the same code, which is the same as the original OS_ATTR_HILITE code. OS_ATTR_ITALIC will map to a new code, but since existing ports don't know this code, it will do nothing. OS_ATTR_STRONG maps to zero by default (i.e., no change to the codes at all), so it will also do nothing. Thus, existing ports will work the same as they always have with existing games. Any port that's capable of displaying italics might want to add recognition of italics. If you do this, it might be desirable to add definitions to your OS-specific header to #define OS_ATTR_EM as OS_ATTR_ITALIC and OS_ATTR_STRONG as OS_ATTR_BOLD, for consistency with most HTML browser renderings. Your #defines for these symbols will override the defaults, since the defaults will be defined only if your OS header doesn't otherwise define the symbols. * MORE banner style: A new style flag has been added: OS_BANNER_STYLE_MOREMODE. This style indicates that the banner should pause for a MORE prompt any time its text overflows the vertical display area of the banner, to give the user a chance to read a long passage and acknowledge that it's okay to show more text. When this flag isn't set, the default is that long passages are allowed to scroll off the screen (assuming the auto-vscroll style is set), even if the user might not have had a chance to read them. With this new style flag set, though, a banner window should pause when text overflows the window, using the same sort of prompt and user acknowledgment used for the MORE prompt in the main game window. Note that there's no sysinfo flag to opt out of implementing this style; it's important enough that platforms supporting the banner API at all must support this style. Platforms based on osgen3.c won't need to do anything to support this new style, as it's fully handled in osgen3.c. Other platforms will have to implement this style in their banner code. * Parent Windows in the os_banner API: The new concept of a "parent" window has been introduced into the banner API. When a banner window is created, the caller can now optionally designate a parent banner, which is an existing banner window from which the new banner's display area will be taken. In past versions, there was no explicit parent window, and each new banner's display area was taken out of the main game window's rectangle. When the caller does not specify a parent banner, this traditional behavior applies; however, when the caller specifies a parent, the display area is taken from the parent rather than from the main game window. For implementations based on osgen.c or osgen3.c, no changes are needed to any system-specific code. All of the necessary changes are handled entirely in osgen.c and osgen3.c. Implementations that do not implement the banner API must merely to change the definition of their dummy os_banner_create() function to match the new prototype. Systems that implement the banner API and which DO NOT use osgen.c or osgen3.c must implement the new parent window mechanism. This will require several changes. First, os_banner_create() must be changed to use the new prototype, which includes an argument for the parent banner handle. Second, os_banner_create() must remember the parent window handle with the internal data structure it uses to represent the new window. Third, the system-specific code that calculates the banner layout must take the parent structure into account; see os_banner.htm for details of the required new layout algorithm. Fourth, if the implementation supports drawing banner window borders (as specified through the style flags), it will have to take into account the new rule that a border is drawn to the horizontal or vertical extent (as appropriate) of the *union* of the banner and all of its child windows. Fifth, os_banner_delete() might need to hide or set to zero size any children of the banner being deleted, since once a parent is deleted, its children become invisible, although they remain valid as abstract objects until explicitly deleted. * os_nonstop_mode(): This new function lets the portable code tell the OS layer that it wants to run in "non-stop" mode, which means that MORE prompting (or any local equivalent) should be disabled. By default, non-stop mode is turned off; this function lets the portable code turn it on and off. Character-mode platforms based on osgen.c/osgen3.c don't have to worry about this. Platforms where MORE prompting doesn't exist or is provided by the portable console layer can simply provide a dummy implementation. Platforms where the OS layer handles MORE prompting should provide a real implementation of this function. Version 2.5.7 changes --------------------- IMPORTANT - if you're using a platform based on osgen.c, you can skip #1 through #5, because these changes are handled by the portable osgen.c implementation. Skip to #6 if you're using osgen.c 1. The function os_hilite() has been DELETED. You should remove any implementation of this function you provide. 2. The new function os_set_text_attr() replaces os_hilite(). This new function is used to set text attributes in the main window. If you had an implementation of os_hilite() that simply returned zero, your os_set_text_attr() need do nothing at all. If your os_hilite() implementation did anything else, it can be implemented with something like this: void os_set_text_attr(unsigned int attr) { os_printf("%c", (attr & OS_ATTR_HILITE) != 0 ? ON : OFF); } where ON is to be replaced by the character code that os_hilite(1) returned in your old implementation, and OFF is to be replaced by the character code that os_hilite(2) returned. If possible, you should optimize this by doing nothing when the current hilite mode is not being changed. The purpose of this change is to generally improve the osifc design, and to make it more extensible by providing a structured way to add new attributes to be added in the future. The meanings of colors in os_set_text_color() and os_set_screen_color() have changed. The values are now encoded RGB values; some macros are provided in osifc.h for manipulating these color values. Platforms based on osgen.c will NOT have to do anything for this change, since osgen.c defines the osifc-level color routines. 3. The os_printf() and os_vprintf() routines have been DELETED. These have been replaced by os_printz() and os_print() (see #4). 4. The new routines os_printz() and os_print() have been ADDED. These routines do essentially what os_printf() and os_vprintf() formerly did, but with a greatly simplified interface. These functions are notably different from the former os_printf() and os_vprintf() in some important ways: 4a. These routines do NOT take a printf-style format string, but simply take a string to display directly. There is no need to perform any parameter substitutions on the string. 4b. os_printz() takes a NULL-TERMINATED string, while os_print() takes a COUNTED-LENGTH string (the byte length is given as a separate argument), and thus CANNOT assume that the string is null-terminated. os_printz() for (PRINT-Zero-terminated) can be implemented as a simple call to os_print, with the string's length computed with strlen(). 4c. These routines NO LONGER have to worry about the "highlight" escape code sequences. Highlighting is handled with the new text attributes settings (see #2 above), so strings presented to os_print will no longer have any embedded escape codes. The only special characters that these routines are now expected to recognize are '\n' and '\r'. 5. Several new SYSINFO_xxx codes have been added to osifc.h. Your os_get_sysinfo() implementation should be updated for the new codes. 6. [Begin osgen.c changes] All platforms based on the semi-portable osgen.c implementation must make some small adjustments to their ossxxx routines. THE FOLLOWING NOTES APPLY *ONLY* TO OSGEN.C PLATFORMS - if you're not linking with osgen.c, ignore the following. Note that these aren't changes you have to make to osgen.c itself - these are changes you have to make to your system-specific code that osgen.c relies upon. There are now two different "osgen" implementations. First, there's the traditional osgen.c - this is the one you'll get by default if you do nothing, since it's the one your makefile (or equivalent) already uses. This version of osgen.c has been reworked a little, but it's substantially the same as in past versions. Second, there's a new version called osgen3.c - if you like, you can switch to this one. osgen3.c is a REPLACEMENT for osgen.c - you use EITHER osgen.c or osgen3.c, but not both. If you switch, simply substitute osgen3.c for osgen.c wherever it appears in your makefile. Okay, so should you switch to osgen3.c, or stick with osgen.c? In the long run, the answer is that you should switch; but when you do so is up to you. What you get with osgen3.c is a full implementation of the new TADS 3 "banner API," which lets TADS 3 programs control the screen layout much more precisely than TADS 2 programs could. If you are building the TADS 3 interpreter, you should definitely switch to osgen3.c, to give TADS 3 programs the full capabilities of the new banner API. If you're building only a TADS 2 interpreter, there's less immediate benefit in switching, because TADS 2 programs don't have any way to access the banner API. The main reason not to switch if you don't have to is that osgen3.c is a substantial rewrite that hasn't been around nearly as long as osgen.c and thus hasn't had nearly as much practical testing; sticking with osgen.c is less likely to introduce new bugs. On the other hand, it should be practically no work to switch to osgen3.c - the only change should be replacing osgen.c with osgen3.c in your makefile. If you do plan to port the TADS 3 interpreter at some point, switching to osgen3.c now would have the advantage that you'll feel safer using osgen3.c when the time comes for the TADS 3 port, because you will have been using osgen3.c between now and then. You'll have change your osxxx code as described in the sub-topics below, regardless of whether you stick with osgen.c or switch to osgen3.c. 6a. Several global variables have been eliminated. You can delete these from your osxxx.h headers and remove calcaulations from your osxxx.c source files. (It's not vital that you do this, but it's encouraged in the interests of general clean-up. If you're still using any of these variables for your own purposes, of course, you are free to keep them - just be aware that osgen.c no longer needs them.) The following variables are no longer used in osgen.c: ldesc_color ldesc_column ldesc_curline ldesc_line max_column max_line score_column sdesc_column sdesc_line text_column text_lastcol text_normal_color text_bold_color Note that you might not want to delete the xxx_color variables, since you might want to use these same values in your ossgetcolor() implementation (see "fifth..." below). However, osgen.c no longer uses these variables, since it now instead calls your ossgetcolor() routine to determine what color values to use. If you switch to osgen3.c, the following globals are also eliminated (or, rather, can be eliminated, as osgen3.c doesn't require them): sdesc_color text_color 6b. Your osxxx.c code MUST NOT set or modify G_os_pagelength or G_os_linewidth. In the past, the osxxx.c code was responsible for setting these, and was aware of the status line location and so forth. Don't set these variables any more. Instead, set the new global variables G_oss_screen_width and G_oss_screen_height to the height and width of the screen, in characters - DO NOT make any adjustments for status lines or anything else, as osgen.c will take care of that. Be sure to set these variables in your os_scrini() routine, so that they're available during osgen.c's initialization. 6c. If the screen size can ever change dynamically after initialization, you MUST call the new function osssb_on_resize_screen() immediately after updating the global variables G_oss_screen_width and G_oss_screen_height. This will ensure that osgen.c has a chance to refigure its internal layout parameters according to the new screen size. 6d. You should #include "osgen.h" in your osxxx.c file(s). 6e. You must implement the new routines oss_get_sysinfo() and ossgetcolor(). (These actually were added just before the 2.5.6 release, so if you've sync'ed up lately, you probably have implemented these already - they haven't changed in this version.) Refer to osgen.h for details on how these are supposed to work. These new routines provide text color support, so that games can explicitly set the colors of the text they display; color support is optional, but if you don't support color, you must must still provide minimal implementations of these routines to return the same color scheme you've used in the past. In the past, several global variables (text_normal_color, sdesc_color, etc) had the "oss color codes" to use for different types of text - these color codes were passed to ossdsp(), ossclr(), and so on for rendering by your osxxx.c code. osgen.c no longer uses most of these; it only uses sdesc_color and text_color. If you're switching to osgen3.c, even those hold-outs are gone. Instead, the osgen layer calls your ossgetcolor() routine to get the color code to use for each case. If you don't want to support any more color than you did in the past, you can simply implement ossgetcolor() so that it looks for the various abstract color codes (OS_COLOR_TEXT, OS_COLOR_STATUSLINE, etc), and returns the corresponding global variable value. The advantage of the new scheme is that it allows the game code to set *specific* colors (e.g., red, blue, cyan) for individual bits of text, as long as your osxxx.c code (and your display hardware, of course) can support specific colors. 6f. The macro OSGEN_ATTR_BOLD has been renamed to OS_ATTR_HILITE. If you ported the 2.5.6 release, you might have a reference to this macro; the meaning is the same, so all you have to do is change the name. For convenience, OSGEN_ATTR_BOLD is now defined as equivalent to OS_ATTR_HILITE, but the new name should be used as soon as possible. 6g. The OS_COLOR_xxx symbols have been renamed to OSGEN_COLOR_xxx. The meanings are the same as they were; the name change reflects the change in the osifc-level color scheme. 6h. Eight new OSGEN_COLOR_xxx values have been introduced, to allow for somewhat better granularity in setting colors at the system level. With the new colors, the OSGEN_COLOR_xxx values can specify a total of 16 colors, which correspond to the eight ANSI colors at "low intensity" and "high intensity"; the low-intensity set is essentially the high-intensity set at half brightness. On most systems based on osgen.c, if this full range of colors is available, the brighter half of the range will be used to show high-intensity text, so in fact only eight colors will really be available; on such systems, ossgetcolor() should simply collapse the 16 colors down to the 8 ANSI colors, by treating the low-intensity and high-intensity version of a given color the same, and then pick from the low- or high-intensity version according to the boldness attribute. Even for systems that collapse the colors in the this manner, though, there is still value in having the broader range, in that the full range of 16 colors can be selected for backgrounds, which do not have a boldness attribute and thus are free to offer the full range of 16 colors to clients. 6i. You must implement the new routine oss_raw_key_to_cmd(). This routine's purpose is to map "raw" keystroke codes to command-editing CMD_xxx codes, essentially mapping results from the style that os_getc_raw() uses to the style os_getc() uses. This routine was created to give the OS implementation more specific control over the mapping of raw keystrokes to command-editing functions. For most systems, you can probably just copy the MS-DOS implementation, in the source file msdos/osdos.c, making any desired changes to the control key or other mappings. [End osgen.c changes] Version 2.5.6 changes --------------------- Several new SYSINFO_xxx codes have been added - osifc.h has the full list. The system-specific code that provides system information should be updated to handle the new codes. A new file-open function, osfoprwt(), has been added. This function is the text-mode equivalent of osfoprwb(): it opens a text file for both read and write access, keeping intact any existing contents. I have attempted to provide a suitable implementation in the source code for each ported version of which I have a copy in my source tree, but a platform expert on each target system might want to check my work to make sure it's correct for their system. A second new file-open function, osfoprwtt(), has been added. This function is the text-mode equivalent of osfoprwtb(): it opens a text file for both read and write access, truncating any existing file. I've attempted to provide a suitable definition for each platform for this function as well. The new function os_update_display() has been added. This function's purpose is to process any pending redraw events immediately, if applicable to the system. This function only needs to be implemented on event-driven operating systems where the display is drawn in response to redraw events generated by window region invalidations. (If that doesn't mean anything to you, you're almost certainly not on such a platform - the Windows and Macintosh GUI's both use this kind of drawing scheme, but text-mode versions would certainly not.) The purpose of this routine is to update the display prior to a long computation, to avoid the appearance that the program is frozen during the computation delay. The new functions os_set_text_color() and os_set_screen_color() can optionally provide the game with control over display colors. These are described in osifc.h. These functions are optional; if they're not suitable for a given platform, simply provide empty implementations. The new functions os_gets_timeout() and os_gets_cancel() provides an event-oriented interface for reading a command line from the keyboard. This function is similar to the regular os_gets(), but has a timeout feature that allows the keyboard read to be interrupted after a specified interval has elapsed, and then possibly resumed later. Timed keyboard input is optional; if it can't be implemented on a platform, simply provide an implementation for os_gets_timeout() that returns OS_EVT_NOTIMEOUT when a timeout is provided, and an empty implementation for os_gets_cancel().. Note that even if timed input is not allowed, os_gets_timeout() should minimally call the plain os_gets() when the caller does not specify a timeout, and only return the OS_EVT_NOTIMEOUT error when a timeout is actually requested by the caller. A new interface for managing "banner" windows in the process of being added. This is a work in progress, and you should just ignore it for now, providing only stub implementations. The new interface consists of a set of functions (about a dozen) whose names are of the form os_banner_xxx(); I'm still in the process of working out the final details, so the set may change in the future. These new functions, when they're completed, will provide functionality similar to the HTML TADS tag, but without the limitations that arise from using a single output stream to control all aspects of the display. In the future, this interface will allow for for implementation of banner-like behavior in text-only platforms, allowing more flexibility for games in their use of text-mode displays. The banner interface will always be entirely optional; if it is not feasible or desirable to implement the interface, a platform will be able to provide do-nothing implementations for these routines. Version 2.5.2 changes --------------------- In preparation for TADS 3, I've been tightening up the interfaces between the OS layer and the portable code. My goal is to ensure that the a common OS layer can be shared by TADS 2 and TADS 3, which will dramatically simplify the porting of TADS 3 by eliminating the need to write a new set of OS routines. Porting TADS 3 will simply be a matter of compiling and linking the TADS 3 portable code with the existing TADS 2 OS code for your platform; the only coding you should have to do is to create the makefile. I've been working on this OS interface improvement for some time. This process started with the creation of osifc.h, which provides us with a comprehensive definition of the entire interface that the portable code uses to call the OS code. I've also been expanding and improving the OS interfaces slightly over the past few releases, but I've been trying to minimize these changes to minimize the resulting porting work created. With this release, I've addressed a new area, which is references from the OS code to the portable code. In general, the OS code should never call the portable code or use any global variables defined in the portable code; doing so ties the OS code to a particular portable system, which makes it difficult to re-use the OS code in a new system (such as TADS 3). The OS code should NOT henceforth refer to ANY function or global variable defined in the portable code. If your OS code refers to anything in the portable code, your OS code will not work with TADS 3, because those portable symbols will probably not be defined at all in TADS 3. I've tried to remove all of the references I could find from the OS code into the portable code. However, until TADS 3 is ported to the existing platforms, where the linker can root out the missing symbols, it is difficult to know for sure that all of the references are gone. * OS code should no longer call runstat(). Most os_gets() implementations started by calling runstat() to update the status line prior to reading input. The OS code is no longer responsible for this. You should simply remove any runstat() calls from your OS code. If you have runstat() calls that are needed in places other than at the start of os_gets(), contact me (mjr_@hotmail.com) if you would like advice on how to remove the dependency and I'll try to help figure out what to do. * The global variables "pagelength" and "linewidth" have been renamed to G_os_pagelength and G_os_linewidth. This formally moves these variables into the OS layer rather than leaving them in the portable formatter. * The global variable "moremode" has been renamed to G_os_moremode, moving it into the OS layer. * OS routines should no longer call tio_is_html_mode() (or any other tio routines). * OS routines should no longer call qasclose(). Most of the os_term() implementations in the OS code were calling this routine; this should instead be handled in the portable code, so the OS code should not use this method at all. * OS code should no longer call outformat() or getstring(). The text-only implementations of os_askfile() and os_input_dialog() called these routines, because they implemented their dialogs using formatted text. To address this, I've moved these text implementations to the portable layer; there is no longer a text-only version of os_askfile() or os_input_dialog(). Refer to askf_tx.c, askf_os.c, indlg_tx.c, and indlg_os.c for details on the new portable-layer implementations. MAKEFILE CHANGES: You must include one of askf_tx.c or askf_os.c in each executable you build. You must include one of indlg_tx.c or indlg_os.c in each executable. Refer to these files to determine which one to choose. In general, the _tx versions are text-only implementations -- these do not invoke os_xxx routines; the _os versions simply pass through the operation to the corresponding os_xxx routines. You should choose the _os version if you have a corresponding os_xxx implementation that you want to use; you should choose the _tx version if you want to use the plain text version. If your os_xxx routine was calling outformat() or getstring(), you should simply delete your os_xxx routine entirely and use the _tx version; otherwise, keep your os_xxx routine and use the _os version. OTHER CHANGES: * added os_is_special_file() * added os_uninit() * added OS_CHARMAP_FILECONTENTS identifier Version 2.5.1 changes --------------------- * I added a new command code for os_getc(): CMD_EOF. This new code indicates that "end of file" has been reached on the console input; this usually means that the player has terminated the application through some means such as closing its main window, or disconnected the terminal, or something like that. os_getc() should return this new code when it detects this type of condition so that code that calls os_getc() can finish up whatever it's doing and terminate the application gracefully. * There's a new function called os_getc_raw(). This is conceptually a lower-level version of os_getc(). The difference is that os_getc() returns "translated" keystroke CMD_xxx codes, whereas os_getc_raw() returns "raw" CMD_xxx codes. The translated codes are higher-level functional codes; for example, on Unix, when the user types Ctrl-E, os_getc() would return CMD_END, because Ctrl-E is bound to the end-of-line command. In contrast, os_getc_raw() would simply return ASCII 5. Similarly, on DOS, if the user presses the Escape key, os_getc() would return CMD_KILL, but os_getc_raw() returns ASCII 27. Note that some keys return different CMD_xxx codes for os_getc() and os_getc_raw(); for example, on DOS, if the user presses F1, os_getc() returns CMD_SCR (to toggle scrollback mode), but os_getc_raw() returns CMD_F1. The purpose of os_getc_raw() is to allow lower-level access to keystroke information, particularly for the inputkey() TADS function, while still preserving the port-specific functional mapping that os_getc() affords. Refer to osifc.h for documentation of the new function. Note that the CMD_xxx codes defined in osifc.h are now commented to indicate which command codes are "translated" and which are "raw"; when a particular key maps to one of each type, os_getc() should return the translated CMD_xxx code and os_getc_raw() should return the raw code. * The osfoprwb() function has traditionally been implemented incorrectly. On most platforms, this has been implemented as a call to fopen() with the mode "r+b" (or simply "r+" on platforms with no text/binary mode distinction), following the example of the DOS code. In fact, the intended function, which was not adequately documented in the osifc.h comments, was to open an existing file or create a new file if it didn't already exist. The C fopen() "r+" mode fails if the file doesn't exist. The intended behavior has now been correctly documented in osifc.h, and the DOS, Windows, Unix, and Mac versions have been changed to use the correct behavior. Version 2.4.1 changes --------------------- * New OS function: os_input_dialog(). A default character-mode implementation in osgen.c should suffice for most ports, but GUI ports should show a dialog box if possible. The character-mode implementation in osgen.c is selected at compile time with #define symbol USE_STDIO_INPDLG, which osgen.c defines implicitly if USE_STDIO is defined. * New OS function: os_get_str_rsc(). Most platforms should be able to use the default implementation in ostadrsc.c (in the generic code directory). Where possible, though, this function should be implemented using native OS string resources; this will make it easier for translators to localize the TADS executables. * TADSVER.TXT has been changed to TADSVER.HTM, and DOSVER.TXT is now DOSVER.HTM. * The interface to the os_askfile() function has changed very slightly, in that the result code values are now more precisely specified. In the past, this function simply returns zero for success and non-zero to indicate failure of some kind. The result codes can now indicate the type of failure; in particular, the function can distinguish actual errors from the case where the user simply chooses to cancel the dialog. Refer to osifc.h for details. I've attempted to change all of the OS sources that I have in my source tree; experts on the code for the individual platforms are encouraged to double-check my changes. Version 2.4.0 changes --------------------- * os.h has been reorganized to make it cleaner and more consistent. All of the DOS code has been removed from os.h and moved to new DOS-specific headers; this leaves os.h as a very simple "switchboard" file whose only function is to select the appropriate set of headers to #include based on the system macro settings. All of the non-DOS headers already worked this way, so this change should be transparent to other ports. * osifc.h now has (as far as I can tell) complete documentation of the entire TADS Operating System Interface. In particular, all of the file-related functions (osfoprb, osfrb, etc) are now specified and documented in osifc.h, as are the former "library" functions (osmalloc, osfree). This change should not affect any existing code; the additions are mostly comments. * The os_find_first_file() and os_find_next_file() interfaces have changed. Two new parameters, outpathbuf and outpathbufsiz, have been added; the new buffer is to receive the full path of the current file, so that the caller doesn't have to attempt to construct the path name from its components using possibly non-portable assumptions. This should have minimal impact, since the only ports that appear to implement these functions currently are DOS, Windows, OS/2, and Macintosh. Refer to osifc.h for documentation. * The os_progress() interface has changed slightly. This function no longer takes a (struct linfdef *) argument, which tied the function to the particulars of an internal structure in the TADS 2 compiler; instead, the function now takes a filename string and a line number. This change should have minimal impact, since only the Macintosh port currently provides a non-trivial implementation of this function. Version 2.3.0 changes --------------------- * ATTENTION UNIX USERS - If you run into weird display problems in the interpreter, especially with spurious extra spaces at the beginning of each line, try these definitions in your makefile: LIBS= -lncurses CFLAGS= -DHAVE_TPARM (plus whatever other CFLAGS you were already using) * The existing os_term() function is now used more rigorously. In the past, the generic code called exit() directly in a few places, which prevented the OS layer from doing any clean-up at termination. The generic code should now use os_term() exclusively to terminate execution. This shouldn't require any new porting work, since nothing about os_term() has changed -- the only change is that the generic code is calling it more predictably now. However, a few ports may accidentally have omitted an os_term() implementation from some executables, since it wasn't always needed before; if you get a link error for this symbol during building, adjust your makefile to include an os_term() implementation. * A new file, oem.c, defines some global strings for use in identifying the version of TADS. You must fill in that file with a couple of customized settings for your version. Please refer to oem.h and oem.c for details on how to do this. * A few new OS interface functions have been added. These functions are described in comments in osifc.h. The new functions are: os_get_sys_clock_ms() os_sleep_ms() os_get_event() os_set_title() osfdel_temp() These functions should be reasonably simple to implement on most systems. os_get_event() in particular can simply degrade to os_getc() with some additional argument checks and changes in the return value if it's hard to implement fully on your system; the USE_STDIO version in osgen.c can be used as a default implementation. Note also that a completely portable implementation of os_sleep_ms() is in msdos/oswin.c. You can use this implementation if you wish, but the function is included in the OS interface so that you can replace it with something more suitable for your platform. The implementation in msdos/oswin.c discards keystrokes and other events that occur during the delay, which may be undesirable on your system. This implementation has the desirable effect of processing UI events, such as window sizing, during the delay. Most platforms can provide an empty implementation of os_set_title(); this function merely notifies the OS layer that the game has set a title string via the HTML tag. If it's convenient, the OS layer can use the title string as a window caption, or whatever else makes sense. Most character-mode display code will simply ignore this; osgen.c has a default implementation that does nothing. osfdel_temp() is a new function to complement os_create_tempfile(). This function deletes a temporary file previously created with os_create_tempfile(), after the temporary file has been closed; callers previously used osfdel(), the generic file deletion function, to delete these temporary files. The purpose of this new function is to support systems that can be instructed to delete temporary files automatically when closed; on such systems, a call to osfdel() is redundant (and problematic), since the system will already have automatically deleted the temporary file by the time the caller gets around to invoking osfdel(). osnoui.c provides a simple implementation of osfdel_temp() that simply calls osfdel(); this works with the implementation of os_create_tempfile() in osnoui.c, so you won't need to make any changes if you're already using this file. If you're using a customized os_create_tempfile(), you may want to provide a correspondingly customized osfdel_temp(). In particular, if your os_create_tempfile() opens the file in such a way that the underlying OS will automatically delete the file when closed, your osfdel_temp() should simply do nothing. * The os_askfile() interface (defined in osifc.h) has been changed to provide more information about what kind of prompt to use. The extra parameters indicate whether we're opening an existing file or saving a new file, and what type of file we're interested in. Many GUI systems use different dialogs for opening and saving files, and filter files displayed in a file selector dialog according to the type of file to be chosen. Systems that don't need the extra information can ignore the new parameters; they're purely to make it easier for the system code to determine what type of dialog to show. I've updated my copies of the MSDOS, Win32, Mac, and Unix sources for the new os_askfile interface, but I don't have a copy of every port-specific version, so you may need to propagate this change into your version. * The #define's for the OSFTxxx codes (the file type codes) are now in osifc.h. In the past, these were scattered among the different system-specific headers for no good reason -- these are used by portable code, so they are necessarily part of the OS interface, and hence belong in osifc.h. If you encounter any errors with redundant #define's for these symbols, you should simply remove the #define's from your system-specific headers and use the ones in osifc.h. * The #define's for the CMD_xxx codes (the os_getc() extended keystroke codes) are now in osifc.h. In the past, as with the OSFTxxx codes, these were scattered among the system-specific headers. It's desirable to be able to use these key codes in the portable code, so these are now part of the general OS interface. As with the OSFTxxx codes, if you run into any compilation errors with redundant #define's for CMD_xxx symbols, you should be able to remove the #define's from your system- specific headers and use the new osifc.h definitions. Version 2.2.6 changes --------------------- In past versions, the release notes for generic changes and platform- specific changes were combined into a single file, which had to be maintained separately on each platform. For example, the DOS release notes were in TADSVER.DOS. To create a comprehensive set of release notes for another platform, someone porting TADS had to sift through TADSVER.DOS to pick out the generic changes, then add any changes specific to the ported version. In order to remove this extra work, I've broken up the release notes into two separate files. The new file TADSVER.TXT contains only the generic changes. Since these changes will automatically be included in any port (because such changes are always in the portable portion of the code), the distribution for a port should always be able to include TADSVER.TXT unchanged. The separate new file TADSVER.DOS contains changes that apply only to the DOS version. Since such changes are only in the DOS osxxx code, they should not affect any other platforms, so only DOS users should need to look at this file. When creating a ported version, you can now simply create your own platform-specific release notes file where you can describe any changes that you made that apply only to your platform. Please let me know (mjr_@hotmail.com) the name of your port-specific release notes file, and I'll add it to the table at the top of TADSVER.TXT, which refers users to the appropriate file for each platform. (You may want to add an entry to the table yourself for your initial version, if you plan to include TADSVER.TXT with your distribution.) Version 2.2.4 changes --------------------- I've changed the source distribution zip file's layout slightly to make the TADS source tree more self-contained. In the previous version, several files were in a separate LIB directory, which was at the same level in the hierarchy as the TADS2 directory. This was an undesirable layout for some people, since it meant that TADS had two high-level directories. Since keeping the files in a separate directory doesn't serve any real purpose currently (the separation is historical), I've merged these files into the base TADS directory (except that a few of the files are now in TADS/MSDOS, because they're DOS-specific). Of course, you can still lay out your source files however you want them, of course, but if you follow the layout in the zip file, you'll need to adjust your makefile for the changed location of these files. If you change the default memory sizes in your osXXX.h file, you should also define message strings that correspond to your new defaults for the usage messages. To do this, look at errmsg.c, and notice the big list of TxD_xxxSIZ_MSG definitions. For each value that you override in your OS header, you should provide a corresponding TxD_xxxSIZ_MSG definition that looks like the one in errmsg.c but uses your changed default value. Version 2.2.3 changes --------------------- The operating system interfaces have changed somewhat between version 2.2.2 and 2.2.3 (the HTML TADS release). The OS interfaces have been stable for a long time, so most systems where TADS has been ported should have OS-dependent implementations that conform to the 2.2.2 interfaces. Unfortunately, all of these ports will have to be reworked slightly to accomodate the changes in 2.2.3. The changes in 2.2.3 should be fairly minor, though, so it shouldn't require a huge effort to upgrade port-specific code that worked with the previous version. The main changes are that the OS interfaces have been rearranged slightly in the header files (note in particular the creation of osifc.h), and some of the functions that were in osgen.c have been moved to the new file osnoui.c. A few functions have had minor changes to their interfaces as well. It will probably be necessary to make some small changes to your makefiles because of the rearrangements. One of the biggest changes may be the use of ANSI-style function prototypes throughout the code. Depending on your compiler, you may or may not have to change your OS function implementations to use ANSI prototypes. ============================================================================== General notes on porting TADS ----------------------------- Code Organization ----------------- The TADS code is divided into portable and system-specific sections. In porting TADS to a new platform, only the system-specific portions should need to be changed. If any changes are needed to the portable code, we consider the portable code in error and will try to correct it so that it is the same on all platforms. We want the portable portion to have a single set of sources, without port-dependent ifdefs, across all platforms. For the most part, the system-specific sections are isolated in files with the prefix "os", and system-specific functions and macros all begin with "os" or "OS". Historical note: At one point we started to group some code that High Energy was internally sharing between multiple products into a code library, all of whose files start with "L". The file los.h formerly many system-specific macros and definitions; these have been moved into the OS files along with everything else. oem.h ----- This file defines information about the person who built a particular version ("OEM" for "Original Equipment Manufacturer"). Refer to oem.h for details about how to set up the definitions there. osxxx.h, os.h, and osifc.h -------------------------- The first thing to do when starting a new port is to create a new system-specific header file for your platform. You should choose a name for the header based on your platform name; for example, on DOS, the file is osdos.h, and on Unix it's osunix.h. Next, you must define a C preprocessor macro that will identify your platform. This macro will be used to select the appropriate code to include in your version of TADS, using #ifdef preprocessor directives in the TADS code. Many compilers automatically pre-define a symbol for this purpose, in which case you should use this symbol; otherwise, make up a symbol of your own and define it on the C compiler command line or equivalent when you compile the TADS C files. Edit the file os.h. This is a "switchboard" file that selects an appropriate system-specific header to include. The TADS portable C files #include os.h, and os.h must in turn #include your header file. You should add a few lines to os.h to #include your header; follow the example of DOS, Unix, and the other platforms: #ifdef FROBNIX_OS /* Frobnix operating system definitions are in osfrob.h */ #include "osfrob.h" #endif You shouldn't add anything else to os.h - the rest of your code should go in your own "osfrob.h" file. Now you must write the code in your OS-specific header ("osfrob.h" in the example above). To do this, refer to osifc.h, which defines the TADS Operating System Interface, which is what you must implement for your machine. The purpose of your osxxx.h is to isolate certain platform-specific definitions to a well-defined location in the code. The rest of the TADS C code is written so that it uses the definitions in osxxx.h; this way, the other code doesn't have to be changed when moving TADS to a new platform. The file osifc.h contains the specification and documentation of the TADS Operating System Interface, which contains the portable interface definitions for functions, types, and macros that must be tailored to each platform. osifc.h contains a portable interface to code that varies by platform; because the interface is portable, the rest of the TADS code doesn't have to know anything about different platforms, since it can expect the exact same functions to be available on every machine. osifc.h contains only portable code, so you shouldn't make any changes in this file. You can probably take one of the existing osxxx.h implementations as a starting point, depending on your hardware and operating system. For example, for a 68000 or 68000-like processor, you might want to start with the Macintosh header (osmac.h), since the alignment and byte ordering macros will be appropriate. For an 8086-like processor, start with the DOS headers (os.h and los.h). Other platforms may need additional work. IMPORTANT NOTE: Do not make changes in osifc.h - this file should never contain any #ifdef's for different platforms, because it defines a portable interface that is the same everywhere, even though the implementation of the interface varies by platform. In particular, the #define constants (OS_EVT_xxx, SYSINFO_xxx, and so on) MUST be the same on all platforms, because some of these values can be used programmatically by TADS games; a game can be compiled on one platform and run on any other machine, so these constant values must be the same everywhere for games to work properly. Datatype configuration ---------------------- TADS makes relatively few assumptions about datatype sizes. TADS expects that shorts are at least 16 bits, ints are at least 16 bits, and longs are at least 32 bits. System-specific function implementation --------------------------------------- Depending on your platform, you may be able to re-use some code that was originally written for the DOS platform, but works with some minor changes on other types of machines as well. The files osdos.c, osgen.c, and osnoui.c provide an implementation of the system routines for DOS. osgen.c and osnoui.c should be usable on any system with DOS-like display characteristics. Note osgen.c and osdos.c check to see if several USE_xxx preprocessor symbols are defined; these macros are NOT defined anywhere in the source, but are to be defined by the makefile. On DOS, these os files are compiled multiple times with different combinations of the USE_xxx symbols to produce different object files for linking the compiler and run-time. A stdio (C run-time library "standard input/output") implementation of many of the routines can also be selected by defining the preprocessor symbol USE_STDIO. The file osnoui.c is similar in purpose to osgen.c. The functions implemented in osnoui.c are semi-portable implementations of functions that have no user interface component ("no UI"). Building the system ------------------- TADS consists of three main pieces: the compiler, the run-time, and the debugger. Refer to the DOS makefile to see which object files are included in each. Note that it may not be possible to let a librarian/archiver resolve the links for all symbols, because a few of the object files provide overlapping sets of symbols -- it is necessary to manually include such object files in the links. (The overlapping symbol sets are generally used to provide empty definitions for certain functions that are referenced but not actually used by one executable, but must be fully implemented in another.) Debugger -------- The debugger diverges somewhat from the convention of keeping the os-dependent functions in a file prefixed by "os" or "los". The debugger's user interface is implemented in the file dbgu.c on DOS, and dbgumac.c on Macintosh. The DOS version also has a file, osdbg.c, which provides lower-level functions; it should be possible to port the debugger to a new platform by using the DOS user interface and replacing only osdbg.c with an appropriate set of functions for the new system. However, for any system without DOS-like display and input device characteristics, the entire user interface will usually need to be replaced; for example, on the Mac, dbgu.c is not used at all, but is replaced by dbgumac.c. This separation of the user interface provides maximum flexibility for the debugger's user interface while minimizing the amount of work to port it to a new platform. Testing ------- The first test is to build a run-time and see if it can run games compiled on another system. The second is to build the compiler, use it to compile game on your system, and see if that runs. You can further test using the regression tests. Compile DEEP.T, then run tr like this: tr -i dsdwalk.in -l dsdwalk.new deep Then compare dsdwalk.new (the new log generated by your version of DEEP) with dsdwalk.log (the reference version) -- if they're identical, your system is probably in pretty good shape. You can test similary with DITCH.T and DDDWALK.IN, and BUGS.T and BUGS.IN. MS-DOS and Windows Compilation ------------------------------ Two makefiles are provided for compilation on MS-DOS and Windows machines; both makefiles are in the msdos directory. To avoid confusion, these makefiles build object files into separate directories, and name their executables differently from one another. makefile.bc is for compilation with the 16-bit Borland C compiler, version 4.5. With this makefile, you can build the real-mode and protected-mode 16-bit DOS executables. (You'll need the Borland Power Pack for DOS in order to build the protected-mode versions of the executables.) Note that you should run makefile.bc with Borland make. makefile.bc compiles into objs (for real-mode object files) and xobjs (for protected-mode object files), and generates the following executables: tc.exe - real-mode TADS compiler tr.exe - real-mode TADS run-time tdb.exe - real-mode TADS debugger tcx.exe - 16-bit protected-mode TADS compiler trx.exe - 16-bit protected-mode TADS run-time tdbx.exe - 16-bit protected-mode TADS debugger maketrx.exe - single-file executable binder tadsrsc.exe - TADS resource manager trcolor.exe - run-time color picker makefile.vc5 is for Microsoft Visual C++ version 5.0. VC++ 5.0 can only perform 32-bit compilations, so this makefile builds only 32-bit versions of the executables. Note that this makefile builds the two TADS libraries that are required for building HTML TADS: tr32h.lib (the TADS run-time 32-bit Windows library for HTML) and tdb32h.lib (the TADS debugger 32-bit Windows library for HTML). You should run makefile.vc5 with Microsoft nmake. makefile.vc5 compiles into winobjs32 (for 32-bit Windows object files), and generates the following executables: tc32.exe - TADS compiler 32-bit Windows console application tr32.exe - TADS run-time 32-bit Windows console application tdb32.exe - TADS debugger 32-bit Windows console application maketrx32.exe - executable binder 32-bit Windows console application tadsrsc32.exe - TADS resource manager 32-bit Windows console application trcolor32.exe - run-time color picker 32-bit Windows console application