Lotus 1-2-3 DOS Development Encyclopedia.

Tavis Ormandy

$Id: a07cf90837a3c4373b82d6724b97593810766af7 $

Everything I know about Lotus 1-2-3 internals.

Note: This document is incomplete.

Notes

I am only interested in the 123R3 series, but this can get confusing.

123R3 was the first version of 123 after the rewrite in C, prior versions were written in assembly. This means that it is not ABI, API or driver compatible with any other major versions.

However, I’m also interested in 123R4D, which is essentially just an update to 123R3 and is binary compatible with addins and drivers. It’s not that simple though, 123R4D (DOS) is an entirely different product than 123R4W (Windows). You might expect them to be API compatible - but they’re not.

However, the 123R4W ADK (Add-in Development Kit) includes an API compatability library that means you can recompile 123R3/123R4D code for 123R4W, so they’re source compatible. You can’t go in the other direction - there is no way to make a 123R4W add-in work on 123R4D.

Addins

There are two major types of Addins. Those with a DLD extension are dynamically loaded libraries. Those with a PLC extension are compiled LPL scripts. LPL was a scripting language developed specifically for 1-2-3 addins, the language later became LotusScript and was used in other Lotus products.

Here is a fun review of the commercial addins available in 1993.

LPL Addons

To compile an LPL plugin, you need the 1-2-3 DOS ADK, I have a copy available here. These files were thought to be lost for many years, thanks to scenelist for recovering them from their classic BBS collection!

Books

I have a copy of the book “Lotus Add-In Toolkit Guide” by Robert Ainsbury (ISBN 0880225505) which described the language, has code samples and screenshots of the editor and debugger.

I have some low quality scans from the book if you want to see them.

one two three four

TODO: Get the book scanned.

Drivers

An addin typically add @functions or other functionality, whereas drivers abstract away some hardware or software details.

Types and Names

There are four types of drivers, DISPLAY, PRINTER, FILE, and LINK.

Drivers must follow a naming convention that 123 uses to determine what type of driver it is. The filename format is L13XYYYY.DLZ

              ┌→ Release 3
     Lotus ←┐ │ ┌→ Name
            ↓ ↓ ↓
            L13XYYYY.DLZ ← Platform, D (DOS) or W (Windows)
             ↑ ↑
      1-2-3 ←┘ └→ Type, D, P, F, or L

If you have a DRV file, that is from an earlier 123 release. If you have a DLD file that does not follow this naming convention, then it is an addin and not a driver. The difference is simply what APIs are exposed when loaded and what exports 123 expects to find.

Using the wrong code or trying to load a driver as an addin will cause 123 to crash, because they all use different APIs.

The purpose of DISPLAY and PRINTER drivers is obvious. FILE drivers allow i/o from new file formats, and LINK drivers allow 123 to interact with a database or other data source.

Drivers are shipped with configuration files in a binary format called a BUNDLE. A bundle contains a short header followed by a series of TLV (Tag, Length, Value) records. This allows some configuration of the driver during installation, such as fonts, colors, charset, resolution, etc.

Note: The BUNDLE related APIs use the prefix BDL.

See the description of VBD files for an example, a VBD is a Video Bundle.

API

I have never seen the 123 Driver Development Kit, so I’ve had to reverse engineer it.

Sources of information

I converted the adk hlp file to html, if you’re interested.

Here is the driver, and I’ve dumped the CodeView data.

Here is the lucky addin, and I’ve dumped the stabs.

Mirror available Here

I do have the R9 (I think?) ADK, it’s of no interest to me but maybe someone else wants it.

Others

I do not have these, but would really like to see them.

  • The book “Customizing 1-2-3 Release 3” by Barton Listick, ISBN 0201523248.
  • Presumably there was some kind of “driver” development kit shipped to OEMs.

In particular, I would love to see the SunOS or Xenix builds, or a copy of SCO Professional.

I’m aware of a few other ports, but I’m not sure how relevant they are to 123R3, e.g.

  • OpenVMS
  • Xenix
  • System/390

SCO Professional

Update (December 2021): A vintage UNIX enthusiast was kind enough to point me to a copy of SCO Professional for Xenix. This includes procalc a mostly complete 32-bit x86 UNIX clone of 123! Here is a screenshot.

