³E! APPLICATION PROGRAMMING INTERFACE³
³ PROGRAMMER'S GUIDE ³
³ version 5.0 ³
A . INTRODUCTION
The E! application programming interface allows you to write
programs that directly use E! functions or modify E! variables.
Since E! is an "in-memory-only" editor, it was a real concern to
provide the user with such an interface without using much memory
space. Moreover, it was important to find a way to program new
functions or E! "add-in" without requiring the programmer to learn
a new language such as the Brief macro language. Then I decided to
allow the programmer to write such programs with its own and usual
language whatever it could be (Pascal, C, etc..).
To achieve this goal I wrote a built-in interrupt handler thru
which the user may call E! functions, modify E! variables and
flags, access specific values FROM WITHIN an external program
executing while E! is present in memory. That is, from a program
loaded from the E! command line. This program will typically be
loaded using the "@" prefix so that it executes as a "blind"
command and that the DOS screen has not to be displayed.
With the E! API you can now write a program, say myaddin.exe,
that will be called from the E! command line (@myaddin) and that
can execute a E! command such as moving the cursor, marking and
manipulating blocks or modifying flags or variables.
This program could be a full-featured MENU that executes
functions without having to remember the required keystroke or a
program that performs a special job on the current text flow. You
can do almost anything you want using the E! API.
This approach has many advantages. First, it is not memory
consuming since the memory space used by the program is released
when the program finishes. Then, you can use any language to write
an API program. Nothing new to learn. You just have to use a
language that can issue software interrupts and load the cpu
registers with the appropriate values. Another advantage is that
the size of the E!.EXE file remains almost unchanged after adding
The main difference with previous E! versions is the way E!
manages memory space when it calls a child process. This makes no
difference for the user.
The main drawback of this approach is that API programs cannot
be loaded if too many files are currently edited.
B. THE E! API INTERRUPT
When E! is first loaded it traps interrupt C0h and redirects
it to an internal interrupt handler. This handler analyzes any
request addressed to it thru INT C0h and performs any action
requested. It then returns to the calling program.
Interrupt C0h is normally an unused interrupt vector. If there
is any conflict with existing hardware or software you may change
this interrupt number adding the following line in your active
where n is the interrupt number (decimal) you want to use
instead of C0h.
³ IF YOU ALREADY COMPILED API PROGRAMS THAT USE INTERRUPT C0h ³
³ OR ANY OTHER VALUE, THESE PROGRAMS WILL FAIL TO RUN WITH THIS ³
³ NEW INTERRUPT NUMBER (e.g. P!.EXE 1.0) UNLESS YOU PATCH THEM. ³
³ ANOTHER WAY TO HAVE THEM WORKING IN ANY CASE IS TO TEST THE ³
³ E!PRESENT ENVIRONMENT VARIABLE (SEE BELOW). ³
Please, use this feature only in case of very particular
problems or needs.
Be aware that calling INT C0h when E! is not present in memory
will likely cause a system crash. To detect E! from your API
program you just have to test the E!PRESENT environment variable
which must yield 192 if E! uses the default interrupt vector or any
other value if you changed it. Only E! child processes can "see"
this variable. So this is a secure way to make sure INT C0 has been
installed. If E! is not present (or no more present) this
environment variable does not exist (unless you create it).
The value of the E!PRESENT environment variable is changed
dynamically whenever a new PROFILE containing a new INTERRUPT command
is loaded. So, if your API program tests this value it will execute
E! API INTERRUPT SERVICES
You may call the E! API interrupt to perform 11 kinds of
tasks. The task E! API will execute depends on the value you put in
the AH register when you call INT C0h. If AH is not set to a right
value an error code (FFh) is returned in AL.
1/ Calling a E! function - AH = 0
Calling INT C0h with AH set to 0 will execute the E! function
which code is loaded in BX. This code will be the same function code
described in the E! documentation for macros. See also the API.PAS
unit for this description.
MOV AH, 0
MOV BX, 5
will clear the current line from the cursor to the end of the
So you can activate up to 108 basic E! functions.
Passing a function code > 32 or < 256 will be interpreted as a
normal character entry. Though, it is not an efficient way of
writing a string to E!. You may want to use the Editbuffer to do
Function code 27 (Escape) will be ignored and no error will be
The return code of the function is always returned in AL. This
value will be identical to the error codes described in the E!
documentation or 0 or 99 if the operation succeeded. See E!.DOC.
2/ Requesting internal addresses - AH = 1 to 7 and 20 to 22
The E! API allows you to access and modify internal E!
variables. Actually the E! API will return the address of variables
blocks which structure is described below for each call from AH = 1
to AH = 7 and from AH = 20 to AH = 22. The block address is always
returned in the ES:BX register pair. There is no return code and AL
is undefined on exit.
AH = 1 - Address of EditBuffer
On return ES:BX contains the address of the E! EditBuffer.
When you are editing a line in E! the actual text is not modified
until you leave the line (or the file) or execute a E! function.
This is why you can retrieve the previous content of the line when
you hit F9. Any modification made to the EditBuffer is stored to
the actual text when the cursor leaves the current line or when a
function is executed that needs to have the EditBuffer stored.
The E! Editbuffer is a 255 characters string. This is a Pascal
string. So the first byte contains the length of the string and the
string actually begins with the second byte. Space is reserved for
255 characters + 1 length byte.
Modifying the EditBuffer will result in no action on the
screen (see AH = 9). This will not modify the actual text unless
you leave the current line, call a E! function or call the special
"store edit line" service with AH = 12. Be aware that certain
functions acting only on the current line (hence on the editbuffer
only) will not store the editbuffer to the actual text.
Do not forget to update the EditBuffer length if you make
changes to the EditBuffer string without using Pascal strings
functions or procedures.
The reverse function (GetEditline) which stores the actual line
to the EditBuffer is always done automatically. So don't care.
AH = 2 - Address of flags block
On return ES:BX contains the address of the following block:
AddOnLast 1 byte Key down insert newline on last line flag
AlarmFlag 1 byte Error beep flag
AsciiMode 1 byte Control characters entry flag
AutoBackFlag 1 byte Backspace behaviour flag
AutoSaveFlag 1 byte Autosave flag
AutoScrollFlag 1 byte Autoscroll flag
AutoTabFlag 1 byte Autotab flag
BakFlag 1 byte .BAK files generation flag
BlanksFlag 1 byte Strip trailing spaces when saving flag
CompFlag 1 byte Compression mode flag
Ega_Vga_OK 1 byte 43 lines mode flag
Flag35 1 byte 35 lines mode flag - EGA_VGA_OK must be on
LogFlag 1 byte Log file flag
PauseMode 1 byte Pause on return from DOS flag
ShiftFlag 1 byte Accelerated cursor flag
Warning 1 byte Warning before saving flag
Enter_Classic 1 byte Split line using ENTER flag
RefreshFlag 1 byte Display authorization
SelfInsert 1 byte Braces autoinsertion flag
There is only two possible values for each of these flags: 1
if the flag is TRUE and 0 if it is FALSE.
These flags correspond to the equivalent commands described in
the E! documentation.
Changing Ega_Vga_Ok and Flag35 will not set the corresponding
display mode. These are only "authorization" flags. You have to use
Ctrl G (function code 7) to activate this modes.
AH = 3 - Address of display attributes block
On return ES:BX points to the following structure:
Text_Attr 1 byte Edited text attribute
BlockAttr 1 byte Marked block attribute
CommandLineAttr 1 byte Command line attribute
LoStatusLineAttr 1 byte Status line 1 attribute
HiStatusLineAttr 1 byte Status line 2 attribute
MessageAttr 1 byte Messages attribute
HelpAttr 1 byte Help line attribute
CursorAttr 1 byte Virtual cursor attribute
Please note the following:
Changing the value of Text_Attr will NOT change the color of
the edited text unless you call the ResetMask service described
below (AH = 11). This applies to Text_Attr only.
AH = 4 - Address of system variables block
On return ES:BX points to the following structure:
ShiftKeyCount 2 bytes Delay value for secondary help lines
AutoSaveLimit 2 bytes Seconds # for autosave
ScrollAmountGlb 2 bytes Left / Right scroll amount
DrawModel 2 bytes Drawing style
LeftMargin 2 bytes Left margin
ParMargin 2 bytes New paragraph margin
RightMargin 2 bytes Right margin
AH = 5 - Address of messages block
On return ES:BX points to the following structure:
HelpLine1 81 bytes 4 Pascal strings
HelpLine4 81 bytes "
ErrorMessage1 38 bytes 47 Pascal strings
(43 bytes for the french version)
ErrorMessage47 43 bytes "
Normally you will not need to access these message strings.
This service is only here for special needs.
AH = 6 - Address of "text flow"
On return ES:BX points to the "text flow" representing the
current text beeing edited. This pointer is actually the address of
an array of pointers. Each of these pointers points to a line of
text, that is, to a Pascal string representing a line of the text.
Array indexes are actual line numbers. So, the first element of
this array will point to the first line in the current text and so
ES:BX ÄÄÄÄÄÄÄÄÄ> Ptr1 (4 bytes) ÄÄÄÄÄÄÄÄÄÄÄÄ> Line #0
Ptr2 (4 bytes) ÄÄÄÄÄÄÄÄÄÄÄÄ> Line #1
Ptr3 (4 bytes) ÄÄÄÄÄÄÄÄÄÄÄÄ> Line #2
Ptr4 (4 bytes) ÄÄÄÄÄÄÄÄÄÄÄÄ> Line #3
Ptr5 (4 bytes) ÄÄÄÄÄÄÄÄÄÄÄÄ> Line #4
Line #0 is not a real text line. It is used by E! for internal
operations. So the first actual text line is line #1.
This array can be indexed up to the value returned in CX after
call #8. See AH = 8.
This call is made to READ the text lines NOT TO CHANGE them.
There is no extra space allocated for each string and any change to
the length of the string without reallocating the memory used by it
will likely result in a system crash when your program will return
To make changes to the text, access the lines with a E!
cursor positionning function or with function call number 14
(change active line). The EditBuffer will be loaded with the
requested line. Modify the EditBuffer and then store the new line
to the text using the "Store Edit Line " call.
E! is a complex machinery. Please respect these rules to keep
your memory and your files safe.
AH = 7 - Address of filename
On return ES:BX points to a Pascal string representing the
full name of the current file beeing edited.
AH = 20 - Address of locked extensions block
On return ES:BX points to an array of 25 Pascal strings
defining the locked extensions (max. length 3). The first 4 entries
are initialised with COM, EXE, BIN and OBJ.
AH = 21 - Address of compilation commands block
On return ES:BX points to a block of 10 elements. Each element
represents a filename extension and the associated compile command:
- extension is a Pascal string of max. length 3
- compile command is a Pascal string of max. length 128.
AH = 22 - Address of video buffer
This function has been changed. In the previous versions of E!
it returned the address of the path array defined with E!PATH.
Since E! now processes these paths directly from the environment
string, this function is no more supported.
Upon return of this call, ES:BX will contain a pointer to the
screen buffer used by E! to display data. This buffer may be either
B000:0000 or B800:0000 or the address of the screen buffer allocated
by DESQview (tm) to E!.
This allows you to display data on top of the E! screen without
disturbing the DESQview screen. Using this function you will not have
to test yourself on what kind of display E! is running.
Please see the DESQview section below.
3/ Requesting screen status - AH = 8
On return the cpu registers are loaded with some specific
values that are of great interest for the E! API programmer:
CX contains the total number of lines in the current text
DX contains the current line number (from 1 to CX)
AH contains the cursor position (from 1 to 255)
AL contains the number of the first displayed column
DI contains the number of the first displayed line
BH contains the amount of lines displayed on the screen
(25, 35, 43 or 50)
BL contains the number of opened windows
There is no return code for this function.
4/ Requesting special services - AH = 9 to 13
AH = 9
This call will cause E! to display the current editbuffer.
Modifying the editbuffer does not modify the corresponding line on
the screen. If you need to do so, you have to call this function. Be
aware that this function DOES NOT store the EditBuffer to the actual
text. See AH = 12.
AH = 10
This call will cause E! to rebuild the whole screen.
AH = 11 - Rebuild text mask
This call will reset the text mask using the new value of
Text_Attr. The text mask is a structure used by E! to display the
text when a window is refreshed.
AH = 12 - Store EditBuffer in actual text
This function will cause E! to store the content of the
EditBuffer to the actual text. PLEASE DO NOT try to do this yourself
using the value of the text flow pointer. The E! heap manager would
be fooled and an error would probably occur.
AH = 13 - Set Modify Flag
This function sets the current text "change flag" to TRUE.
Please use it if you have made any change to the current text.
³There is no return code for these functions.³
5/ Changing the active line - AH = 14
If the value passed in BX is a valid line number, this line
becomes the active line and also the first displayed line in the
active window. Likewise, the value passed in AL indicates the new
cursor position on the new line and also the new first displayed
column. This will avoid consistency problems between display and
cursor position. If either BX or AL contains 0 on entry, no action
is taken regarding the corresponding parameter. The other one is
processed normally. Otherwise AL will contain FFh on exit or 0 if
the operation succeeded. The Editbuffer is loaded with the content
of the new line. Its previous content is automatically stored to
the line that was active when this call was made.
This is the call you have to use if you want to directly
modify the current text.
6/ Requesting E! version - AH = 15
On return AH will contain the "major" digit of the E! version
and AL will contain the minor digit. i.e. for version 4.0 AH will
return 4 and AL will return 0. The second minor digit is ignored.
7/ Registering a E! command - AH = 16
As stated below it is not possible to execute E! commands
(commands entered on the E! command line) from an external program.
The main reason for that is that the command processor code is not
and CANNOT be reentrant.
However you may need to request a command execution. The E!
API offers a limited way of executing a command from an external
program. Your API program may "register" a command using call
number 16. When the API program EXITS, the command will be
executed. You may register the command anywhere in your API
program. You may register only one command. Though if you register
several commands, only the last one will be executed.
To register a command you have to pass the address of a Pascal
string representing the command exactly as if you entered it on the
E! command line, in the ES:BX register pair. Then call INT C0h with
AH set to 16. That's all.
Please note that this call allows you to easily chain API
programs. The registered command may be a call to another API program
and so on...
The P! program is an example of what can be achieved using this
call. This program allows you to choose a file from a pick list to
have it edited. The P! source file contains copyrighted material, so
it cannot be released. However it is easy to understand that once the
filename has been choosen from the pick list, it can be passed along
with the EDIT command using call number 16. When the API program
returns, the file will be loaded.
8/ Allocating memory in E! - AH = 17
Some API programs may need to hold important data from a call
to another (e.g. the P! program needs to hold the last user
selected path). You can naturally hold these informations in a file
but E! offers you a more graceful way of managing these data.
Likewise the new version of H! which uses the E! API will always
remember the last displayed help screen and display it again on
The "allocate memory" call allows you to dynamically allocate
memory on the E! heap from your API program. So your program may now
hold its own variables on the E! heap.
The requested PARAGRAPHS number (16 bytes) has to be passed
into CX. You have to choose an identification (a handle) for your
memory block (to retrieve it on a later call). This handle may
range between 1 and 65535. You have to pass it in DX.
On return, registers contain the following information:
AL = 0 call has succeeded
AL = FFh call has failed (no memory available or no more
blocks available - maximum = 10)
ES:BX allocated block address
AH = 1 this was the first call for the specified handle
(memory was allocated)
AH = 0 this handle already exists.
CX was ignored.
no memory was allocated.
ES:BX contains the block address for this instance
of your API program (see below).
This mechanism may seem a little clumsy but it is not:
- YOUR program has to decide which is the actual value of the
handle. This is the only way to retrieve your data from a call to
another. That is, this is the only way to make sure your program
uses the same handle when it executes several times in a row.
- At a given time, only 10 handles may be simultaneously
- E! permanently moves the heap data to hold dynamic variables
as low as possible on the heap. Thus, your API program has to call
this function EACH TIME it executes. The first time to allocate the
memory block and to get the block address, the subsequent times to
get the block address only. Anyway, your program has no way to hold
this information (except in a file).
- If you choosed a handle already initialized by another API
program, E! has no way to warn you from a confusion. It will behave
as if the call was not an initial call but a subsequent call.
However the probability for a "collision" between handles is very
1 / 65535 * api programs # in one E! session
You may use this function to share information between API
programs using the same handle or you may use it to simply store
permanent information for your single API program.
- if a block is already allocated for the handle you passed in
DX, CX is IGNORED and no memory is allocated. So you may use only one
function for both the first call and the subsequent calls. See
If the memory allocation fails, try to change the default value
passed to the HEAP command in the main PROFILE. See below.
9/ Freeing a memory block in E! - AH = 18
You may free an allocated block if it is unused and if no more
blocks are available. You just have to pass the block handle in DX
and call API function 18. The memory used by the block is released
and you may now call function 17 to allocate a new block. If the
handle passed in DX is not recognized, E! will return FFh in AL,
otherwise it will return 0 if the call succeeds.
You have not to worry about the memory block size when freeing a
block (like in the C language). E! keeps track of the block size and
will do the job for you.
You may also use this call to resize a memory block. It's up to
you to manage the data present in the block and to save it before
reallocating the block.
Please see the partial source file P!.PAS to have an example of
how to use calls 17 and 18.
10/ Calling E! API from TSR programs - AH = 19
Although E! API is designed to be called from external non
resident programs, you may want to use the E! API calls from a TSR
program. However you may not call any E! function from your TSR
program at any time. If E! is performing a DOS call or if it is
performing a non reentrant editing function when you interrupt it,
your program will not terminate safely.
This API function lets you know if E! was idle at the time
your TSR was popped up and if you may perform any other call to the
If E! was idle this call will return 1 into AL and 0 if not.
Anyway, E! will refuse to execute any function if it was not
idle at the time of the call. But it is safer to test this yourself
from your program. The call # 19 is the only call you can issue from
your program when E! is not idle.
11/ Getting file modification status - AH = 23
On return AL contains 0 if the file has not been modified or
if it was saved and 1 otherwise.
The REFRESH flag
By default, E! will automatically refresh the screen if
necessary after each function call (AH = 0). If you find this
somewhat tedious or if you want to spare time, you may disable the
screen refresh process by setting the REFRESH flag to FALSE before
the function calls are made and resetting it to TRUE after these
calls. You MUST reset this flag to TRUE before your program
terminates. If you fail to do so, E! will do it for you if your
program has been loaded with the "@" prefix. The REFRESH flag is
accessed thru the call number 2.
C. THE HEAP MANAGEMENT
You may wonder how E! manage the memory to allow you to use
the full system memory when no external program is active and still
allow E! functions to execute when the same external program is
loaded on the top of E! even if these functions make an extensive
use of the heap. This is one of the E! programming secrets. However
the user has to control the memory amount left between E! and the
If your program only changes variables values or call E!
functions that do not make use of the heap, you may leave this
value unchanged (it defaults to 256 paragraphs) or even set it to
0. Though there are very few E! functions that do not make use of
the heap or that do not call a subroutine that uses the heap
itself. So it will be almost always necessary to preserve some
space for the E! heap before calling the API program .
This is done using a command in the E! main profile:
n representing the number of PARAGRAPHS (16 bytes) you want to
preserve. The value you will use will highly depend on what your
program actually does and what E! functions it calls. The E! MEMORY
command will always show you the amount of free memory available
for your program to run. Substract from this value the memory space
needed by your program plus a security and you will have
approximately the value to pass to the HEAP command in your
The default value for n is 384 paragraphs which is the minimum
amount necessary for the API to work without any difficulty.
It is allowed to set the HEAP parameter to 0 if you don't need
the E! API.
This space is reserved whatever the command you used to make
the dos shell, that is, even if you did not use the "@" prefix.
If there not enough memory space reserved for the E! functions
your program calls, E! will behave exactly the same as if these
commands have been launched from the keyboard. So you will get an
error message and so on... If your program fails because it lacks
memory space, try a greater value for the HEAP command parameter.
YOU MAY NOT LAUNCH AN API PROGRAM USING THE # OPTION SINCE
THIS WILL CAUSE THE E! HEAP TO DISAPPEAR FROM MEMORY AND TO BE
SWAPPED TO DISK OR EMS. SO MANY FUNCTIONS CALLED BY THE API PROGRAM
WILL NO MORE EXECUTE PROPERLY AND YOUR SYSTEM WILL PROBABLY CRASH.
However, the E! interrupt handler is aware of the heap
swapping and will exit if you try to call it from a shelled program
running with the swap option on.
D. AN API EXAMPLE
The API.PAS and API.C files provided with the API
documentation are an example of how to interface the E! API with
your own programming language. Reading this commented code will
help you understand how the API works. The source of the LOADEXT
utility is also provided to help you undestand the E! API
E. USING EMS
E! version 5.x uses EMS (expanded memory) when it is installed.
E! automatically restores its own EMS context when entering the E!
API handler and restores the EMS context of your program on exit.
This way your program doesn't have to bother about EMS.
Nevertheless, if it uses EMS for itself, your program will have do
deallocate all the EMS pages it used before exiting.
For any EMS process error E! returns FEh in AL.
F. COMPATIBILITY WITH DESQview
If you are using E! under DESQview, your programs will have to
take care of the screen management. Please read the DESQview
documentation about DESQview "aware" programs" first.
Under DESQview, E! runs in a small window. In this case it
doesn't use the video adapter display buffer but a video buffer
allocated by DESQview. If your API programs were using a DESQview API
call to obtain a video buffer they wouldn't get the same buffer as
E!, naturally. So they couldn't work in conjunction with E! in most
To make things happen exactly as if E! and the API programs
were running under "native" DOS, those of your API programs making
direct write to the video buffer MUST use the same address as E!.
If you are using DOS or BIOS calls to write to the screen there
will be no problem, DESQview handles this automatically.
To get the address of the video buffer used by E! you just
have to use the API service #22 described above. Use the address
returned as you would use B800:0000 or B000:0000.
The API programs delivered with E! (H!, P!, C!, Z! etc...) use
this technique. Just observe how they work when E! is running in a
Moreover, your API programs should naturally follow the general
recommendations for any program running under DESQview.
Existing programs will be easily modified. At the beginning of
the program you will just add a call to API service #22 (after
having verified that E! is present) to know in which buffer you
will have to write. If this buffer is different from the hardware
buffer, you don't have to synchronize writing. DESQview will do it
for you. Using this method, you don't even have to test the video
hardware to configure your program.
Using the mouse in a E! API program raises some difficulties
under DESQview. This is due to a shortcoming of most mouse
Actually, when a program uses the mouse, it first tests
whether the mouse is installed. With a Microsoft (tm) compatible
mouse driver this is done by a call to INT 33h service 0.
Unfortunately this call also resets the mouse. Your program will
use the mouse without any problem but on return, the DESQview
window will have lost the mouse cursor. DESQview manages this
problem very well for the main program running in a window but
cannot manage it for programs called by this "first level" (main)
Generally, an API program called by E! under DESQview should
refrain from using the mouse. It shouldn't even try to detect if the
mouse is present.
If you want to write your own menu or complex macros or whatever
function you will find useful you have now a simple to use interface
to E!. It is highly recommended that programs written for the E! API
be called with the "@" prefix from the E! command line.
It is up to the API programmer to make sure that the E! screen
is not destroyed during program execution. E! does not refresh the
screen when returning from external programs launched using the @
You may wonder why there is no provision in the E! API to
execute a E! command exactly as if you typed it on the E! command
line. The answer is quite obvious : a E! command can also be a DOS
command. Imagine your API program is executing and you ask E! to
execute another child process...