Porting WWIV 4.23 to FreeBSD UNIX
Robert N. M. Watson
In January, 2007, I spent a weekend porting WWIV 4.23, based on a source
license from 1995, to FreeBSD 6.2.
These notes describe what's involved in doing this.
As the WWIV 4.x source code is not open source, I cannot redistribute the
patches, but I can provide supplemental DOS and BIOS compatibility source
that anyone wanting to replicate this can use.
I ported the basic WWIV structure, including the communications and display
libraries, user system, message bases, g-files, e-mail, transfers, and so on.
I made no attempt to deal with external programs, such as doors, WWIV
networking, external events, and external transfer protocols.
By the end of the weekend, I was able to log in locally and via FreeBSD's
synthetic NULL modem device driver, read e-mail, messages, and so on.
This port is hardly complete, but as proof of concepts go, it's not shabby.
Discovered while perusing posts from my BBS message boards using the FreeBSD
port of WWIV:
47/50: Port to UNIX
Name: Star Gazer #1 @3101
Date: Sat Nov 19 22:21:27 1994
I'm currently in the process of porting WWIV to Unix so I can switch to a free
implementation of BSD (eg., FreeBSD 2.0 when released.) If anyone out there
has experience with Unix/C (and BSD style socket access) and is interested in
helping out, please let me know. Email firstname.lastname@example.org, or autoreply to
this mail and I can email you source and access information.
陳陳様様様- Star Gazer -様様様陳陳
I meant well, but never got much further than that e-mail. However, this does
illustrate an early interest in FreeBSD!
No software port is complete without the a few screenshots. These images
were captured using ksnapshot running on FreeBSD 7.x, with WWIV 4.23 running
over the synthetic null modem device (nmdm). Click to view the full-size
WWIV is written in relatively portable Borland C code, but relies on the
availability of a variety of system functions and libraries present in the MS
DOS and Borland C programming environments. These include DOS APIs for file
system access, BIOS functions for screen management, and direct hardware I/O
for serial port access. To get WWIV running on FreeBSD, I had to provide
support routines to emulate these services based on natively available
services, such as POSIX I/O routines, ncurses, etc. There are also some C
language differences that must be adapted to, and in some cases papered over.
Why not just use WWIV 5.0, the open source C++ offspring of WWIV 4? Well, I
have a WWIV 4.23 install already, and am, at heart, a C programmer, not a C++
programmer. However, you can learn more about WWIV 5 on its sourceforget.net web
New Files (DOS, BIOS compatibility)
New files created during the port, under a BSD license:
- borland.h - Functions and definitions from
Borland C not found in most UNIX environments
- cons.h - Abstracted ncurses interface to resemble
functions implemented by video BIOS
- dos.h - Functions and definitions for DOS library
- emul.h - General functions for setting up the DOS
and BIOS emulation environment
- my_bios.c - Implementation of some BIOS and
- my_borland.c - Implementation of Borland
- my_com.c - Implementation of WWIV communications
API using UNIX ttys
- my_cons.c - Implementation of simple BIOS video
functions using ncurses
- my_dos.c - DOS library functions wrapped around
POSIX, mostly file access
- my_emul.c - Infrastructure and setup for emulation environment
Porting WWIV to UNIX in 10 Easy Steps
-1. Rename all source files to be all lower-case.
This is UNIX.
0. Create a makefile
The shipped makefiles are not useful as a starting point, create new
1. C language changes
- Remove all "#pragma hdrstop" statements; these are a Borland C
compiler performance optimization and to not apply with gcc. Likewise
other #pragma statements.
- Rename WWIV 'yn()' function to 'yesno()' to avoid collision with then
yn(3) floating point function.
- #define far, interrupt, and huge to nothing, since they don't apply.
- #define farmalloc() and farfree() to POSIX equivilents.
- Remove references to farcoreleft(), which returns the amount of free
memory available in the Borland C runtime environment. This was purely
- Eliminate or otherwise ignore all code relating to overlays, which
are not required.
- Fix certain printfs, sprintfs, etc. For example, gcc doesn't like
format strings specifying decimal points for longs.
- Cast arguments to time(3) to (time_t *), not (long *). Similar
time_t related changes elsewhere.
- Provide our own OFFOF() macros to replace use of FP_OFF(), since far
pointers don't exist in UNIX-land. This sort of expression comes in
handy: #define OFFOF(x) ((uintptr_t)(&u.x) - (uintptr_t)(&u))
- Replace dubious casts of data structure types to pointer casts. For
example, replace: "&(type)variable" with: "(type *)&variable".
- Replace "random(x)" with "(random() % x)". Initialize the random
number generator with srandomdev() rather than randomize().
- Modern compilers and systems pad structures for alignment reasons;
Borland C did not. As WWIV interchangeably uses on-disk and in-memory
data structure layouts, it's easiest simply to mark all WWIV data
Structures as __packed in order to eliminate binary incompatibility.
In particular, look in instmsg.h, net.h, and vardec.h. This is
important because the source code for INIT.EXE was not distributed to
WWIV source licensees, and so there is no way to initialize new BBS
configuration files without writing a new initialization tool.
- Implement Borland C string functions that don't exist in POSIX/C99:
gcvt(), itoa(), ltoa(), stricmp(), strupr(), ultoa().
- Implement Borland C non-portable I/O functions getch(), getche().
2. Adjust include files
Search for and remove inclusions of: alloc.h conio.h dos.h io.h mem.h;
In some cases, replace paths, for example sys\stat.h to sys/stat.h.
3. Disable everything relating to printers
Search for and #ifdef out references to stdprn and sysconfig_printer.
Probably this should be replaced with syslog(3) calls.
4. Disable all external programs
Do so by hook or by crook; I disabled various shell points, execution
points, external chat programs, network callouts, external programs,
etc, using ifdefs.
5. Build a serial port compatibility library
- Rename many functions in com.c to begin with wwiv_ or #ifdef out
entirely (such as setbeep(), dtr(), etc).
- Implement check_comport(), async_isr(), outcomch(), peek1c(),
get1c(), comhit(), dump(), set_baud(), initport(), closeport(), dtr(),
rts(), cdet(), empty() in terms of UNIX ttys. Implement ring buffer,
etc, to allow peaking and so on.
6. Build a BIOS and console compatibility library
- Implement several parallel instances of various functions that invoke
DOS or BIOS interrupts, including geninterrupt().
- Implement interrupt 0x10 (video BIOS) functions in terms of ncurses,
including 0x2 (setxy), 0x3 (getxy), 0x6 (scrollup), 0x7 (scrolldown),
0x9 (putchar), 0xf (getwidth).
- Implement keyboard interrupt 06 function 0x1 (check for key
- Implement DOS interrupt routines 0x6 (console I/O), 0x33 (ctrl-break
- Implement setbeep() in terms of ncurses.
- Provide peek() and peekb() for video mode, keyboard status, and
numlock state (sysop available to chat).
7. Build a DOS compatibility library
- Search through for references to function name and constant conflicts
between DOS and POSIX. For example, chdir() to dos_chdir(), mkdir() to
dos_mkdir(), and open() to dos_open().
- Define _osmajor, _osminor.
- Define struct ftime, struct date, struct time, struct dostime, struct
ffblk, struct dfree, struct SREGS, struct DWORDREGS, struct WORDREGS,
struct BYTEREGS, struct REGS.
- Define _S_IWRITE to S_IWUSR, _S_IREAD to S_URUSR.
- Define O_BINARY to 0.
- Define MAXDRIVE, MAXPATH, MAXDIR, MAXFILE, MAXEXT, DRIVE, DIRECTORY,
FILENAME, EXTENSION, WILDCARDS.
- Define compatibility flags for dos_open() which will be mapped to
similar UNIX semantics: SH_COMPAT, SH_DENYRW, SH_DENYWR, SH_DENYRD,
- Create wrapper geninterrupt(), getvect(), int86(), int86x(),
- Implement DOS library calls: _chmod(), chsize(), delay(),
dostounix(), filelength(), findfirst(), findnext(), fnsplit(),
getcurdir(), getdate(), getdfree(), getftime(), getdisk(), gettime(),
searchpath(), setdisk(), setftime(), unixtodos(), dos_chdir(),
dos_mkdir(), dos_open(). Also dos_init() to initialize the DOS
run-time environment. Among other things, the library calls simulate
multiple drives pointed at various UNIX paths, and the fact that DOS
programs have several different simultaneous working directories (one
- Implement interrupt 21h function 0x2a (get time).
8. Add MT_UNIX to utility.c, comment out various things that can't
10. Fix lots of random compiling things that aren't meaningful anymore,
such as attempts to manipulate stack pointers in extrn.c.
Since we're not implementing external programs or overlays, these can
basically be ignored.
There are so many issues it's almost silly to enumerate them, but here are
- My understanding of ncurses, especially scrolling, is poor, so the
console login is of limited utility.
- Right now there's no way to set that the sysop is local, so chatting
- I did test multi-instance execution, but not much.
- Not being able to run external programs, such as the daily external event
and networking, is quite a problem.
- You must have an existing WWIV configuration since INIT.EXE source isn't
available. It would be easy enough to create one, but I haven't since I
already have a configuration.
- There are a number of Y2K bugs in WWIV 4.23.
- Certain activities, such as reading the display using direct memory
access, don't work. As such, activities that may re-draw the prompt and
current input, which include requesting help in some contexts, fail.
- Input during the waiting-for-caller screen is unreliable.
Copyright 2007 Robert N. M. Watson