************************************
*                                  *
*         SCO Professional         *
*         ( 386 Version )          *
*                                  *
*  Portions Copyright (c) 1985-88  *
*  The Santa Cruz Operation, Inc.  *
*   Portions Copyright (c) 1985    *
*   VIP Technologies Corporation   *
*       All Rights Reserved        *
*          Version 2.0.0           *
*            sco001429             *
************************************

I was interested in looking at the disassembly, but SCO distributed their software in an obfuscated form that isn’t readable until you “serialize” it. That requires entering some codes into a Xenix program.

I’ve done the hard work for you, I setup a Xenix VM and serialized it. The binaries are all in Xenix x.out format.

In theory, I think this should work with iBCS on Linux, but I haven’t tried.

SysV386

Update (April 2022): I now have a copy of 1-2-3 for SysV386. It was worth the wait…. remarkably it was shipped with the original unstripped object files on the installation media. Combined with the type data recovered from SunOS, this will be an invaluable source of API information.

objcopy supports this object format and can convert it to ELF, it seems plausible we could mark a few symbols weak, then link it into a native linux executable….is a native linux port of 1-2-3 now feasible?

Here is the manual page and here is the main i386 COFF object file (nearly 20,000 symbols!).

The linker time stamp says it was compiled on September 8th 1990:

$ objdump -p 123.o | grep Date
Time/Date               Sat Sep  8 06:23:50 1990

Goldstein’s C Library

I found a copy of an old C library for parsing worksheet files on eBay. With the help of a friend I was able to image the files.

They’re available here if you’re interested.

Siemens Highprint 7400

The file L13PSI74.DLD is a third-party print driver presumably developed by Siemens. I found a copy on a shareware archive, and was surprised to find it had some embedded CodeView data.

It was an ancient verion that no modern tools could parse, I even tried old tools like TDUMP, CVDUMP and and SYMDEB. I did eventually find an old debugger that could dump it. Unfortunately, it was quite a challenge extracting the output - it had no way to save output to a file except “printing” your backlog, and the backlog wasn’t big enough to hold all the data!

I had to script a DOS emulator to scrape the data out a page at a time! I can’t tell you how much time I wasted getting this working, but it was worth it, it gave some valuable hints about how the DEVPRIM API worked.

XALERT addin for SunOS

The Lotus FTP server had a few official addins still available to download after all these years, and amazingly one was mistakenly compiled with very detailed stabs data.

That addin was alert.so1, the XALERT addin for SunOS4. It’s a SPARC demand paged shared library, which is a long-obsolete format.

I was able to find an older version of GNU binutils that still supported a.out-sunos-big, and could dump it.

The structure names and enums were remarkably helpful, even though it’s for an entirely different platform.

123R4W Addin Development Kit

I sadly do not have the 123R4D or 123R3 development kits, but I do have the 123R4W kit. This includes headers, libraries and a comprehensive HLP library.

123R4W Directory Layout…

