ADOM Sage - Technical Description

Just a few notes on how ADOM Sage works, since I've gotten a few questions about it... Parts of this description may be too technical for those who are curious but aren't technically inclined; parts of the description may be too elementary or vague for those who are more technically inclined. Please email me if you have any questions that I don't address.

How ADOM Sage works under Linux

Linux (and most Unixes, I think) have shared libraries (similar to Windows DLLs) where common code is kept. These are often provided by the operating system so that each program doesn't have to provide their own. ADOM uses two shared libraries, one for the standard C library and one for the ncurses library (handles screen I/O). In Linux (and, again, most Unixes, I think), if you set the LD_PRELOAD environment variable with the name of a shared library, the system loads that library before loading any of the standard libraries when running programs. You can therefore use an LD_PRELOAD library to override functions provided in the standard library. So that's what I do - override several functions in the ncurses library to monitor and alter input and output, and override the vsprintf function in the standard C library so that I can monitor and alter ADOM's messages. In the source package, contains the code that does all of this.

How ADOM Sage works under Windows

It's possible under Windows to monitor and, I believe, intercept calls to external libraries, much like under Linux. However, ADOM is a DOS program, so that doesn't help any. (There is a Windows port of ADOM, but it's only in beta.) DOS doesn't have external libraries - all of the functions that ADOM Sage needs to monitor and intercept are inside the code, so getting at them would require disassembling ADOM. (This is an oversimplification - see the section on DOS for details.)

So instead ADOM Sage takes the Linux version of ADOM and makes it run under Windows. Windows allows a program to set up its address space to match that of a Linux program, and in most other respects (function calling conventions, register usage, structure padding, instruction set, etc.) Windows and Linux are identical, so this isn't too difficult to do. Additionally, Linux programs (like Windows programs) contain detailed information about functions that they expect the operating system to provide, so ADOM Sage can redirect those function requests to Windows or to its own internal routines.

Windows programs do differ from Linux programs in how they allocate stack space. So ADOM Sage simply takes allocates all available stack space before doing anything else. A bit wasteful on memory, but it's simple and effective.

ADOM Sage focuses on making ADOM run under Windows, but it could be adapted to run other Linux programs under Windows. Anyone have a use for such functionality?

Running a Linux program under Windows consists of interpreting the ELF file format used by Linux programs, loading the program into the Windows program's address space, and then transferring control to the Linux program, redirecting any requests for Linux library functions to equivalent Windows functions. ADOM uses two libraries - libc and ncurses. Most of libc's functions are available in Microsoft's C runtime library. Functions from ncurses are handled by PDCurses; I had to hack PDCurses to make it more compatible with ncurses. (For these purposes it's not enough to be compatible at the source level - macros, constants, and structure layouts all have to match so that code is compatible at the binary level.)

Oddly enough, making a Linux program run under Windows is easier than making a DOS program run under Windows. DOS programs have no standard way of listing which functions they expect the operating system to provide, so replacing their function requests to DOS with function requests to Windows would probably require disassembling the program.

Making ADOM Sage work under DOS

It would be possible to make a program like ADOM Sage work under DOS, but it would be a lot of work. The standard method for modifying how a DOS program runs is hooking one or more interrupts. However, programs compiled with DJGPP (like ADOM) write directly to video memory, so interrupt hooking wouldn't permit a program to monitor ADOM's output. Even if interrupt hooking did work, it would be a much lower-level approach than ADOM Sage currently uses, and it would take a lot of work to get ADOM Sage to work with such a low-level approach.

The x86 processors offer several debugging mechanisms, so it would probably be possible to have a program set itself up as a debugger for ADOM and break on any access to video memory. This again has the problem of being a much lower-level approach than ADOM Sage uses.

A third approach would be to use disassembly to locate and replace the library functions with ADOM Sage's versions of the functions. This also is a lot of work, and it would have to be repeated for each new version of ADOM.

So, in short, there probably won't be a port of ADOM Sage to DOS.