$Id: a07cf90837a3c4373b82d6724b97593810766af7 $
Everything I know about Lotus 1-2-3 internals.
Note: This document is incomplete.
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.
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.
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!
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.
TODO: Get the book scanned.
An addin typically add @functions or other functionality, whereas drivers abstract away some hardware or software details.
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.
I have never seen the 123 Driver Development Kit, so I’ve had to reverse engineer it.
I converted the adk hlp file to html, if you’re interested.
Here is the driver, and I’ve dumped the CodeView data.
Some versions of UNARJD.DLD have CodeView debugging data (type NB09, probably using Visual C++ 1.0 from 1993 or so). Here is a sample.
The XALERT addin for 123 on SunOS4/SPARC (wtf!) was accidentally compiled with stabs enabled, which was very detailed.
Here is the lucky addin, and I’ve dumped the stabs.
There is an OS/2 “Tech Pack” which included some sample addins with source code. I have a copy here.
The REXXLINK addin for OS/2 included some of the LEP (Lotus External Programming) headers. I have a copy here.
The book “Lotus Add-in Toolkit Guide” by Robert Ainsbury, ISBN 0880225505.
PC Magazine Volume 10, Number 7 (April 16, 1991) included a (non-technical) overview of add-in capabilities in the story “Building The Perfect Spreadsheet”.
Mirror available Here
I do have the R9 (I think?) ADK, it’s of no interest to me but maybe someone else wants it.
I do not have these, but would really like to see them.
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.
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.
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
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.
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.
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.
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.
.
├── [ 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
The 123R4W ADK uses the Lci
symbol prefix, 123R4D uses Lpi
. There is a library that translates many functions.
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.
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 beforeGetDisplayInfo()
, 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);
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 BDLRECHDR
s.
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;
12];
PATT FillPatts[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; };
This function is called when you are being unloaded, you can use it to cleanup.
int __pascal DriverTerminate();
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();
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();
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. |
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. |
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.
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. |
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. |
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. |
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. |
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. |
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. |
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. |
The screen cursor should no longer be visible to the user, perhaps because they are navigating a menu.
void __pascal SetCursorInvisible();
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();
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);
This is called before entering graphics mode, and appears to be related to querying resolution data.
int __pascal QQCalledBeforeGraphicsMode(void far *m)
This appears to be related to setting graph legends in graphics mode.
int __pascal DrawString(int strarglen, void far *strarg);
This appears to be identical to MoveCursor()
.
void __pascal MoveCursor2(int col, int line);
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)
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).
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.
TODO
I don’t know much about the LINK API, other than what is written in Patent US54745836. I think the patent combined with the details here, you might be have enough details to write a driver.
The obvious project ideas are integrating with a modern SQL database or Yahoo Finance or something, but that’s not something I use very often…perhaps someone else is interested!
TODO
TODO
The OS/2 port had an official REXX extension API.
TODO