.
├── [        512 Feb 16 07:17]  BINB
│   └── [     228080 Jun 10  1993]  LRC.EXE
├── [        512 Feb 16 07:26]  HELP
│   ├── [      20928 Feb 20 18:44]  ADKREF.GID
│   ├── [     611855 Jul 26  1993]  ADKREF.HLP
│   └── [        766 Dec 27  1992]  ADKREF.ICO
├── [        512 Feb 20 22:41]  INCLUDE
│   ├── [      10788 Jul 29  1993]  GUITYPES.RC
│   ├── [      23469 Jul 29  1993]  KEYCODE.H
│   ├── [       3372 Jul 29  1993]  LCIAF.H
│   ├── [      23400 Jul 29  1993]  LCIATFNC.H
│   ├── [      11962 Jul 29  1993]  LCICELL.H
│   ├── [      10909 Jul 29  1993]  LCICOMN.H
│   ├── [      19826 Jul 29  1993]  LCIENUM.H
│   ├── [       7873 Jul 29  1993]  LCIERROR.H
│   ├── [       4487 Jul 29  1993]  LCIEVENT.H
│   ├── [      26115 Jul 29  1993]  LCIEVENT.T
│   ├── [       8956 Jul 29  1993]  LCIGUI.H
│   ├── [       3534 Jul 29  1993]  LCILD.H
│   ├── [       6465 Jul 29  1993]  LCIMATH.H
│   ├── [       8656 Jul 29  1993]  LCIMB.H
│   ├── [        902 Jul 29  1993]  LCIMBLIB.H
│   ├── [       2332 Jul 29  1993]  LCIMC.H
│   ├── [      11499 Jul 29  1993]  LCIRANGE.H
│   ├── [       5823 Jul 29  1993]  LCISHEET.H
│   ├── [       5265 Jul 29  1993]  LCIUTIL.H
│   ├── [       9116 Jul 29  1993]  LCIWFILE.H
│   ├── [      12990 Jul 29  1993]  LCIWKSPC.H
│   ├── [       1390 Jul 29  1993]  LPIMEMM.H
│   ├── [       9335 Jul 29  1993]  LPIMEMM.T
│   ├── [       9568 Jul 29  1993]  LPIPRIM.H
│   ├── [       2399 Jul 29  1993]  LPIXPORT.H
│   ├── [      44708 Jul 29  1993]  LPXATFNC.H
│   ├── [      19643 Jul 29  1993]  LPXCELL.H
│   ├── [       1781 Jul 29  1993]  LPXCOMN.H
│   ├── [      20236 Jul 29  1993]  LPXDEFS.H
│   ├── [      12832 Jul 29  1993]  LPXENUM.H
│   ├── [       7590 Jul 29  1993]  LPXERROR.H
│   ├── [       1959 Jul 29  1993]  LPXMACRO.H
│   ├── [       4966 Jul 29  1993]  LPXMATH.H
│   ├── [      23952 Jul 29  1993]  LPXRANGE.H
│   ├── [      11743 Jul 29  1993]  LPXSHEET.H
│   ├── [       2423 Jul 29  1993]  LPXUTIL.H
│   ├── [      18223 Jul 29  1993]  LPXWFILE.H
│   ├── [      17015 Jul 29  1993]  LPXWKSPC.H
│   ├── [      20055 Jul 29  1993]  LRCDEFS.H
│   ├── [       5704 Jul 29  1993]  LTSCNTRL.H
│   ├── [      21294 Jul 29  1993]  LTSDEFS.H
│   ├── [       3876 Jul 29  1993]  LTSSTRCT.H
│   └── [      47128 Jul 29  1993]  WINRESID.H
├── [        512 Feb 19 16:03]  LIB
│   ├── [      67072 Jul 29  1993]  LCI.LIB
│   ├── [       3558 Jun 10  1993]  LIBMAIN.OBJ
│   ├── [      16384 Jun 10  1993]  LMBSRVW.LIB
│   ├── [      72704 Jun 10  1993]  LPIMISC.LIB
│   └── [       3408 Jun 10  1993]  WEP.OBJ
├── [      12341 Jul 27  1993]  READ.ME
└── [        512 Feb 19 16:04]  SAMPLES
    ├── [        512 Feb 16 07:16]  ADDMENU
    │   ├── [        485 Jul 29  1993]  ADDMENU.BMK
    │   ├── [      15944 Jul 29  1993]  ADDMENU.C
    │   ├── [        568 Jul 29  1993]  ADDMENU.DEF
    │   ├── [       2237 Jul 29  1993]  ADDMENU.H
    │   ├── [       2384 Jul 29  1993]  ADDMENU.MAK
    │   ├── [       1022 Jul 29  1993]  ADDMENU.NMK
    │   └── [       1606 Jul 29  1993]  ADDMENU.RC
    ├── [        512 Feb 16 07:16]  ADNDLG
    │   ├── [        528 Jul 29  1993]  ADNDLG.BMK
    │   ├── [      12134 Jul 29  1993]  ADNDLG.C
    │   ├── [        566 Jul 29  1993]  ADNDLG.DEF
    │   ├── [       2585 Jul 29  1993]  ADNDLG.H
    │   ├── [       2439 Jul 29  1993]  ADNDLG.MAK
    │   ├── [       1161 Jul 29  1993]  ADNDLG.NMK
    │   └── [       3157 Jul 29  1993]  ADNDLG.RC
    ├── [        512 Feb 16 07:16]  ADNMDI
    │   ├── [        528 Jul 29  1993]  ADNMDI.BMK
    │   ├── [      11067 Jul 29  1993]  ADNMDI.C
    │   ├── [        578 Jul 29  1993]  ADNMDI.DEF
    │   ├── [       2451 Jul 29  1993]  ADNMDI.H
    │   ├── [       2439 Jul 29  1993]  ADNMDI.MAK
    │   ├── [       1159 Jul 29  1993]  ADNMDI.NMK
    │   └── [       3539 Jul 29  1993]  ADNMDI.RC
    ├── [        512 Feb 16 07:17]  AFHELLO
    │   ├── [        633 Jul 29  1993]  AFHELLO.BMK
    │   ├── [       4577 Jul 29  1993]  AFHELLO.C
    │   ├── [        552 Jul 29  1993]  AFHELLO.DEF
    │   ├── [       2233 Jul 29  1993]  AFHELLO.MAK
    │   └── [       1010 Jul 29  1993]  AFHELLO.NMK
    ├── [        512 Feb 16 07:17]  AMORT
    │   ├── [        607 Jul 29  1993]  AMORT.BMK
    │   ├── [      19650 Jul 29  1993]  AMORT.C
    │   ├── [        548 Jul 29  1993]  AMORT.DEF
    │   ├── [       2259 Jul 29  1993]  AMORT.MAK
    │   └── [       1004 Jul 29  1993]  AMORT.NMK
    ├── [        512 Feb 16 07:17]  COLORS
    │   ├── [        652 Jul 29  1993]  COLORS.BMK
    │   ├── [      20170 Jul 29  1993]  COLORS.C
    │   ├── [        578 Jul 29  1993]  COLORS.DEF
    │   ├── [       5049 Jul 29  1993]  COLORS.H
    │   ├── [        766 Oct  8  1991]  COLORS.ICO
    │   ├── [       2439 Jul 29  1993]  COLORS.MAK
    │   ├── [       1167 Jul 29  1993]  COLORS.NMK
    │   └── [       8030 Jul 29  1993]  COLORS.RC
    ├── [        512 Feb 16 07:17]  DEMO
    │   ├── [       5632 Jul 29  1993]  ADDMENU.ADW
    │   ├── [      37316 Jul 22  1993]  ADKDEMO.WK4
    │   ├── [       6144 Jul 29  1993]  ADNDLG.ADW
    │   ├── [       8704 Jul 29  1993]  ADNMDI.ADW
    │   ├── [       2784 Jul 29  1993]  AFHELLO.ADW
    │   ├── [       5376 Jul 29  1993]  AMORT.ADW
    │   ├── [      15360 Jul 29  1993]  COLORS.ADW
    │   ├── [       8704 Jul 29  1993]  FILEMEMO.ADW
    │   ├── [       7680 Jul 29  1993]  LMBCS.ADW
    │   ├── [       2784 Jul 29  1993]  MCHELLO.ADW
    │   ├── [       3792 Jul 29  1993]  MYSUM.ADW
    │   ├── [       2768 Jul 29  1993]  NAVIGATE.ADW
    │   ├── [       3536 Jul 29  1993]  UNDOEX.ADW
    │   ├── [      12800 Jul 29  1993]  WECYCLE.ADW
    │   └── [       2752 Jul 29  1993]  WIDTHX2.ADW
    ├── [        512 Feb 16 07:17]  FILEMEMO
    │   ├── [        686 Jul 29  1993]  FILEMEMO.BMK
    │   ├── [      26423 Jul 29  1993]  FILEMEMO.C
    │   ├── [        626 Jul 29  1993]  FILEMEMO.DEF
    │   ├── [       1308 Jul 29  1993]  FILEMEMO.H
    │   ├── [       2850 Jul 29  1993]  FILEMEMO.MAK
    │   ├── [       1177 Jul 29  1993]  FILEMEMO.NMK
    │   └── [       3029 Jul 29  1993]  FILEMEMO.RC
    ├── [       1696 Jul 29  1993]  LIBMAIN.C
    ├── [        512 Feb 16 07:17]  LMBCS
    │   ├── [        511 Jul 29  1993]  LMBCS.BMK
    │   ├── [      12274 Jul 29  1993]  LMBCS.C
    │   ├── [        597 Jul 29  1993]  LMBCS.DEF
    │   ├── [       4279 Jul 29  1993]  LMBCS.H
    │   ├── [       2406 Jul 29  1993]  LMBCS.MAK
    │   ├── [       1154 Jul 29  1993]  LMBCS.NMK
    │   └── [       4237 Jul 29  1993]  LMBCS.RC
    ├── [        512 Feb 16 07:17]  MCHELLO
    │   ├── [        633 Jul 29  1993]  MCHELLO.BMK
    │   ├── [       5933 Jul 29  1993]  MCHELLO.C
    │   ├── [        552 Jul 29  1993]  MCHELLO.DEF
    │   ├── [       2228 Jul 29  1993]  MCHELLO.MAK
    │   └── [       1010 Jul 29  1993]  MCHELLO.NMK
    ├── [        512 Feb 16 07:17]  MYSUM
    │   ├── [        607 Jul 29  1993]  MYSUM.BMK
    │   ├── [      10805 Jul 29  1993]  MYSUM.C
    │   ├── [        548 Jul 29  1993]  MYSUM.DEF
    │   ├── [       2445 Jul 29  1993]  MYSUM.MAK
    │   └── [       1004 Jul 29  1993]  MYSUM.NMK
    ├── [        512 Feb 16 07:17]  NAVIGATE
    │   ├── [        646 Jul 29  1993]  NAVIGATE.BMK
    │   ├── [       3804 Jul 29  1993]  NAVIGATE.C
    │   ├── [        552 Jul 29  1993]  NAVIGATE.DEF
    │   ├── [       2240 Jul 29  1993]  NAVIGATE.MAK
    │   └── [       1013 Jul 29  1993]  NAVIGATE.NMK
    ├── [        512 Feb 16 07:17]  UNDOEX
    │   ├── [        620 Jul 29  1993]  UNDOEX.BMK
    │   ├── [       6751 Jul 29  1993]  UNDOEX.C
    │   ├── [        560 Jul 29  1993]  UNDOEX.DEF
    │   ├── [       2115 Jul 29  1993]  UNDOEX.MAK
    │   └── [       1007 Jul 29  1993]  UNDOEX.NMK
    ├── [        512 Feb 16 07:17]  WECYCLE
    │   ├── [        370 Jul 21  1993]  TRUCK.BMP
    │   ├── [        766 Sep  2  1991]  TRUCK.ICO
    │   ├── [        691 Jul 29  1993]  WECYCLE.BMK
    │   ├── [      19406 Jul 29  1993]  WECYCLE.C
    │   ├── [        584 Jul 29  1993]  WECYCLE.DEF
    │   ├── [       2066 Jul 29  1993]  WECYCLE.H
    │   ├── [       2984 Jul 29  1993]  WECYCLE.MAK
    │   ├── [       1180 Jul 29  1993]  WECYCLE.NMK
    │   └── [      13859 Jul 29  1993]  WECYCLE.RC
    ├── [       1545 Jul 29  1993]  WEP.C
    └── [        512 Feb 16 07:17]  WIDTHX2
        ├── [        633 Jul 29  1993]  WIDTHX2.BMK
        ├── [       4050 Jul 29  1993]  WIDTHX2.C
        ├── [        529 Jul 29  1993]  WIDTHX2.DEF
        ├── [       1969 Jul 29  1993]  WIDTHX2.MAK
        └── [       1010 Jul 29  1993]  WIDTHX2.NMK

20 directories, 158 files

Compatability Library

The 123R4W ADK uses the Lci symbol prefix, 123R4D uses Lpi. There is a library that translates many functions.

Display Driver API

The display driver requires the following function exported by ordinal. Note that I do not know the names of any of these functions, structures or types - I have guessed them from reverse engineering what 123 is doing.

Also, expect mistakes or incomplete information - I’ve never seen any documentation!

Note: If you do find an error, I would like to hear about it!

A working implementation of a 123R4D driver is available here, it might be a useful reference if you’re trying to get 123 work somewhere new.

1. GetDisplayInfo

This function simply copies a struct DISPLAYINFO into the far pointer provided. 123 uses this to learn the display charateristics. The DISPLAYINFO struct is given below.

Note: DriverInit() is called before GetDisplayInfo(), even though it’s declared first.

struct DISPLAYINFO_ { /* size 24 id 107 */
  short unsigned int num_text_cols; /* bitsize 16, bitpos 0 */
  short unsigned int num_text_rows; /* bitsize 16, bitpos 16 */
  short int graphics; /* bitsize 16, bitpos 32 */
  short int full_screen_graph; /* bitsize 16, bitpos 48 */
  short unsigned int hpu_per_col; /* bitsize 16, bitpos 64 */
  short unsigned int graph_cols; /* bitsize 16, bitpos 80 */
  short unsigned int graph_rows; /* bitsize 16, bitpos 96 */
  short unsigned int graph_col_res; /* bitsize 16, bitpos 112 */
  short unsigned int graph_row_res; /* bitsize 16, bitpos 128 */
  short unsigned int view_set_size; /* bitsize 16, bitpos 144 */
  short int iscolor; /* bitsize 16, bitpos 160 */
};

The prototype is:

void __pascal __far GetDisplayInfo(struct DISPLAYINFO far *dspinfo);

2. DriverInit

This function is called as soon as the device is loaded, you can use this function to initalize any data structure you want, parse configuration data, etc.

The prototype is something like:

int __pascal DriverInit(void far *drvapi,
                        struct BDLHDR far *vbdptr,
                        const char far *cfgname,
                        char deflmbcsgrp);

Return 0 if you successfully initialized, or 1 if there was an error.

Param Description
deflmbcsgrp The LMBCS code group to use with optimized LMBCS.
cfgname The filename of the BDL configuration data.
vbdptr Pointer to your BDL data.
drvapi Array of function pointers used to interact with 123.

The vbdptr is a BDLHDR, followed by a sequence of BDLRECHDRs.

struct BDLHDR
{
  int bdllen;
  int bdlver;
  int driver_id;
  int bundle_id;
};

struct BDLRECHDR
{
  int bdltype;
  int reclen;
};

Before this routine completes, you should use RegisterDevData to report device capabilities and graphics callbacks to 123.

struct DEVDATA
{
  int ShowMeFlag;
  int RasterHeight;
  int AspectX;
  int AspectY;
  int RHmusPerTHmu;
  int RHmuPerGHmus;
  int RVmusPerTVmu;
  int RVmuPerGVmus;
  PATT FillPatts[12];
  unsigned char ColorMap[16];
  int SrcPatD[8];
  int SrcPatS[8];
  int FIndexCnt;
  void far *FontIndex;
  int FNamesCnt;
  void far *FontNames;
  DEVPRIM DevFuncs;
};

struct DEVPRIM
{
  void (__pascal __far *scan_linx)(int, int, int, int);
  void (__pascal __far *fill_rect)(int, int, int, int, int);
  void (__pascal __far *thin_diag_line)(int, int, int, int, int);
  void (__pascal __far *thin_vert_line)(int, int, int, int);
  void (__pascal __far *shade_rect)(int, int, int, int, void far *, int);
  void (__pascal __far *fill_scan_list)();
};

struct POINT
{
  int pty;
  int ptx;
};

struct PATT
{
  POINT pattsize;
  POINT patthotSpot;
  void far *pattptr;
  POINT pattSecOffset;
};

3. DriverTerminate

This function is called when you are being unloaded, you can use it to cleanup.

int __pascal DriverTerminate();

4. ResetDisplay

This function is called when 1-2-3 exits graphics mode (e.g after the user closes a graph).

You would typically use it to switch back to text mode. The screen should be blank, ready to write text, and the cursor should be at 0,0 when complete.

int __pascal ResetDisplay();

5. SetGraphicsMode

Prepare the screen for graphics, e.g. when the user presses F10.

The screen should be blank and ready to accept drawing commands (e.g. fill_rect, thin_line, etc) when this returns.

int __pascal SetGraphicsMode();

6. MoveCursor

Move the internal cursor to the specified position, you do not have to adjust the position of the visible cursor - only where the next screen write will occur.

void __pascal MoveCursor(int col, int line);
Param Description
line Move the cursor to this line.
col Move the cursor to this col.

7. WriteLmbcsStringWithAttributes

A pointer to an LMBCS string is specified with color attributes, it should be translated to the users current charset, and printed to the current cursor location.

int __pascal WriteLmbcsStringWithAttributes(unsigned byteslen,
                                            unsigned char far *lmbcsptr,
                                            unsigned char attrs);
Param Description
attrs The requested color attributes.
lmbcsptr Far pointer to an LMBCS string.
byteslen Length of lmbcsptr in bytes to be translated.

Screen Attributes

The attributes are made of 6 bits, 3 bits representing the foreground class and 3 bits representing the background class. These don’t represent any specific colors - just types, you can draw them however you want.

8. WritePaddedLmbcsStringWithAttributes

This is similar to WriteLmbcsStringWithAttributes, but you can specify padding to apply to either side of the string. The attributes specified are applied to the padding and the string.

Using SPC characters for padding is fine.

int __pascal WritePaddedLmbcsStringWithAttributes(unsigned byteslen,
                                                  char far *lmbcsptr,
                                                  unsigned char attrs,
                                                  unsigned startpad,
                                                  unsigned endpad);
Param Description
endpad How much padding is required after the content.
startpad How much padding is required before the content.
attrs The requested color attributes.
lmbcsptr Far pointer to an LMBCS string.
byteslen Length of lmbcsptr in bytes to be translated.

9. SetRegionBgAttributes

Change the bg attributes of a screen region, leaving the contents intact. The region starts at the current cursor.

For example, this might be used to change the background color.

Note that the text and fg attributes are unchanged.

void __pascal SetRegionBgAttributes(unsigned cols,
                                    unsigned lines,
                                    char attrs);

As far as I can tell, 123 will never request an invalid column or line number, i.e. you are not expected to handle line wrap or off-screen drawing.

Param Description
attrs The requested color attributes.
numlines How many lines to apply this change to.
endcol On the last line, where should the region stop.

10. ClearRegionForeground

Remove the foreground content (i.e. write blanks over it and clear fg attributes). New background attributes can be specified.

void __pascal ClearRegionForeground(unsigned cols,
                                    unsigned lines,
                                    unsigned char attrs);
Param Description
attrs The requested color attributes.
numlines How many lines to apply this change to.
endcol On the last line, where should the region stop.

11. ClearRegionForegroundKeepBg

The same as ClearRegionForeground, but the existing bg attributes are retained.

void __pascal ClearRegionForegroundKeepBg(unsigned cols, unsigned lines);
Param Description
numlines How many lines to apply this change to.
endcol On the last line, where should the region stop.

12. BlockRegionCopy

Copy a rectangular, including attributes, from one screen area to another.

The regions may overlap, and you’re expected to handle that.

void __pascal BlockRegionCopy(int width, int height, int dstx, int dsty);
Param Description
width Number of columns of the region.
height Number of lines.
dstx The destination x position, the source is given by the cursor.
dsty The destination y position.

13. CalcSizeTranslatedString

You should translate the passed LMBCS string into the current character set, and then return the length. You don’t have to display anything.

int __pascal CalcSizeTranslatedString(int argstrlen, char far *argstr);
Param Description
argstr The LMBCS string to translate.
argstrlen Length of the string.

14. FitTranslatedString

Lotus has maxlen columns to display an LMBCS string, so you should translate it and truncate it (if necessary) to fit.

int __pascal FitTranslatedString(int strarglen,
                                 char far *strarg,
                                 int ncols,
                                 int far *bytesneeded)
Param Description
dstptr Write the translated & truncated string here.
ncols The maximum length the translated string should be.
argstr The LMBCS string to translate.
argstrlen Length of the string.

15. SetCursorInvisible

The screen cursor should no longer be visible to the user, perhaps because they are navigating a menu.

void __pascal SetCursorInvisible();

16. SetCursorVisible

The user is entering data so the screen cursor should be visible. The position should match the internal cursor, as set by MoveCursor.

void __pascal SetCursorVisible();

17. LockCursorAttributes

Not really sure why this is useful, or what the intention is. I believe it ignores changes to cursor attributes until turned off.

void __pascal LockCursorAttributes(int, int);

18. Unknown

This is called before entering graphics mode, and appears to be related to querying resolution data.

int __pascal QQCalledBeforeGraphicsMode(void far *m)

19. DrawString

This appears to be related to setting graph legends in graphics mode.

int __pascal DrawString(int strarglen, void far *strarg);

20. MoveCursor2

This appears to be identical to MoveCursor().

void __pascal MoveCursor2(int col, int line);

21. IsGraphicsModeReady

When entering graphics mode, this function is called repeatedly until it returns 1. Perhaps the intention is to query if the hardware is ready to display graphics.

int __pascal __far IsGraphicsModeReady(void far *ptr)

Printer Driver API

The display driver API is quite complicated, the printer driver API is simple in comparison. Even better, it’s essentially just a tiny subset of the DISPLAY API (the DEVPRIM part).

File Driver API

TODO

I don’t know very much about the FILE API, but if it means I could write a driver that allows Lotus 1-2-3 for DOS to open modern XLS or ODS files, then that sounds like the kind of ridiculous project I would be interested in!

If you didn’t already know this, LibreOffice can open Lotus WK3 files just fine.

Native Add-In API

TODO

LPL Add-In API

TODO

Other Extension Languages

The OS/2 port had an official REXX extension API.

Building Native Add-Ins

TODO