Contents
DOS-Extender Swallow v1.0

Contents:

Agreement

Introduction

Features, Restrictions, Known Problems

How to Use

Run Time Error Messages

First Aid Kit

Internal Structure of Swallow

Description of the API

© 1995 Thomas Kurschel


Introduction
DOS-Extender Swallow v1.0

Who needs Swallow?

All those programmers debugging hours and hours their program because it writes like fury something somewhere into the memory by mistake and drives the system and themselves mad.

And what does Swallow do?

Swallow executes your programs in Protected Mode. That is the CPU keeps your program under control during its execution. And by the way it breaks down the 640 KByte border line by using the whole extended memory or simulating it.

But the big brothers like the extender of Borland Pascal/C can do it too!

That's right: but Swallow observes each memory block individually whereas the other extender collects multiple allocations to one block, so you can write over your neighbours within your block without being disturbed by the CPU. Even Borland does it in this same way. And not to forget: only the professional versions of a compiler include an extender.

What do I need to use Swallow?

You need Turbo Pascal 6 or 7 or Turbo C++, which are much cheaper then the professional Borland-brothers.

Yeah, and how much the extender is?

That's simply: nothing! But at this time I don't spread the source code, 'cause it was a hard (and undocumented) work. Before I forget: I would be happy getting a postcard of every country where my extender is used!

Address:

Thomas Kurschel

Lindenstraße 1

07747 Jena

GERMANY

e-mail:

topical@pdec01.mipool.uni-jena.de

Related topics:

Agreement

Features, Restrictions, Known Problems


Features, Restrictions, Known Problems

Features of Swallow:

-	executes your Turbo Pascal 6/7 or Turbo C++ 1.0 / Borland C++ 3.1 programs within Protected Mode

-	is easy to use

-	allows simple adapting of already written programs

-	provides virtual memory

-	shows you even small memory access errors for easier debugging

-	supports 32 bit assembler code

-	emulates all important DOS and BIOS functions (16 bit calls of 16 bit code and 32 bit calls of 32 bit code!)

-	is tested with Himem.Sys, Emm386 (Microsoft and Novell), QEmm, Windows 3.11, OS/2 Warp and Windows 95

Restrictions:

-	Swallow can handle only 32 MByte free main memory up to now

-	up to 64 MByte swap space are supported

-	C-specific huge points are not supported (Swallow was designed for Pascal and Assembler using only)

-	Swallow using programs cannot be debugged directly with a debugger (even if it is a Protected Mode debugger), so you have to use the built in Real Mode emulator of Swallow to debug these programs

-	the Real Mode emulator of Swallow does not support all functions of the Protected Mode original, e.g. you cannot use the Turbo Vision editor with the emulator, if you want to debug a program using it too, you have to remove Swallow from your program (exact description resides in the "First Aid Kit")

-	within a DOS shell of Swallow using programs you cannot start Windows

Known problems:

-	under Windows 3.11 there are sometimes problems to start a Swallow using program: it will be terminated immediately with an error messages (I'm trying to find a bugfix in the next future)

Related topics:

First Aid Kit


Agreement

If you use Swallow you have to accept following agreement between you and me (=Thomas Kurschel):

-	You can freely use this version of Swallow without registering or anything else.

-	If you are able to you can write own adapters to other high level languages, but please send me an email if you did or tried it (may be I try to support them too, e.g. an assembler adapter, but I won't support your adapter, and I'm free to change the enclosed adapters in further releases of Swallow).

-	If you distribute Swallow you must not change the contents of the package and you don't have to distribute Swallow for profit.

-	If you use Swallow by your own programs, please mention it at your info file or (if you use it in conjunction with a demo) within the greetings part or the copyright part of it.

-	Do not remove the copyright notice from any part (driver, source code, documentation or any one else) of Swallow.

-	Of course you can give Swallow your friends, use it in schools or software companies, burn it into CD's, give it the CIA or KGB or use the listings as toilet paper or whatever you want to.

-	You are free to use or adapt the examples for your own purposes, but don't change the original package if you distribute Swallow.

-	Any disassembling, decompiling, reverse or re-engineering of any executable or driver which source code is not enclosed by the package (e.g. "SegReg.Exe", "Swallow.Drv") is prohibited.

-	There is no warranty, any use by you or your software is at your own risk, Swallow is provided for use "as is" without warranty of any kind.

-	I am not obligated to provide any updates to Swallow, but if you find bug's, please send them me, I'll will try to fix them in a bugfix or by the next version of Swallow.

-	There is no liability for consequential damages. I will not be liable for any damages whatsoever (including, without limitation, incidental, direct, indirect special and consequential damages, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use or inability to use this product, even if I has been advised of the possibility of such damages.

-	If you distribute Swallow in violation of this agreement, you agree to indemnify, hold harmless and defend me from and against any claims or lawsuits, including attorney's fees that arise or result from the use or distribution of Swallow in violation of this agreement.

With your using you agree that this is the complete and exclusive statement of the Agreement between you and me which supersedes any proposal or prior agreement, oral or written, and any other communications between us regarding the subject of this Agreement. If any provision of this Agreement is found unenforceable, it will not affect the validity of this Agreement, which shall remain valid and enforceable according to its terms.

Any problems regarding Swallow (and postcards of happy using) can be send to

Thomas Kurschel

Lindenstraße 1

07747 Jena

GERMANY

or mailto:

topical@pdec01.mipool.uni-jena.de

© 1995 Thomas Kurschel, Topical Software


How to use

Swallow must be linked to your program, and so it's (theoretically) possible to use it with nearly every compiler.

Following compilers are supported up to now:

Turbo Pascal 6.0/7.0

Borland Pascal 7.0

Turbo C++ 1.0

Borland C++ 3.1

But to adapt the connection module of Swallow to other languages shouldn't be so difficult.

Related topics:

Internal Structure of Swallow


How to use with Borland/Turbo Pascal 6/7
How to use with Borland/Turbo Pascal 6.0/7.0

The Pascal-compilers of Borland create completely Protected Mode-compatible code, even those of TP 6.0. There will only be problems with assembler modules or own memory managers. The most important drop of bitterness is the unit Graph: it disregards the rules of Protected Mode so heavily that I didn't found a simply and fast way to let it fit. Overlays aren't supported by the Pascal-adapter because you normally have enough memory within Protected Mode, and in case of need you can let simulate virtual memory by the Pager additionally. The HLL(High Level Language)-adapter is saved as Swallow.Pas.

To use Swallow in your program you have to do the following steps:

-	link (by "uses Swallow") the unit Swallow into your primary file as the first one, even before the CRT-unit and all the other standard units

-	if you want to use Turbo Vision, link "SwaTV1", for Turbo Vision 1, or "SwaTV2", for Turbo Vision 2, as the second file into your primary file, directly after "Swallow"

-	instruct the linker to create a simple map file (within the IDE at Options|Linker... Map File: Segments)

-	compile your primary file

-	call the enclosed convert program SEGREG after each linking; as the only one parameter it expects the name of your program

Your program is now ready to run. Make sure the files "Programname.Seg" and "Swallow.Drv" are in the directory of the exe-file at run time. The latter is searched within the "PATH"-directories too.

!!! Attention !!!

Call the program SEGREG after each linking. Otherwise your program will be loaded into extended memory erroneously!

Related topics:

Problems adapting a Program

First Aid Kit

Run Time Error Messages

Loading Process of Your Program

DOS/BIOS-Emulator


How to use with Turbo C++ 1.0/ Borland C++ 3.1

The C++-compilers of program create nearly Protected Mode-compatible code. The DOS/BIOS-Emulator takes care that incompatible commands work under Swallow too. But if you include assembler code and use system-hacking libraries, like special Critical Error-handlers you have to expect problems. The most important drop of bitterness is the unit Graph: it disregards the rules of Protected Mode so heavily that I didn't found a simply and fast way to let it fit. Overlays aren't supported by the Pascal-adapter because you normally have enough memory within Protected Mode and in case of need you can let simulate virtual memory by the Pager additionally. The HLL-adapter is saved as c0?.asm and replaces the original c0?.obj. These are linked automatically and have to start the program even before executing the function main (the names are c0l.obj for memory model large, c0m.obj for medium and so on). The functions provided by this unit are exported via Swallow.H. If you want to debug the program you have to link Swallow.C additionally.

To use Swallow in your program following steps must be done:

-	instruct the linker to search for object files first in the directory where Swallow's c0?.obj-files are stored. Else you don't get an error message, because the standard c0?.obj are linked, but your program will still run in real mode. You don't need to add a c0?.obj-file to the project.

-	instruct the linker to create a simple map-file (within the IDE you find it under Options|Linker... Map File: Segments)

-	link your program

-	call the enclosed convert program SEGREG after each linking; as the only one parameter it expects the name of your program

Your program is now ready to run. Make sure the files "Programname.Seg" and "Swallow.Drv" are in the directory of the exe-file at run time. The latter is searched within the "PATH"-directories too.

!!! Attention !!!

Call the program SEGREG after each linking. Otherwise your program will be loaded into extended memory erroneously!

Related topics:

Problems adapting a Program

First Aid Kit

Run Time Error Messages

Loading Process of Your Program

DOS/BIOS-Emulator


Problems adapting a Program

Following parts of your program must be checked or replaced:

-	The memory management: Swallow exchanges the memory management of the run time library with an own by patching. In this way all standard procedures/functions are executed correctly. Special commands of the C-memory management aren't emulated (for instance heap-checks) and have not to be called. Own memory managers work incorrectly under Protected Mode with guarantee. So if your program uses an own you have to replace it by Swallow-calls.

-	Assembler-Code: Assembler code normally is Protected Mode-incompatible, because many programmers write directly into the code segments or even stores their variables there. This kinds of errors will be visible at least at runtime by getting a "General Protection Fault". With this knowledge you have to revise your assembler parts.

-	Access to system data: Many programs directly access system data, like the BIOS segment or the video memory. This is forbidden in Protected Mode. A loophole is the function Seg2Selector: it expects a real mode segment as the parameter and returns an adequate selector. This selector can or better: have not to be freed, because the same selector will be returned by multiple calls with the same segment. There are 4 selectors of the most important segments defined in the HLL-adapters: SEG0040, SegA000, SegB000 and SegB800.

-	Overlays: They aren't supported by the HLL-adapters, because memory shortage shouldn't exist in Protected Mode. All extended memory can be used and you can add virtual memory too.

-	Unit Graph: This unit cannot be used under Swallow. The old one of TP 6.0 is to incompatible with Protected Mode and I hadn't enough time to adapt the new one of TP 7.0 up to now. (Borland knew, why they created a new BGI-format)

-	Turbo Vision: There are patches for Turbo Vision 1 and 2 for Turbo Pascal: they must be linked into your primary program by uses, directly after "Swallow", i.e. "uses Swallow, SwaTV1" for Turbo Vision 1 and "uses Swallow, SwaTV2" for Turbo Vision 2. Up to now there is no feature to use Turbo Vision with Turbo/Borland C++

-	Use of unsupported DOS/BIOS functions: though the DOS/BIOS-Emulator simulates most of the functions, there are some seldom and undocumented it don't support. In this case the program will be stopped at run time with the message: "Unsupported Interrupt xx".

Related topics:

First Aid Kit

Run Time Error Messages

How to use Swallow

DOS/BIOS-Emulator


Internal Structure of Swallow

If you are interested in the secret how an extender works, you should take a look at this article. But don't get frightened: you only have to know how to insert Swallow into your program, and that's poor.

The extender, stored in Swallow.Drv, consists of 6 different modules:

Initialisation Module

Kernel

Low-Level Memory Manager (Pager)

High-Level Memory Manager (HighMem)

DOS- and BIOS-Emulator

Real Mode-Emulator (RealKrnl)

Related topic:

Diagram of Swallow


Diagram of Swallow

Simply click onto the interesting things in the draft.

Related topic:

Internal Structure of Swallow


Initialisation Module

This part of the extender has to look for memory and contacts with other programs. As memory will be used:

Raw extended memory

XMS

EMS

VCPI Memory

DPMI Memory

Optionally there can be added DOS memory and virtual memory after initialisation, but these are things the Pager will have to bother with, which will be born later. Contacts have to be created with Himem.Sys, Emm386.Exe and other VCPI- or DPMI-servers. On this base you can distinguish the following kinds of contact:

Stand alone - Mode

Co-operation with a XMS Manager

Co-operation with an EMS Manager

Subjection to a DPMI-Server

After allocating the memory and creating necessary contacts a buffer within DOS memory will be allocated to copy in the needed code and data. Now you switch into Protected Mode. Hopefully arrived, at first the Pager will be made functionary and extended memory for Swallow will be allocated by its help. When the extender is copied into this new location, the HighMem is initialised and the DOS/BIOS-Emulator is registered, we can switch back to real mode and release all DOS memory occupied by Swallow except 10 KByte consisting of V86-Kernel and DOS/BIOS-Emulator-buffer. The copying of the EXE-file into the extended memory and the starting of it there will be done by the DOS/BIOS-Emulator. A little bottle neck is the start-up initialisation within the DOS memory, because we need additionally to the loaded EXE-file about 150 KByte DOS memory, 50 KByte for the file Swallow.Drv and 100 KByte for the ready to switch Swallow, temporary. Altogether the Initialisation Module is one of the most complex parts of Swallow and occupies a, let's say: not unimportant, part of the code.

Related topic:

Internal Structure of Swallow


Raw Extended Memory

That's the paraphrase, that the extended memory isn't observed by a manager, like Himem.Sys or Emm386.Exe would do (you can say: it exists only if your Config.Sys is nearly empty).


XMS
XMS (simply called: extended memory)

This is the extended memory managed with regard to the XMS (eXtended Memory Specification). Examples for managers are Himem.Sys or QEmm386.


EMS
EMS (called: expanded memory)

This is the extended memory managed with regard to the LIM-EMS (Lotus-Intel-Microsoft Expanded Memory Specification). Programs like Emm386.Exe or QEmm386 offers it. There are hardware and software solutions for this kind of memory, but Swallow only supports the emulation by software. By the way: you can find hardware solutions only at AT-286 computers today, so you can forget it.


VCPI Memory

This is the memory offered by an VCPI-Server (members of this family are all modern EMS-managers, like Emm386.Exe and Co.)


VCPI
VCPI (Virtual Control Program Interface)

That's an interface between Protected Mode-programs, whereby one is the server, offering all the VCPI-functions, and all the others are the clients, using these functions. The specification exactly defines the functions and the data structures needed. VCPI-Server are for example Emm386.Exe or QEmm386.

Related topic: DPMI


DPMI
DPMI (DOS Protected Mode Interface)

That's an interface between Protected Mode-programs. One of them is the server, offering all DPMI-functions, and all the other ones are the clients, using these functions. The specification defines all structures and functions. Well known DPMI-servers are Novel's Emm386.Exe, Quarter-deck's QDPMI, Windows 3.x, Windows 95 or OS/2.

Related topic: VCPI


DPMI Memory

That's the memory a DPMI-server offers, like Windows or OS/2.


Stand alone - Mode

If no one manages the extended memory and nobody lay claim to owning the Protected Mode, Swallow has to care for the management of the memory and the switching into Protected Mode and visa versa. The disadvantage of this mode is the touch of the extender at the hardware, what can get problematical.

Related topic:

Initialisation Module


Co-operation with a XMS Manager
Co-operation with a XMS Manager, like Himem.Sys

This programs help Swallow to avoid bothering with search for extended memory or the switching of the A20-gate. This lovely gate permits or prohibits the access to the extended memory and has to be enabled in Protected Mode. This work has to be done in the Stand alone-Mode by Swallow itself, and it's possible that Swallow has trouble with exotic motherboards. In this case the XMS-manager comes at the right moment, so this Mode is preferable.

Related topic:

Initialisation Module


Co-operation with an EMS Manager = VCPI-Server
Co-operation with an EMS Manager, like Emm386.Exe

Correctly spoken they are not EMS-managers than -simulators today. This is only possible by switching the CPU into V86-Mode, that is the CPU works in Protected Mode, and DOS and all the other Real Mode-programs are deceived by the help of the V86-Mode to think they are in Real Mode. That's why Swallow cannot simply switch into Protected Mode, because the CPU already is in, but it can share it with the EMS-emulator. That is either the emulator is active or Swallow is, but they can't be active simultaneously. This system is defined exactly by the VCPI-specification. The emulator (e.g. Emm386.Exe) is the VCPI-Server and the user (Swallow) is the VCPI-client. VCPI is simply to support and to use and, compared to the other co-operation modes, the safest one. And last but not least: nearly every computer I know work with Emm386.Exe and so this mode is the most often used.

Related topic:

Initialisation Module


Co-operation with a DPMI-Server
Subjection to a DPMI-Server, like Windows is

Looked at the thoughts of the inventors of DPMI, it should replace VCPI and be supported by the Operation System itself. The idea of the DPMI-interface is, that the DPMI-server, e.g. Windows, observes the Protected Mode alone and doesn't think about sharing it like a VCPI-Server. Instead the server offers all important functions needed to run a program in Protected Mode. The advantage is: the server lays his protecting hand on the Protected Mode-program (and of course on itself) and help to avoid trouble between multiple programs. But by the way you can ask: does the server offer enough functions and is the system as save as it should? That's the point: the interface is only better then nothing, most of the functions you need you have to do yourself, and so it makes programming in Protected Mode not simpler then more complicated. Nevertheless the first DPMI-servers implemented the interface like they thought it should be and not like it was defined, and because many programs relies on this special ...eh... features, up to now version 0.9 is standard and the newest extenders I know (the ones of OS/2 Warp and Window 95) support only 0.95, so many functions defined in the standard are missed up to now. Additionally you need for DMA, e.g. for playing music, a special interface, the VDS. This part is forgotten by the most DPMI-servers too, so there's no way for music output under Windows 3.x. Altogether you see the DPMI is more a defined bug than a standard. And if you don't believe me, look at the QDPMI: it disables itself, if a professional extender, like the one of Rational ©, is loaded. Because these facts Swallow only uses DPMI, if there is no other way.

Related topic:

Initialisation Module


VDS
VDS (Virtual DMA Specification)

It is an interface, supported by VCPI- or DPMI-servers, which enables Protected Mode programs to do DMA-access in Protected Mode. Unfortunately many DPMI-servers don't support VDS or does it only partially.


Kernel

Easy to see it is the main module of Swallow. It manages the system tables of Protected Mode and controls the switching between the privilege levels within Protected Mode and between Protected Mode and Real/V86-Mode. Because it is unimportant for the running Swallow to distinguish of Real Mode and V86-Mode, only the Initialisation Module have to bother with it, I say in the whole document only V86-Mode and mean both, because there is a completely independent Real Mode-Kernel too you shouldn't confuse.

The CPU runs in 3 different modes under Swallow:

V86-Mode

Supervisor-Mode

User-Mode

Therefore the Kernel is split into 3 parts:

V86-Kernel

Kernel 0

Kernel 3

A little exception is the co-operation with a DPMI-Server: in this case the DPMI-server runs in the supervisor-mode and so the whole system of Swallow in user-mode. This is the security deficiency: the Kernel 0 isn't protected against an amok-running program. But fortunately the chance to destroy Swallow in this way is very small (and normally the DPMI-server stops the program in this case).

The tasks of the Kernel are:

Exception Handling

Hardware Interrupt Handling

Software Interrupt Handling

Descriptor Table Management

Calling V86-Mode Code

Callback Management

Related topics:

Internal Structure of Swallow

Initialisation Module

Subjection to a DPMI-server


V86-Mode

The Real Mode is the standard mode of the Intel-CPU's. The convention Real Mode was declared when the alternative, called Protected Mode, was introduced. Modern CPU's simulate the old mode within Protected Mode, this emulation is called V86-Mode. For a programmer Real Mode and V86-Mode are adequate and so they aren't distinguished in this document. Swallow needs this mode to communicate with other programs, like the Operation System. Typically it will be hidden called by DOS/BIOS-Emulator.


Supervisor-Mode

There are 4 privilege levels within Protected Mode. The Supervisor-Mode is privilege level 0 (alternatively called: ring 0) and the highest of all levels. Program modules running at Supervisor-Mode can access all data in the system, they have all rights. Program modules at a lower level are forbidden by the CPU to access supervisor-programs or data.

Only program modules of Swallow run in Supervisor-Mode.

Related topic:

User-Mode


User-Mode

There are 4 privilege levels within Protected Mode. The User-Mode is privilege level 3 (called: ring 3), the lowest one. Program modules of this level must not access data or direct call code of an higher level. Only by the help of Call Gate's a program at User-Mode can call higher privileged code at defined entries.

All user programs run in this mode.

Related topic:

Supervisor-Mode


Call Gate

It's an exactly defined interface between multiple privilege levels. Only by there help you can call higher privileged program modules.

Related topics:

Supervisor-Mode

User-Mode


Callback Functions

It is impossible to call programs running in Protected Mode directly from the V86-Mode. But if it is impossible to avoid, e.g. if DOS want to report a Critical Error or the pressing of Ctrl-Break, you can declare it by the help of Swallow as a so called callback function. Swallow returns a V86-Mode address any V86-program can call and redirects any call to it into Protected Mode to your callback function.

Related topic:

Internal Callback Management


V86-Kernel

This part of the Kernel of Swallow runs in V86-Mode. Code and data of this module resides in DOS memory, that is: in the first MByte. Whenever your program or Swallow itself calls a V86-Mode program, the Kernel 0 switches into V86-Mode and hands over the control to the V86-Kernel. This module calls the desired program, e.g. a DOS function, and switches after completion back to Protected Mode to Kernel 0. Moreover this module is the entry point for V86-Mode programs calling callback functions  of your program or Swallow itself.

Related topics:

Kernel

Kernel 0

V86-Mode

Callback Functions


Kernel 0

The central administrator in Protected Mode is Kernel 0. Because it runs in Supervisor-Mode, it has direct access to all system tables and takes care for its management. Moreover only Kernel 0 is able to switch to V86-Mode and that's why it offers all the functions to call V86-Mode programs and to allocate and execute callbacks. If Swallow has to live with a DPMI-server, this one solves above-mentioned tasks itself and does not allow Swallow to execute modules at Supervisor-Mode. That's why Kernel 0 have to run in User-Mode and redirect most of the function calls to the DPMI-server.

Related topics:

Supervisor-Mode

Kernel

V86-Kernel

Kernel 3


Kernel 3

This module of Swallow is the mediator between your user program and Kernel 0. If Kernel 0 calls your program, for example a callback, your program can't return to Kernel0 directly, because it is running in User-Mode but Kernel 0 is running in supervisor-mode. In this case your callback gets as the return address one in Kernel 3. From there the CPU jumps through a call gate back to Kernel0, where it can continue its works. The second task of Kernel 3 is to create and show error messages. All exceptions and unused software interrupts are handed over to Kernel 3, which aborts the program with an error message.

Related topics:

Supervisor-Mode

User-Mode

Kernel

Kernel 0


Internal Exception Handling

All kinds of exceptions leads to a call of Kernel 0. At first it tries to find the reason and solve it itself. If it can't, it chains the exception to a user exception handler. If such a handler doesn't exists or cannot find a solution too and so calls the original handler, which resides in Kernel 3, the program will be aborted with an error message. To avoid a stack overflow during an exception handling, every handler gets its own stack of 512 byte.

Possible applications of exceptions are the adaptation of Protected Mode-incompatible code by the DOS/BIOS-Emulator and the simulation of virtual memory.

Related topics:

Kernel

DOS/BIOS-Emulator

Exceptions


Internal Hardware Interrupt Handling
Hardware Interrupt (=IRQ) Handling

To describe, how hardware interrupts are handled, you must take a look at the moment, when the interrupt was released:

At first let's take the case the CPU is in Swallow-based Protected Mode, for instance executing your program. The CPU directly jumps into Kernel 0. This module calls at first an own handler (e.g. to recognise the pressing of the Break-key combination) and then hands over the control to a user IRQ-handler. If it finished its work the CPU returns via Kernel 3 back to Kernel 0, and your program can be continued. If the originally IRQ handler is called, e.g. it's needed for timer and keyboard IRQ, so it points to Kernel 3. From there the CPU jumps via a call gate to Kernel 0, which has the task to call the original V86-Mode handler, like the keyboard driver is. To make this the CPU will be switched to V86-Mode and the handler is finally called by the V86-Kernel. And when the original V86-handler has its work done, it returns to the V86-Kernel, which helps the CPU to get, after a Protected Mode switch, via the Kernel 0 and Kernel 3 back to the calling user IRQ handler.

And now the case, that CPU is in V86-Mode, e.g. during a DOS call: Because Swallow normally can't reach the IRQ's in V86-Mode, it has to install a V86-handler like all other programs too. If an IRQ occurs now the V86-Kernel jumps via a callback to Kernel 0. Now the above-mentioned procedure starts and after finishing the CPU returns from Kernel 0 to the V86-Kernel, which can continue the interrupted program.

Related topics:

Hardware Interrupts

Kernel


Internal Software Interrupt Handling
Software Interrupt (=int) Handling

Generally Swallow only proceeds software interrupts called in Protected Mode. At first the CPU jumps to Kernel 0. This module creates a 16-Bit stack frame and calls the user handler. If no one exists or, if one exists, it calls the original handler, the program will be aborted by Kernel 3 with the message "unknown interrupt".

The reason, why Swallow doesn't call the V86-handler of the interrupt directly, is the problem of memory addressing: if you call a function expecting a memory block as a parameter, in Protected mode this block is normally in extended memory. But no V86-program can access it directly. That's why the block must be copied into a buffer within the first MByte and the V86-handler must be given the address of the (accessible) buffer. After finishing the handler the buffer normally have to be copied back. But because it differs from function to function, every function needs an own preparation.

In most cases the DOS/BIOS-Emulator worries about this problem by installing own handlers. But if you use an unknown interrupt or an unknown DOS function you have to take care yourself. In contrast to other extenders Swallow does not call the V86-handlers of unknown functions without adaptation but aborts the program.

Related topics:

Kernel

DOS/BIOS-Emulator


Descriptor Table Management

Within Protected Mode there are three descriptor tables: GDT, LDT and IDT.

The first one is used only by Swallow itself and isn't managed officially.

The LDT contains the addresses, limits and access rights of all memory blocks of your program and enjoys public interest. Kernel 0 takes care for distributing the entries of the LDT, the so called local descriptors and does all changes on it. It doesn't worries about sense or senselessness of these descriptors, but it saves code and data of Swallow and denies any try of your program to switch itself into supervisor mode. Kernel 0 also takes care that freed entries of the LDT are invalidated and won't be used again as long as possible. In this way it can warrant, that you cannot access memory after you freed it. At system start the LDT offers place for 512 entries, but on demand it can be expanded up to the physical maximum of 8192 entries. Please take a look at this limit if you want to allocate many small blocks in your program. Altogether you have to use the management functions of LDT only in exceptional cases, normally HighMem does it for you automatically.

The last table is the IDT: here the handler addresses of hardware and software interrupts and exceptions are stored in. Any changes are done by Kernel 0, which refuses invalid addresses (if it can recognise them), because the IDT is kind of central protection executor in Protected Mode. As an alternative of calling the management functions of Swallow directly you can register and remove interrupt handlers by the use of DOS functions too. In this case the DOS/BIOS-Emulator calls the adequate Swallow function.

If Swallow works under a DPMI-server, the server manages the descriptor tables itself, and Swallow only redirects the management calls to it.

Related topics:

Kernel

DOS/BIOS-Emulator

HighMem


GDT
GDT (Global Descriptor Table)

It's the table, where system-wide accessible code, data, call gates and other, for you unimportant, system structures are registered.

This table is reserved for entries of the operating system and is used for data exchange between multiple tasks in a multitasking environment. The indexes of the GDT are called global selectors.

Related topics:

LDT

Selector


LDT
LDT (Local Descriptor Table)

All code and data of a task are registered in this table. Every tasks owns a LDT in order to deny access of other tasks. Because Swallow isn't designed for multitasking there is only one LDT. The entries of the LDT are the local descriptors. The index of an entry is called local selector. Kernel 0 of Swallow is the manager of the LDT.

Related topics:

GDT

Descriptor Table Management

Selector


IDT
IDT (Interrupt Descriptor Table)

On the contrary to the GDT and LDT the IDT doesn't manages memory blocks. Instead it stores the addresses of all hardware/software interrupt and exception handlers. Kernel 0 of Swallow manages this table.

Related topic:

Descriptor Table Management


Selector

It's the Protected Mode equivalent to the segment part of a Real/V86-Mode address. But whereas you can calculate the base address of memory block by knowing only the segment part in real mode (that's why you can access only one MByte in this mode), the selector of an address in Protected Mode is only an index of a descriptor of the GDT or LDT. The information a selector directly contains are the index of the descriptor, the table (GDT or LDT) it belongs to and the privilege level of the memory block.

Related topics:

GDT

LDT

System Tables

Descriptor

User-Mode


Descriptor

It's an entry of 8 bytes within the GDT, LDT or IDT. It contains the description of a memory block (that is its base address, the limit and the access rights), of a call gate or of other system data. Whereas the descriptors are reserved under Swallow by itself, the ones of the LDT are available for every one. The CPU distinguish between 8192 descriptors per table. To access a descriptor you have to use the appropriate selector as the table index.

Related topics:

GDT

LDT

System Tables

Selector


Calling V86-Mode Code

Even when you program is executed in Protected Mode, it's necessary to use V86-programs, like functions of the BIOS or DOS. The appropriate functions are offered by Kernel 0. It switches into V86-Mode, and lets the V86-Kernel call the V86-mode function. After finishing it returns back to Kernel 0, which hands over the control to the calling program. If Swallow uses a DPMI-server the switching and executing is done by the DPMI-server itself, so Swallow has only to chain the functions to this server.

Normally the DOS/BIOS-Emulator keeps care, when you have to switch to V86-Mode and when not.

Related topics:

Kernel

Software Interrupts

DOS/BIOS-Emulator

knf_CallV86Int

knf_CallV86Proc

knf_CallV86IRet


Management of Callbacks
Internal Management of Callbacks

Callbacks are the opportunity of calling V86-Mode programs from Protected Mode code. By the help of callbacks your program is enabled to let call Protected Mode functions from V86-Mode, e.g. if the mouse driver has to call your event handler in case of changes of the mouse state.

For that purpose you register your function at Swallow to get an entry address of V86-Mode. If a V86-Mode program calls this entry the V86-Kernel intercepts this and switches into Protected Mode. From there the Kernel 0 calls your function, which after proceeding returns via Kernel 3 to Kernel 0. This one switches back to V86-mode, where the V86-Kernel returns to the calling program.

Related topics:

Kernel

Callbacks


Low-Level Memory Manager
Low-Level Memory Manager (Pager)

The low-level memory manager, called Pager, has to keep care of the right translation of linear to physical addresses. By its help you even can allocate and free (linear) addresses and physical memory separately, which is summarised as Linear Memory. Thereby the Pager is able to simulate virtual memory. Therefore all memory management has to pass the Pager. You will probably never have to take a look at the work of the Pager. Instead the HighMem is offering a comfortable interface for you.

Please take notice of the following, if you call any functions of the pager

-	The Pager can only handle blocks of 4 KByte due to the design of the MMU

-	Physical memory will never be fragmented by the Pager

-	The resizing of linear memory blocks are very fast due to remapping

-	You can release each unused 4 KByte separately and assign physical memory to it at any moment (if there is memory left), take a look at knf_CommitLinearMem and knf_UncommitLinearMem

-	You can let memory areas (in blocks of 4 KByte) discard, if you don't need the contents of it any more but wish to have access without any extra registration in the future. Such blocks aren't swapped out to hard disk, they simply will be "forgotten"; look at knf_DiscardLinearMem

If there is a DPMI-Server, all functions of the Pager will be done by it itself. Unfortunately the functions of DPMI aren't so powerful compared to the Pager. For this reason some functions will simply be ignored.

Related topics:

MMU

Linear Address Space

Physical Address Space

Virtual Memory

API: Linear Memory Management

HighMem

Internal Structure of Swallow


MMU
MMU (Memory Management Unit)

It's built in the CPU since the 80386. It translates so called linear addresses into physical ones. For that purpose the whole memory is split into equal-sized blocks, called pages, á 4 KByte. Every linear address resides in exactly one page. Every page of the linear address space can be assigned any physical memory block. The unique assignment is determined by so called page tables and hidden done by the MMU before each memory access. Consequently there is a strict difference between linear addresses and physical memory. Therefore every page can be assigned special access limitations, e.g. to forbid reading or writing, or you can deny the access of programs running at user-mode.

Related topics:

Pager

Virtual Memory

Page tables


Page Table
Page Tables

The physical addresses of 1024 pages, that is 4 MByte linear addresses, are saved in one page table of 4 KByte. At system start there is only one page table, or 4 MByte of addresses, but on demand there will be created up to 64 page tables, i.e. 256 MByte address space, additionally.

Related topics:

MMU

Linear Address Space

Pager


Page

It's the granularity of memory on the part of the MMU. One page has a size of 4 KByte and the base address is a multiple of 4 KByte.

Related topic:

MMU


Virtual Memory

Swallow simulates it by the MMU. For that purpose unused pages are swapped out to hard disk and are reported to the MMU as non-existent. If a program accesses such a page the MMU causes an exception, so the memory manager swaps this page in and tell its address the MMU. As transparent as the MMU the work of the memory manager is. Under Swallow this work is done by the pager.

Related topics:

MMU

Pager

LRU


LRU
LRU (Least Recently Used)

It's the system Swallow decides which pages should be swapped out by. That is: those pages are swapped out, which aren't accessed for the longest time. And the background? Most programs often use only a small part of the memory, so you can say that pages not used for a long time won't be accessed within the near future. But if your program turns upside down the whole memory, you can remark the non-functioning LRU system by a much higher hard disk access rate.

Related topics:

MMU

Virtual Memory


Linear Address

This is the address created by the program. Without the MMU, like in Real Mode or with an 80286, it is the same as the physical address of the accessed memory. But if the MMU is enabled, it translates the linear address in a, often completely different, physical address before every access.

Related topics:

MMU

Virtual Memory

Pager


Physical Address

This is the address of the specific memory unit, i.e. the memory chip, which is accessed by the hardware.

Related topics:

MMU

Linear Address


Linear Address Space

This is the memory on the part of the user. It is normally bigger then the physical address space. The translation from linear to physical address space is determinated by the page tables and executed by the MMU, through which parts of the linear address space can be non-existent, or better: temporary non-accessible. On this base virtual memory exists. Under Swallow the size of the linear address space is 4 MByte at the beginning, but it can grow up to 256 MByte on demand.

Related topics:

MMU

Physical Address Space

Page Tables

Pager


Physical Address Space

This is the address space on part of the hardware. You can say, it's the real existing memory. The task of the memory manager is to enable the user program to access the physical memory via linear addresses.

Related topics:

MMU

Linear Address Space

Pager


Fragmentation

This is the splitting of the free memory into small pieces. It's a consequence of allocating and freeing memory blocks. As the result it may be an allocation fails even if there is enough memory left, because this memory is split, fragmented. Only the pager is able to manage the physical memory without fragmentation by the help of the MMU. But it cannot do this trick with the management of the linear addresses, but it's not a problem with up to 256 MByte of linear address space.

Related topics:

Pager

MMU

Linear Address Space

Garbage Collection


Remapping
Expanding of Memory Blocks by Remapping

If you expand a memory block it can happen the new block doesn't fit into its old location of the linear address space, because there is another block in his direct neighbourhood after him. In this case there must be searched a new address. Normal memory managers have to copy the content of the block into its new location byte by byte. This costs time and there must be enough room even for the old and the new block. By using the MMU the Pager has only to search a new linear address in this case, but it copies only the physical addresses to the new linear address. So you need only memory for the new addresses and the copying is speeded up by one thousand. All of the internal tables of Swallow and the system tables too are expanded by demand in such a way.

Related topics:

MMU

Pager

Linear Address Space


High-Level Memory Manager
High-Level Memory Manager (HighMem)

Whereas the memory management by the Pager is very near the hardware and powerful it isn't like the way programmers know and use. The most important problem is the peculiarity of the pager managing memory in steps of 4 KByte, which is a consequence of using the MMU.

HighMem offers two kinds of memory:

Watched Memory

Unwatched Memory

The whole memory offered by HighMem is indirectly managed by the Pager. Therefore HighMem allocates huge linear memory blocks (>512 KByte) and distributes them by splitting into pieces of watched memory. The physical memory of unused areas of the linear memory blocks is returned to the pager, which can offer it without fragmentation. Last one is only possible if the unused areas occupies a whole page, i.e. they must be larger then 4 KByte. Else, and that's the problem, these blocks are assigned physical memory and so this one is fragmented. So the Pager has the problem of the large granularity and the HighMem has the one of fragmentation. But because the advantages of the Pager are extremely important if you have excessive memory using, modern operating systems, like OS/2, mainly use systems equal to the Pager and only sometimes ones like HighMem, even you have to work with whole 4 KByte blocks.

All unwatched memory blocks reside in one linear memory block and are managed like in the good old times, i.e. with fragmentation and without special protection systems.

Related topics:

Pager

MMU

Fragmentation

Garbage Collection

Internal Structure of Swallow


Watched Memory

This is the standard kind of memory of Swallow. Each watched memory block gets an own selector, which is used to access a special descriptor of the LDT. Therefore each memory block can have special access attributes, like write protection, and any access above the block limit or to freed blocks can be detected by the CPU and is reported to Swallow. The disadvantage of watched memory is: the CPU can distinguish only 8192 selectors i.e. watched memory blocks. But because the very high security the using of watched memory is to prefer.

Watched memory is managed by HighMem in steps of 32 bytes and is protected with the accuracy of 1 byte (!).

Related topic:

Descriptor

API: Management of Watched Memory

Unwatched Memory

HighMem


Unwatched Memory

This is a tribute to the CPU, which can handle only 8192 different selectors and therefore only 8192 watched memory blocks can be addressed. All of the unwatched memory resides in only one linear memory block. To enable 16 bit code to access this memory, the manager allocates one descriptor per 32 KByte, so it can translate the addresses into ones with offsets of 16 bit.

Additionally the information about free memory blocks are stored in the free blocks itself and not, like at watched memory, in a separate table, to minimize memory needed by the management. Therefore unwatched memory is destined to small memory blocks, because you save extra memory and descriptors. But never forget: you lose important security systems of the CPU, which can't check, whether you access memory outside your block or a freed block or not.

That's why the management functions chain allocations of more then 1 KByte to the manager of watched memory and deals only with smaller ones. If you free memory, the manager decides automatically whether it is watched or unwatched one and calls the right function.

Unwatched memory is managed by HighMem in steps of 8 bytes and does not have a special protection.

Related topics:

API: Management of Unwatched Memory

Watched Memory

HighMem

Descriptor


Garbage Collection

This is one way to remove memory fragmentation. In the case of to intensive fragmentation the memory will be contracted like you squeeze a sponge. So you get one empty block at the end of the memory, any fragments don't exist. This process is called Garbage Collection and was mainly used by BASIC-interpreters to manage strings. The problem of this solution is the duration of the moving and the moving itself, because all references to a moved block must be updated. A modern application you can find is Windows 3.x. The problem of referencing is solved by many restrictions there.

Related topics:

Fragmentation

Pager

HighMem


DOS- and BIOS-Emulator

The DOS/BIOS-Emulator has to take care that programs written for the Real Mode still work in Protected Mode. Following tasks are solved by it:

Loading Process of Your Program

Emulation of DOS and BIOS Functions

Emulation of Protected Mode-incompatible CPU-Instructions

The whole emulator is running at user mode and uses only official Swallow functions for offering its services. Therefore you are able to add own emulation procedures, if you want to use unsupported BIOS-functions.

Related topics:

Internal Structure of Swallow


Loading Process of Your Program

Your program will be loaded by DOS in the normal way. Just within initialisation code of your program the HLL-interface of Swallow takes care that Swallow.Drv will be loaded and Swallow itself initialised. Afterwards the DOS/BIOS-Emulator gets the task to load the program into extended memory and to adapt any address references. Now compiler-specific adaptations are done by the HLL-interface to remove last problems. Because the initialisation code switches into Protected mode (and no one earlier), you are allowed to pack the EXE-file by any program or add virus protection and you even can load the program by own loaders, because Swallow never accesses the EXE-file directly.

Take notice that each module and data segment of your program will be loaded into an own locked watched memory block. Furthermore the emulator sets, according to the segment type, the attribute "code" or "data", and segments ending with "32", like "PMGR32", are declared as 32 bit segments. So you have to change the names of normal 16 bit modules ending with "32", like "Module32", to avoid invalid loading. If you don't take care it will be difficult to find the mistake, because the CPU interprets code completely different in 16 bit then in 32 bit code segments.

Related topics:

DOS/BIOS-Emulator

Initialisation Module

Diagram of Swallow


Protected Mode-incompatible CPU-Instructions

In Protected Mode your program normally works very Protected Mode-unfriendly. The problem of system calls is solved by the DOS/BIOS-Emulator. Additionally it adapts all Protected Mode-incompatible CPU-instructions of the run time library of your compiler. This is: the writing of data into program code, the direct access to the BIOS data segment or the text/graphic memory and the entire floating point emulator. All procedures of Turbo Pascal were made Protected Mode-compatible, and the main part of the C++ libraries. But you can't rely on automatic adapting of Protected Mode-incompatible instructions of your own program. Because it would be like writing a software CPU, you have to avoid such constructions, what shouldn't be a problem normally, if you don't use assembler, never rummage in system tables or program code and don't use an own memory manager.

To enable the adaptation, the BIOS-emulator installs own exception handlers the impossible instructions of the program are emulated or patched by.

Related topics:

DOS/BIOS-Emulator

Exceptions


Emulation of DOS/BIOS Functions
Emulation of DOS and BIOS Functions

DOS and BIOS of the PC were designed at the end of the 70's and have been expanded up to now, but they were never made being able to work with Protected Mode. That is: the programmer of an extender has no one DOS function available (and your application too!). But because you can't live without them, you have to emulate DOS and BIOS. For that purpose all DOS and BIOS accesses of the user program are caught, the CPU is switched into V86-Mode, the proper function can be executed there, and last we switch back to Protected Mode and return to the calling program. It doesn't sound so difficult and it wouldn't, if there aren't the selectors: when your program passes a data block, the within Real Mode not understandable selector has to be translated into a Real Mode segment. And to avoid automatic translation the Real Mode can only address the first MByte, and so any direct access to extended memory, where the data block normally resides, is impossible. The only way out is to create a buffer of DOS memory, where the data are copied in before the call of the function and, on demand, copied back after execution. And now all DOS and BIOS functions don't have anything in common. Each function requires different data blocks of different sizes in different registers. So the Emulator has to prepare each function separately.

The following DOS/BIOS functions are supported:

Interrupt 10h

Interrupt 16h

Interrupt 1ah

Interrupt 1bh

Interrupt 21h

Interrupt 24h

Interrupt 33h

If your program calls other or even unknown (sub)functions, you have to worry about the translation yourself. To make the search for unsupported functions easier, each unknown function forces an abort with a message, which interrupt with which registers was called.

Related topics:

Software Interrupt

Re-entrance

DOS/BIOS-Emulator

Calling V86-Mode Functions


Interrupt 10h
Interrupt 10h (Video Functions)

That is the video interrupt. It offers functions to set the video mode, the hardware cursor, the text font etc. Furthermore the VESA-standard is supported by this interrupt. Except the Save/Restore State-functions of VESA all standard calls are emulated by the BIOS-Emulator. Unknown functions are rejected by a program abort.

Related topic:

DOS/BIOS-Emulator


Interrupt 16h
Interrupt 16h (Keyboard Functions)

You can get keyboard inputs by this interrupt. All known functions are supported, unknown ones are rejected by the BIOS-Emulator.

Related topic:

DOS/BIOS-Emulator


Interrupt 1ah
Interrupt 1ah (RTC Functions)

The reading/setting of the software/hardware clock (RTC) is done by this interrupt. Because no one of the functions expects a data block, all calls to this interrupt are chained to V86-Mode by the BIOS-Emulator without adaptation.

Please take notice that an alarm of the RTC (Interrupt 4ah) will not be chained to Protected Mode. So you have to declare your handler as a callback and to register it as Real Mode interrupt 4ah (but I think, that you have never used this BIOS-function).

But the hardware interrupt of the RTC will be chained, like all other IRQ's too, to Protected Mode, so you can simply install an own handler by Swallow or by the known DOS functions.

Related topics:

DOS/BIOS-Emulator

Hardware Interrupts

Callbacks


Interrupt 1bh
Interrupt 1bh (Ctrl-Break Handler)

The pressing of Ctrl-Break forces this interrupt. It will be chained to Protected Mode automatically, so you can simply register an own handler by Swallow or a DOS function. Please take notice, that you have not to use DOS/BIOS functions during the handling, because the DOS/BIOS-Emulator isn't re-entrant!

Related topics:

DOS/BIOS-Emulator

Re-entrance


Interrupt 21h
Interrupt 21h (DOS Functions)

All DOS functions are called via Interrupt 21h. The DOS-Emulator takes care for correct execution of nearly all DOS functions (up to DOS 6.22). Even the registering and reading of interrupt handlers are done by calling the proper Swallow functions. Please take notice, that you cannot communicate with DOS-drivers by IOCTL commands if you pass the address of a data block, because the emulator can't detect and adapt it. Unsupported functions are rejected via a program abort.

Related topics:

Interrupt 24h

DOS/BIOS-Emulator

Re-entrance


Interrupt 24h
Interrupt 24h (Critical Error Handler)

The handling of Critical Errors are done by raising the interrupt 24h. The DOS-Emulator chains this interrupt to Protected Mode and enables you to make a comfortable reaction to this hated messages. Within this handler, and only this handler, you can call any DOS or BIOS function. For that purpose the DOS/BIOS-Emulator saves its own state and the SDA of DOS to solve the problem of re-entrance. Therefore you are allowed to call DOS-functions aren't re-entrant normally. When the handler returns, the old state of the Emulator and DOS will be restored and the return code given back to DOS. Please take notice, that your handler gets an own stack, so you don't use the DOS-stack and you even have not to do any changes of the DOS-stack.

If you don't register an own handler, the standard DOS-handler will be called, i.e. the handler of Command.Com with the well known "Abort, Retry, Ignore".

Any errors during the access of the swap file containing the virtual memory will be handled by Swallow itself.

Related topics:

Interrupt 21h

DOS/BIOS-Emulator

Re-entrance


SDA
SDA (Swappable Data Area)

It was introduced with MS DOS 3.3 to allow multitasking. All data used by DOS are stored in the SDA. By exchanging the SDA it is possible to interrupt a DOS function and to call DOS with another SDA again. To continue the interrupted function you have to restore the old SDA. Therefore DOS can artificially be made re-entrant.

The DOS/BIOS-Extender uses the SDA to offer all DOS and BIOS functions during an interrupt 24h-Handler (= Critical Error).

Related topics:

Interrupt 21h

Interrupt 24h

Re-entrance


Interrupt 33h
Interrupt 33h (Mouse Functions)

The counterpart of the keyboard interrupt 16h is the mouse interrupt 33h. Any mouse related functions are offered by it. Most of the function are supported by the DOS/BIOS-Emulator.

Following features of the Emulator are important:

-	Even if there is no mouse driver installed, you can call this interrupt. On the contrary you have to check whether an handler is installed if you wouldn't use Swallow.

-	You can register an event handler of mouse events by the normal functions directly. The BIOS-Emulator automatically passes the mouse driver a callback address and unregisters the callback if you restore/change the event handler.

-	Only standard functions are emulated. Extensions, like multiple mouse event handlers, aren't supported and rejected by an abort.

Related topics:

Interrupt 21h

DOS/BIOS-Emulator

Re-entrance


Re-entrance

That is the feature of a function to allow to interrupt, recall and continue it. Within a multitasking system all important system- and driver-functions have to be re-entrant. In single task systems it is needed only if a function can be interrupted by an unpredictable event, like an hardware interrupts or a Critical Error, and recalled by the handler. Under MS-DOS as a typical single task system all functions are never, or only under special circumstances, re-entrant. That goes for Swallow, including its DOS/BIOS-Emulator, too. Important exception is the switching between V86-Mode and Protected Mode: it is allowed any time.

In general you should avoid using of any system call during an hardware interrupt or any related handlers, like the mouse event handler or the Ctrl-Break handler. But within exception handlers and during interrupt 24h you are free to use any DOS, BIOS or Swallow function.

Related topic:

DOS/BIOS-Emulator

Interrupt 24h

SDA


Real Mode-Emulator
Real Mode-Emulator (RealKrnl)

The debugging of programs in Protected mode is hard or better: impossible if you have not special debuggers. To not have to remove Swallow during the debug period it contains a Real Mode Emulator. It simulates most of the functions of Swallow and can be enabled, instead of the Protected Mode original, by the command line parameter "/Real". The modules Kernel and HighMem will be emulated, the module DOS/BIOS-Emulator not needed. Please take notice, that the Real Mode Emulator cannot simulate some functions and so you can't say, that a proper working with Protected Mode implies a correct functioning with the Emulator or visa versa.

The Emulator resides in Swallow.Drv too and will be disposed in Protected Mode.

Related topics:

Internal Structure of Swallow

Kernel

High Level Memory Manager (HighMem)


API Description
Description of the API

You have to distinguish the following kinds of API:

Assembler-API

Pascal-API

C-API


Assembler-API

All functions of Swallow are available via the Assembler-API. Normally you won't need it and will use the Pascal- or C-API instead.

All API-calls are done by raising the interrupt 32h. The function code is passed in register ax and the other registers contain function specific values. If the function succeeded the program returns to the following instruction, whereby the carry flag is cleared and some registers are function-demanding changed. If an error occurs the program will normally be aborted with a message. Alternatively you can force by adding the constant knf_NoCheck to the function code to return even in the case of an error. Then the carry flag will be set and an error code will be returned in register ax.

Following function groups are distinguished:

Descriptor Management

DOS Memory Management

Linear Memory Management

Management of Watched Memory

Management of Unwatched Memory

Interrupt, Exception and V86-Mode Interface Management

DMA Management

Other Swallow Functions

Related topics:

Error Codes of Swallow Functions

Pascal-API

C-API

Internal Structure of Swallow


API: Descriptor Management

The functions of the descriptor management should not be called directly normally. The only exceptions is the function knf_AllocAliasLD and so the function knf_FreeLD too.

Never use these functions to manipulate descriptors returned by a memory manager!

knf_AllocLD

Allocate a local descriptor

Return:	ax	- selector

knf_FreeLD

Free a local descriptor

Parameter:	bx	- selector

knf_Seg2Selector

Allocate an alias-descriptor of a V86-Mode segment

Parameter:	bx	- V86-Mode segment

Return:	ax	- selector

knf_GetSegBase

Get the base address of a segment

Parameter:	bx	- selector of the segment

Return:	edx	- linear base address

knf_SetSegBase

Set the base address of a segment

Parameters:	bx	- selector of the segment
edx	- linear base address

knf_SetSegSize

Set the size of a segment

Parameters:	bx	- selector
edx	- segment size

knf_SetDesRights

Set the access rights of a descriptor

Parameters:	bx	- selector
cx	- access rights (cl=standard right, ch=extended rights)

knf_SetWholeDes

Set all parameters of a descriptor

Parameters:	bx	- selector
cx	- access rights (cl=standard right, ch=extended rights)
edi	- linear base address
edx	- segment size

knf_AllocInitLD

Allocate a local descriptor and set all parameters of it

Parameters:	ebx	- linear base address
cx	- access rights (cl=standard rights, ch=extended rights)
edx	- segment size

Return:	ax	- selector

knf_AllocAliasLD

Allocate an alias descriptor of a code segment

Parameter:	bx	- selector (of the code segment)

Return:	ax	- selector (of the alias descriptor)

Related topics:

Error Codes of Swallow Functions

LDT

Assembler-API

Internal Descriptor Table Management

Linear Memory Management

Management of Watched Memory

Management of Unwatched Memory

DOS Memory Management


knf_AllocLD

Allocate a local descriptor

The access rights, the linear base address and the segment size of the descriptor must be set before you can use it. The freeing of a in that way allocated descriptor must be done by knf_FreeLD.

You should avoid this function and use the functions knf_AllocInitLD or knf_AllocAliasLD instead.

Return:	ax	- selector

Related topic:

Descriptor Management


knf_FreeLD

Free a local descriptor

Only descriptors allocated by knf_AllocLD, knf_AllocInitLD or knf_AllocAliasLD should be freed by this function.

Parameter:	bx	- Selector

Related topic:

Descriptor Management


knf_Seg2Selector

Allocate an alias-descriptor of a V86-Mode segment

The returned selector points to the wished V86-Mode segment, has full read/write rights and a segment limit of 64 KByte. Multiple calls with the same segment return the same selector. Therefore it is not allowed to free the selector! Please take notice, that only a limited count of such selectors does exist.

Parameter:	bx	- V86-segment

Return:	ax	- selector

Related topic:

Descriptor Management


knf_GetSegBase

Get the base address of a segment

Parameter:	bx	- selector of the segment

Return:	edx	- linear base address

Related topic:

Descriptor Management


knf_SetSegBase

Set the base address of a segment

This function should be used only to modify descriptors allocated by knf_AllocLD or knf_AllocInitLD.

Parameters:	bx	- selector
edx	- linear base address

Related topic:

Descriptor Management


knf_SetSegSize

Set the size of a segment

This function should only be called to modify descriptors allocated by knf_AllocLD or knf_AllocInitLD.

Please remember: the investigation of the segment limit (it is one byte less then the size!) is done by the assembler instruction "lsl".

Parameters:	bx	- selector
edx	- segment size

Related topic:

Descriptor Management


knf_SetDesRights

Set the access rights of a descriptor

This function should only be applied to descriptors allocated by knf_AllocLD or knf_AllocInitLD.

The type can only be a code or data segment. The creating of call gates etc., the clearing of the "present" bit or the changing of the privilege level will be rejected.

Please remember: the investigation of the access rights of a segment is done by the assembler instruction "lar".

Parameters:	bx	- selector
cx	- access rights (cl=standard right, ch=extended rights)

Related topic:

Descriptor Management


knf_SetWholeDes

Set all parameters of a descriptor

This function should only be used to modify descriptors allocated by knf_AllocLD or knf_AllocInitLD.

Parameters:	bx	- selector
cx	- access rights (cl=standard right, ch=extended rights)
edi	- linear base address
edx	- segment size

Related topic:

knf_SetDesRights

Descriptor Management


knf_AllocInitLD

Allocate a local descriptor and set all parameters of it

Parameters:	ebx	- linear base address
cx	- access rights (cl=standard rights, ch=extended rights)
edx	- segment size

Return:	ax	- selector

Related topic:

knf_SetDesRights

knf_SetWholeDes

Descriptor Management


knf_AllocAliasLD

Allocate an alias descriptor of a code segment

This function is needed to write into a code segment, e.g. within own program code loaders. Because writing into a code segment is not allowed, you have to create an alias descriptor at first to have free access to the code segment via this selector.

The alias descriptor is equal to the passed descriptor except the access rights.

Alias descriptors should be freed by knf_FreeLD after using. Multiple calls with the same selector return a new alias descriptor every time. If you free the original descriptor the corresponding alias descriptors won't be freed too automatically.

Parameter:	bx	- Selector (of the code segment)

Return:	ax	- Selector (of the alias descriptor)

Related topic:

Descriptor Management


API: DOS Memory Management; Memory, Kinds of under Swallow
API: DOS Memory Management

The following functions enables you to directly call DOS memory management functions from within Protected Mode. Alternatively you still can let manage the DOS memory by the DOS interrupt 21h. In this case the DOS/BIOS-Emulator chains the calls to the functions described here.

Please take notice, that, if you don't add the constant knf_NoCheck to the function code, any error codes returned by DOS won't be returned in ax but will force to abort the program with an error message!

Generally you should not use these functions, because the memory management of Swallow is much more powerful.

knf_AllocDosMem

Allocate a DOS memory block

Parameter:	bx	- Size in paragraphs

Return:	ax	- V86-Mode segment
dx	- selector (corresponding to the V86-Mode segment)

knf_FreeDosMem

Free a DOS memory block

Parameter:	bx	- selector

knf_ResizeDosMem

Resize a DOS memory block

Parameters:	bx	- selector
cx	- new size in paragraphs

Related topics:

Error Codes of Swallow Functions

Linear Memory Management

Management of Watched Memory

Management of Unwatched Memory

Assembler-API


knf_AllocDosMem

Allocate a DOS memory block

The call will be simply chained to DOS.

The returned selector must not be modified or even be freed by knf_FreeLD!

Parameter:	bx	- Size in paragraphs

Return:	ax	- V86-Mode segment
dx	- selector (corresponding to the V86-Mode segment)

Related topic:

DOS Memory Management


Paragraph

A paragraph is equal to 16 Byte. The simple reason is the fact, that in Real Mode segment addresses can begin only at multiples of 16 Byte.


knf_FreeDosMem

Free a DOS memory block

The call will be chained to DOS after converting the selector into a V86-Mode segment.

Please take notice, that the access via the selector is still possible (but useless).

Parameter:	bx	- selector

Related topic:

DOS Memory Management


knf_ResizeDosMem

Resize a DOS memory block

The call will be chained to DOS after converting the selector into a V86-Mode segment.

Parameters:	bx	- selector
cx	- new size in paragraphs

Related topic:

DOS Memory Management


API: Linear Memory Management

The management of linear memory if done by the Pager. Normally you can work more comfortable with Watched/Unwatched Memory instead of Linear Memory, so you never have to call any of the following functions except knf_GetLinearMemInfo.

Please take notice, that anything will be rounded to multiples of the size of a page, i.e. 4 KByte. Passed address ranges will be rounded in such a way, that start addresses will be rounded off to the next 4 KByte border and the end addresses will be rounded up. Furthermore all functions returns linear addresses and even expect them as parameters, i.e. they can't handle selectors (handles of linear memory blocks should not be confused with selectors)! So you have to create a descriptor to the linear address of a memory block by knf_AllocInitLD to access a linear memory block, and you have to worry about freeing it by knf_FreeLD if you released the memory.

knf_GetLinearMemInfo

Get information about free memory on the part of the linear memory manager

Return:	eax	- free virtual memory
(this value is used to calculate knf_GetWatchedMemInfo)
ebx	- free linear memory
ecx	- largest free memory block
edx	- lockable memory
(this value is used to calculate knf_GetWatchedLockedMemInfo)

knf_AllocLinearMem

Allocate linear addresses/memory

Parameters:	ebx	- size in bytes
edx	- bit 0
0=uncommitted
1=committed

Return:	eax	- handle
ebx	- linear base address

knf_FreeLinearMem

Free linear addresses/memory

Parameter:	ebx	- handle

knf_ResizeLinearMem

Resize linear memory block

Parameters:	ebx	- handle
ecx	- new size
edx	- bit 0
0=uncommitted
1=committed
esi	- number of selectors to update
es:edi - table of selectors to update (each 2 bytes)

Return:	eax	- new handle
ebx	- new linear base address

knf_LockLinearMem

Lock linear memory

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

knf_UnlockLinearMem

Unlock linear memory

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address
esi	- number of unlocks

knf_DiscardLinearMem

Discard linear memory

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

knf_CommitLinearMem

Commit virtual memory to linear addresses

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

knf_UncommitLinearMem

Uncommit virtual memory of linear addresses

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

Related topics:

Error Codes of Swallow functions

Low-Level Memory Manager (Pager)

Management of Watched Memory

Management of Unwatched Memory

Linear Address Space

Physical Address Space

Virtual Memory

Assembler-API


knf_GetLinearMemInfo

Get information about free memory on the part of the linear memory manager

This function can be used instead of knf_GetWatchedMemInfo or knf_GetWatchedLockedMemInfo, because this works much faster. But please take notice that the Manager of Watched Memory may offer up to 12 KByte less then knf_GetLinearMemInfo returns due to its internal tables, but small free pieces of Watched Memory can not be counted by this function.

Return:	eax	- free virtual memory
(this value is used to calculate knf_GetWatchedMemInfo)
ebx	- free linear memory
ecx	- largest free memory block
edx	- lockable memory
(this value is used to calculate knf_GetWatchedLockedMemInfo)

Related topics:

Linear Memory Management

knf_GetWatchedMemInfo

knf_GetWatchedLockedMemInfo


knf_AllocLinearMem

Allocate linear addresses/memory

If you declare "uncommitted" you will get addresses, but you only will be able to access them after calling knf_CommitLinearMem. You can use this if you want to create a big table, which is empty at the beginning and will be filled up step by step. In this case you have to commit virtual memory to the addresses by knf_CommitLinearMem before you can access a new piece.

Parameters:	ebx	- size in bytes
edx	- bit 0
0=uncommitted
1=committed

Return:	eax	- handle
ebx	- linear base address

Related topics:

knf_FreeLinearMem

Linear Memory Management


knf_FreeLinearMem

Free linear addresses/memory

The addresses as well as the committed virtual memory will be freed. Further access to the freed memory will raise an exception.

You have to worry to free any descriptors you created to access the memory block.

Parameter:	ebx	- handle

Related topics:

knf_AllocLinearMem

Linear Memory Management


knf_ResizeLinearMem

Resize linear memory block

Every time you get a new handle and a new base address. Please take notice, that new address space but no new virtual memory will be reserved if you declare "uncommitted".

If you already have descriptors pointing to this memory block, you can create a table of Words of the accompanying selectors and pass them and the count of them. These will be updated to the new address. Even if they point somewhere into the memory block they will be updated, invalid descriptors and ones pointing outside the block will be skipped. The memory block should not contain any program code or data used by active hardware interrupt/ exception handlers.

Please take notice, that this function works very fast, even if it has to move the memory block, and it does not need temporary memory, on the contrary to knf_ResizeWatchedMem. So if you need huge memory blocks within your program and you have to resize them often, you should use Linear Memory instead of Watched memory.

Parameters:	ebx	- handle
ecx	- new size
edx	- bit 0
0=uncommitted
1=committed
esi	- number of selectors to update
es:edi - table of selectors to update (each 2 bytes)

Return:	eax	- new handle
ebx	- new linear base address

Related topics:

Linear Memory Management


knf_LockLinearMem

Lock linear memory.

The passed memory will be swapped in from Harddisk and will reside on a static physical address within main memory. This function must be applied to program code or data if interrupt or exception handlers reside in, but will slow down the system if there is many virtual memory.

Multiple locking of the same memory must be removed by the same count of unlock operations, because each page has got a separate lock counter.

The locking of "uncommitted"-pages is not allowed.

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

Related topic:

knf_UnlockLinearMem

Linear Memory Management


knf_UnlockLinearMem

Unlock linear memory

This functions is the counterpart of knf_LockLinearMem. It is possible to unlock only a part of the locked area too.

It is not allowed to unlock not locked or "uncommitted"-addresses.

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address
esi	- number of unlocks

Related topics:

knf_LockLinearMem

Linear Memory Management


knf_DiscardLinearMem

Discard linear memory

The declared memory will be discarded in such a way, that the contents will simply be "forgotten". The accompanying main/hard disk memory can now be used by other virtual memory. But if there is an access to a discarded page, the memory of it will be allocated automatically without creating an error message.

Generally all new memory allocated by knf_AllocLinearMem or knf_ResizeLinearMem is declared as discarded and will get memory at the first access.

The memory manager guaranties that there will be enough memory to assign virtual memory to all discarded pages.

The discarding of locked memory is ignored.

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

Related topic:

Linear Memory Management


knf_CommitLinearMem

Commit virtual memory to linear addresses

The allocating of linear memory causes only the allocating of addresses, not of virtual memory. Just the calling of this functions assigns virtual memory to an address range. By setting the flag "committed" as the parameter of knf_AllocLinearMem or knf_ResizeLinearMem the calling of the function knf_CommitLinearMem will be implied.

The try to assign "committed"-memory virtual memory once more will be ignored.

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

Related topics:

knf_UncommitLinearMem

Linear Memory Management


knf_UncommitLinearMem

Uncommit virtual memory of linear addresses

Unused areas of linear memory blocks can be freed by this function. Thereafter any access to such an area is not possible and will be caught by an exception.

Locked pages will be freed too, the freeing of "uncommitted"-memory will be ignored.

Many DPMI-servers don't offer the possibility to create uncommitted addresses. In this case all function calls will be redirected to knf_DiscardLinearMem.

Parameters:	ebx	- handle
ecx	- number of bytes
edx	- start offset relative to base address

Related topics:

knf_CommitLinearMem

Linear Memory Management


Locked Memory

Any memory you get by a memory manager is unlocked, i.e. can swapped out to hard disk or moved within main memory. Normally you can't remark this, because the memory stays on the same linear address by the help of the MMU, even if its physical address is changed.

If someone accesses swapped memory it will be automatically swapped in by the Pager and so the access will be granted. But this can only be realised if the Pager isn't already executed at this time. At normal work this condition is fulfilled, but within asynchronous interrupts, like a hardware interrupt, you can't rely on it. That's why you have to guarantee, that program code and data of interrupt or exception handlers will never be swapped or moved. This task is done by calling lock-functions, which commands the Pager to fasten the memory onto a fixed address. If the reasons don't exist any more, the locking should be removed as fast as possible to make place for other virtual memory.

The loader of EXE-files locks all code and data segments at program start. But any dynamic allocated memory you get will be unlocked normally!

Related topics:

Unlocked Memory

knf_LockLinearMem

knf_LockWatchedMem

knf_AllocWatchedMem

Pager


Unlocked Memory

Unlocked memory can be moved and swapped out to hard disk. Any memory allocated without special attributes is unlocked.

Related topics:

Locked Memory

knf_UnlockLinearMem

knf_UnlockWatchedMem

Pager


Committed Memory

These are linear addresses virtual memory is committed to. Of course any CPU access is allowed only to committed memory.

Related topics:

Uncommitted Addresses

knf_CommitLinearMem

Pager


Uncommitted Addresses

This are memory-less addresses, i.e. this addresses are reserved but no virtual memory is assigned to them. Any access to this addresses raises a exception.

Application of such addresses is a big table isn't used completely. In this case all addresses of the table are allocated but memory has only to be assigned ("committed") to addresses of used entries. Therefore you do not need to allocate memory for the whole table.

It will be hidden used by all Swallow-intern tables and by the management of Watched Memory.

Related topics:

Committed Memory

knf_UncommitLinearMem

Pager


API: Management of Watched Memory

This API is the most important you will use, because Watched Memory is the easiest and safest kind of memory. If you have many small blocks then take a look at Unwatched Memory too.

Each watched memory block is assigned an own LDT descriptor, therefore the base address of a watched block always is "Selector:0".

Please take notice, that the manager of watched memory identifies memory blocks by their selectors: that's why you have to use the same selector for freeing as you got by the allocation of the block, and never an alias selector or any other copy!

knf_GetWatchedMemInfo

Get size of free watched memory

Return:	ebx	- whole free memory
ecx	- largest free memory block

knf_GetWatchedLockedMemInfo

Get size of free watched lockable memory

Return:	ebx	- whole free locked allocable memory
ecx	- largest free locked allocable memory block

knf_AllocWatchedMem

Allocate a watched memory block

Parameters:	ebx	- size in bytes
cl	- attributes
Bit 7 = 1-Cleared/ 0-Not Cleared
Bit 6 = Default Operation Size/ Big Bit (1 = 32-Bit / 0 = 16-Bit)
Bit 4 = 1-Locked Mem/ 0-Unlocked Mem
Bit 3 = 1-Code Segment/ 0-Data Segment
Bit 1 = Write Permission/ Read Permission
(Bit 0 = Accessed) normally 0

Return:	ax	- Selector

knf_FreeWatchedMem

Free a watched memory block

Parameter:	bx	- selector

knf_ResizeWatchedMem

Resize a watched memory block

Parameters:	bx	- selector
ecx	- new size in bytes

knf_LockWatchedMem

Lock a watched memory block

Parameter:	bx	- selector

knf_UnlockWatchedMem

Unlock a watched memory block

Parameter:	bx	- selector

Related topics:

Error Codes of Swallow Functions

Watched Memory

Management of Unwatched Memory

Linear Memory Management

High-Level Memory Manager

Descriptor

Selector

Management of Descriptor Tables

Assembler-API


knf_GetWatchedMemInfo

Get size of free Watched Memory

This function is, especially with regard to the whole free memory, very exactly, but it is slow too, because free memory is searched "byte per byte". If you only want to know approximately how much memory is free, you should call the function knf_GetLinearMemInfo instead, which is much faster. Those result in eax, less 12 KByte "management buffer", is the minimum of the largest free memory block.

Return:	ebx	- whole free memory
ecx	- largest free memory block

Related topics:

knf_GetWatchedLockedMemInfo

Management of Watched Memory


knf_GetWatchedLockedMemInfo

Get size of free watched lockable memory

The values are exact only in the case, that you allocate new memory with the option "Locked" (look at knf_AllocWatchedMem), but not if you lock already allocated memory by knf_LockWatchedMem. Equal to knf_GetWatchedMemInfo this function is relative slow. A faster method is to call the function knf_GetLinearMemInfo. Those result in edx, less 12 KByte "management buffer", is the minimum of the largest free locked allocated memory block. Additionally this value is the size of the largest block lockable by knf_LockWatchedMem too.

Return:	ebx	- whole free locked allocable memory
ecx	- largest free locked allocable memory block

Related topics:

knf_GetWatchedMemInfo

Management of Watched Memory


knf_AllocWatchedMem

Allocate a watched memory block

By this function you allocate a watched memory block. The attributes regard to the creation of the block and the access rights of the descriptor automatically allocated.

The returned selector has to be passed to all functions calls and has to be freed only by knf_FreeWatchedMem.

The memory manager collects all allocations with the option "locked" into one linear memory block and all of the option "unlocked" into another ones. Thereby it is avoided, that due the 4 KByte rounding by the locking (see also knf_LockLinearMem) the memory of an "unlocked"-block is locked too, only because it resides in the same page. Therefore you should decide before allocating, whether the memory should be locked or not, to avoid "mixing".

Parameters:	ebx	- size in bytes
cl	- attribute
Bit 7 = 1-Cleared/ 0-Not Cleared
Bit 6 = Default Operation Size/ Big Bit (1 = 32-Bit / 0 = 16-Bit)
Bit 4 = 1-Locked/ 0-Unlocked
Bit 3 = 1-Code Segment/ 0-Data Segment
Bit 1 = Read Permission/ Write Permission
Bit 0 = Accessed

Return:	ax	- selector

Related topics:

knf_FreeWatchedMem

knf_LockWatchedMem

Management of Watched Memory


Attribute "Cleared"
Attribut "Cleared"

If you set the bit "Cleared" the memory block will be filled with zeros. If this bit is not set the memory content will be random.

Memory with the option "Cleared" is much slower to create, because at first it takes time to fill the memory and at second uncleared memory is declared as discarded after allocation and only needs virtual memory at the first user access, whereas cleared memory is assigned virtual memory by clearing already.

Related topic:

knf_AllocWatchedMem


Default Operation Size
Default Operation Size/ "Big Bit"

This bit decides whether a 16 bit (cleared Big Bit) or a 32 bit (set Big Bit) segment will be created. It is important only at code and stack segments, else it will be ignored.

At 16 bit code segments the good old address calculation of a 8086 (e.g. [bx+si]) and 16 bit registers are used. At 32 bit code segments the new 80386 addressing (e.g. [eax+4*edx]) and 32 bit registers are the standard. Normal Pascal or C programs consists of 16 bit segments, but 32 bit segments can be created by assembler by using the segment attribute "USE32". The EXE-loader of Swallow recognises 32 bit code segments automatically by the suffix "32" of the name (e.g. "FAST32") and sets the Big Bit of these segments.

The CPU implicit uses the SP register to access 16 bit stack segments and the ESP register for 32 bit stacks. This is unimportant if the stack is smaller then 64 KByte (and you don't misuse the esp-register), but

!!! Attention !!!

Windows 3.x sometimes destroys the high word of ESP if the program runs with a 16 bit stack (don't ask me why), especially during hardware interrupt handlers. On the other hand these handlers get a 32 bit stack under Windows automatically. So if your handlers uses an own stack (which is not needed, because you get an own interrupt stack already) and this one is a 16 bit one, you have to restore after processing not only SP then the whole ESP, or you clear the high word of ESP by a command like

MOVZX	ESP, WORD PTR OLDSP

Related topics:

knf_AllocWatchedMem

Loading Process of Your Program


Code/Data Segment

This attribute bit of knf_AllocWatchedMem decides, whether the created segment will be a code (bit set) or data (bit cleared) segment. The try to execute program code within data segments will be rejected by the CPU and the direct writing into code segments too.

If you want to create a code segment you should allocate a memory block of the type "Code" by knf_AllocWatchedMem first and then create an alias descriptor of this segment by knf_AllocAliasLD. Then your program can load program code into this segment via the alias descriptor and at last you free the alias descriptor by knf_FreeLD. Now you can execute the program in the code segment. If you don't need the segment any more you free the segment by knf_FreeWatchedMem and pass the code selector. By no means you can free the code selector by knf_FreeLD and pass the alias descriptor to knf_FreeWatchedMem, because the memory manager identifies the memory block by the selector!

Related topics:

knf_AllocWatchedMem

knf_AllocAliasLD

Read Permission/Write Permission


Code Segment

If you set the Code/Data segment bit at knf_AllocWatchedMem you will get a code segment. This can be executed and, if you additionally set the Read Permission Bit, be read.

Any write access into a code segment is generally not allowed within Protected Mode.

Related topics:

knf_AllocWatchedMem

Code/Data Segment

Data Segment


Data Segment

By clearing the Code/Data segment bit at calling of knf_AllocWatchedMem you get a data segment. This can be read and, if you additionally set the Write Permission Bit, be written.

Any try to execute program code within a data segment raises an exception.

Related topics:

knf_AllocWatchedMem

Code/Data Segment

Code Segment


Read Permission/ Write Permission

The meaning of this flag depends of the segment type (code or data segment):

At data segments a cleared bit means, that the segment can only be read (Read Only-Segment), and a set bit (Write Permit) also permits the writing (Read/Write-Segment).

At code segments a cleared bit means, that the segment con only be executed but not be directly read (Execute Only-Segment), and a set bit says, that also reading (Read Permit) is permitted (Execute/Read-Segment).

It is not possible to create a segment, which can be read, written and executed (Read/Write/Execute-Segment) within Protected Mode. Therefore it is nearly impossible the make a Real Mode BGI-driver working under Swallow, because they write all their data into their code segment.

Related topics:

knf_AllocWatchedMem

Code Segment/Data Segment


Write Permission

A set Write Permission bit of a data segment decides, that the segment can be written. If it is cleared only reading is allowed.

Related topics:

knf_AllocWatchedMem

Read/Write Permission

Data Segment


Read Permission

A set Read Permit bit of a code segment allows the reading of this segment. If it would be cleared, the segment could only be executed and nothing else.

Related topics:

knf_AllocWatchedMem

Read/Write Permission

Code Segment


Accessed-Bit

The Accessed-Bit of a descriptor is set at each descriptor access. So it was designed to count the accesses of memory segments for efficient virtual memory management. Under Swallow it is not needed, because virtual memory is created by the MMU, and it can be used freely.

At allocating of Watched Memory you can set the start value of this bit (normally 0, i.e. no access was done). But no one uses it, you can choose freely, which value you want to assign this bit.

Related topics:

knf_AllocWatchedMem


knf_FreeWatchedMem

Free a Watched Memory block

You have to pass exactly the same selector you got by knf_AllocWatchedMem. Copies of the descriptor won't be accepted!

Parameter:	bx	- selector

Related topics:

knf_AllocWatchedMem

Management of Watched Memory


knf_ResizeWatchedMem

Resize a Watched Memory block

The memory block can be moved if you let expand it. The base address of the corresponding descriptor will be updated automatically. Any copies you created by knf_AllocAliasLD or any other function have to be updated by yourself!

Please take notice, that any moving of memory blocks, especially of large blocks and with using of a swap file, takes a relative long time and temporary much memory, because memory is needed at the old and the new location. Therefore you should avoid this function or alternatively use a linear memory block allocated by knf_AllocLinearMem and use its much faster knf_ResizeLinearMem function.

Parameters:	bx	- selector
ecx	- new size in bytes

Related topics:

Management of Watched Memory

Linear Memory Manager


knf_LockWatchedMem

Lock a Watched Memory block

The passed memory block will be protected against moving within main memory and swapping onto hard disk. This absolutely needed for program code or data used by interrupt or exception handlers.

Please never forget, that locked memory decreases system performance, and that the manager can place locked memory better if you set the option "Locked" at allocating then if you call knf_LockWatchedMem later.

Memory, which can be moved or swapped again, should be unlocked by calling knf_UnlockWatchedMem. Multiple calls of knf_LockWatchedMem of the same memory block can only be removed by multiple calls of knf_UnlockWatchedMemory, so an interlocking is allowed.

Parameter:	bx	- selector

Related topics:

knf_UnlockWatchedMem

Management of Watched Memory


knf_UnlockWatchedMem

Unlock a Watched Memory block

The passed memory block can be moved and swapped onto Harddisk after this call again, if the same count of unlocks are done as lock operations were called.

The unlocking of not locked memory will be rejected.

Parameter:	bx	- selector

Related topics:

knf_LockWatchedMem

Management of Watched Memory


API: Manager of Unwatched Memory

This API is a combination of Watched and Unwatched memory. Allocations for a block size above 1 KByte and passed Watched Memory blocks are chained automatically to the Manager of Watched Memory. So you can free Watched Memory by knf_FreeUnwatchedMem even if you allocated it by knf_AllocWatchedMem. But you cannot pass Unwatched Memory to functions of Watched Memory! That means, that e.g. the locking of Unwatched Memory is impossible.

In general Unwatched Memory is the memory-saving alternation to Watched Memory. Each 32 KByte of Unwatched Memory are described by one common descriptor, i.e. one selector, so you can save descriptors. This is important at many small allocations, because the CPU can distinguish only 8192 descriptors, i.e. only 8192 Watched Memory blocks can exist. The other side of Unwatched Memory is, that any access beyond block limits or to freed blocks can't be detected by the CPU.

Please take notice, that multiple Unwatched Memory blocks are referenced by one selector, so the offset of a base address need not to be zero. To remember: the offset of Watched Memory blocks is always zero.

knf_GetUnwatchedMemInfo

Get free memory

Return:	ebx	- whole free memory
ecx	- largest free memory block

knf_AllocUnwatchedMem

Allocate a memory block

Parameter:	ebx	- size in bytes

Return:	cx:edx	- address of the memory block

knf_FreeUnwatchedMem

Free a memory block

Parameter:	cx:edx	- address of the memory block

Related topics:

Error Codes of Swallow Functions

Unwatched Memory

High-Level Memory Manager

Management of Watched Memory

Linear Memory Management

Assembler-API


knf_GetUnwatchedMemInfo

Get free memory

Internally knf_GetWatchedMemInfo will be called and the sum of all free memory and the largest free memory block of all will be returned.

Return:	ebx	- whole free memory
ecx	- largest free memory block

Related topics:

Management of Unwatched Memory

knf_GetWatchedMemInfo


knf_AllocUnwatchedMem

Allocate a memory block

Only if the block size is below 1 KByte Unwatched Memory will be allocated else Watched Memory will be returned automatically.

Parameter:	ebx	- size in bytes

Return:	cx:edx	- address of memory block

Related topics:

knf_FreeUnwatchedMem

Management of Unwatched Memory


knf_FreeUnwatchedMem

Free a memory block

Please take notice, that, in contrary to Watched Memory, any access to Unwatched Memory is still possible after freeing!

If you pass the address of a Watched Memory block, the call will be chained to knf_FreeWatchedMem automatically.

Parameter:	cx:edx	- address of the memory block

Related topics:

knf_AllocUnwatchedMem

Management of Unwatched Memory


API: Interrupt, Exception and V86-Mode Interface Management

This API is to be used with care, because misuse can endangers the whole system!

Instead the known DOS/BIOS functions can be used, they will be translated into the corresponding Swallow functions by the DOS/BIOS-Emulator.

In any case you should be familiar with the internal construction of Swallow before you use any of these functions.

knf_AllocCallBack

Allocate a callback-entry

Parameter:	cx:edx	- address of the procedure to call

Return:	cx:dx	- V86-Mode address of callback for Far Call-instructions
bx:ax	- callback-entry for Int-instructions

knf_ReleaseCallBack

Free callback-entry

Parameter:	cx:dx	- V86-Mode callback address for Far Call-instructions

knf_GetProtIntVec

Get address of a software interrupt handler

Parameter:	bl	- interrupt number

Return:	cx:edx	- address of the handler

knf_SetProtIntVec

Set address of a software interrupt handler

Parameters:	bl	- interrupt number
cx:dx	- address of new handler

knf_XchgProtIntVec

Exchange address of a software interrupt handler

Parameters:	bl	- interrupt number
cx:dx	- address of new handler
es:esi	- DWord Ptr to store address of previous handler

knf_GetUserIRQ

Get address of a hardware interrupt handler

Parameter:	bl	- IRQ number

Return:	cx:dx	- address of the handler

knf_SetUserIRQ

Set address of a hardware interrupt handler

Parameters:	bl	- IRQ number
cx:dx	- address of new handler

knf_XchgUserIRQ

Exchange address of hardware interrupt handler

Parameters:	bl	- IRQ number
cx:dx	- address of new handler
es:esi	- DWord Ptr to store address of previous handler

knf_GetUserExc

Get address of an exception handler

Parameter:	bl	- exception number

Return:	cx:edx	- address of the handler

knf_SetUserExc

Set address of an exception handler

Parameters:	bl	- exception number
cx:edx	- address of new handler

knf_XchgUserExc

Exchange address of an exception handler

Parameters:	bl	- exception number
cx:edx	- address of new handler
es:esi	- PWord Ptr to store address of previous handler

knf_CallV86Int

Call V86-Mode interrupt handler

Parameter:	es:edx	- pointer to parameter block _par_v86int

Return:	[es:edx] is filled with return values of the handler
eax	- return value eax of the handler
efl	- return value efl of the handler

knf_CallV86Proc

Call V86-Mode far procedure

Parameter:	es:edx	- pointer to parameter block _par_v86proc

Return:	[es:edx] is filled with return values of the procedure
eax	- return value eax of the procedure
efl	- return value efl of the procedure

knf_CallV86IRet

Call V86-Mode procedure returning with IRet

Parameter:	es:edx	- pointer to parameter block _par_v86proc

Return:	[es:edx] is filled with return values of the procedure
eax	- return value eax of the procedure
efl	- return value efl of the procedure

Related topics:

Error Codes of Swallow Functions

Descriptor Table Management

Internal Structure of Swallow

Diagram of Swallow

Assembler-API


knf_AllocCallBack

Allocate a callback-entry

The returned address can be used as a V86-Mode-interface to a V86-Mode program (e.g. mouse driver). Depending whether your procedure will be called via Far Call- or Int-instruction you have to choose between cx:dx and bx:ax. So your callback has to return via "RetF" or "IRet" from a 16-bit callback and "RetF" or "IRetD" from a 32-bit callback. For the decision, which kind of callback it is the "big bit" of the selector of the callback function is used (see also knf_AllocWatchedMem).

All register values (eax...edx, esi, edi, ebp, flags) are passed to the callback unchanged. Vice versa all this registers will be returned. Please take notice, that segment registers will neither be handed over nor can be changed. Furthermore the stack will be changed after switching to Protected Mode, i.e. a new stack of 512 bytes will be used, so any access to the V86-Mode stack is impossible.

Callbacks are completely re-entrant under Swallow.

Parameter:	cx:edx	- address of the procedure to call

Return:	cx:dx	- V86-Mode address of callback for Far Call-instructions
bx:ax	- callback-entry for Int-instructions

Related topics:

knf_ReleaseCallBack

Callbacks

Re-entrance

Interrupt, Exception and V86-Mode Interface Management


knf_ReleaseCallBack

Free callback-entry

You have to pass the address returned in cx:dx by knf_AllocCallBack.

Do not forget to uninstall your call back before freeing!

Parameter:	cx:dx	- V86-Mode callback address for Far Call-instructions

Related topics:

knf_AllocCallBack

Interrupt, Exception and V86-Mode Interface Management


knf_GetProtIntVec

Get address of a software interrupt handler

The handler is only valid within Protected Mode. The standard handlers installed during system start abort the program with the message "Unsupported interrupt xx". Exceptions are the handlers installed by the DOS/BIOS-Emulator simulating the important DOS/BIOS functions. Depending on the interrupt caller the passed parameter addresses will be handled as 16 or 32 bit addresses, e.g. the function 3fh of Int 21h (=read file) interprets ds:dx as the block address if the caller is a 16 bit code segment and es:edx if the caller is a 32 bit one.

Parameter:	bl	- interrupt number

Return:	cx:edx	- address of the handler

Related topics:

knf_SetProtIntVec

knf_XchgProtIntVec

Interrupt, Exception and V86-Mode Interface Management


knf_SetProtIntVec

Set address of a software interrupt handler

The interrupt handler is used only within Protected Mode and gets only software interrupt, i.e. no exceptions or hardware interrupts. The handler always gets a 16 bit stack frame (defined as _sta_int by Swallow.Inc), so even 32 bit handlers have to return by "IRet" and not by "IRetD". Some versions of Turbo Assembler have problems with "IRet" within 32 bit code, they create the code of "IRetD", so you should take a look at the list file whether it is the right one! This is the reason why it is not allowed to call software interrupts from 32 bit code segments at eip>64 KByte, because the return address would not fit into a 16 bit stack frame. Please also take notice, that the offset of an interrupt handler has to be <64 KByte for compatibility too.

If you install an own handler you should not call the original handler if needed, but jump to it via a "Far Jmp" at the end of your handler, to let the original handler recognise by the return address, whether the caller consists of 16 bit or 32 bit program code, to choose the right parameter address mode (see also knf_GetProtIntVec), because else your handler would ever be seen as the caller and the address calculation would fail. The simple way to avoid this problem is to never use 32 bit code within your program (or to not install an own interrupt handler).

Parameter:	bl	- interrupt number
cx:dx	- address of new handler

Related topics:

knf_GetProtIntVec

knf_XchgProtIntVec

knf_SetUserIRQ

knf_SetUserExc

Emulation of DOS and BIOS Functions

Interrupt, Exception and V86-Mode Interface Management


knf_XchgProtIntVec

Exchange address of a software interrupt handler

The address of the previous interrupt handler will be stored into the DWord es:esi points to and immediately the handler cx:dx will be activated.

Parameters:	bl	- interrupt number
cx:dx	- address of new handler
es:esi	- DWord Ptr to store address of previous handler

Related topics:

knf_SetProtIntVec

knf_GetProtIntVec

Interrupt, Exception and V86-Mode Interface Management


Software Interrupts

In the PC software interrupts are used as a fast interface to miscellaneous DOS and BIOS functions. To let this functions still exist in Protected Mode, they will be emulated by the DOS/BIOS-Emulator by installing its own interrupt handlers. Unknown interrupts and functions raise an error message to identify them and will not cause an untranslated (and dangerous) V86-Mode interrupt, like other DOS-Extenders practice. But user programs sometimes install own interrupt handlers to watch special system functions too. Because they consist of 16 bit program code under DOS, all handlers will still get a 16 bit stack frame (defined as _sta_int by Swallow.Inc). Therefore return addresses may consist of a 16 bit offset only too. That's why 32 bit program code can call software interrupts at code offsets <64 KByte only, else a protection fault will be raised.

Any handler installed by you should only call the original handler after clearing its stack and by using a "Far Jmp"-instruction, because the original handlers will decide by the segment type of the caller, whether the function parameters are 16 or 32 bit. So if somebody calls DOS function 40h (=write into file) the block length will be expected in ecx and the block address in es:edx if the caller is 16 bit, but if the caller is 32 bit, ecx will be interpreted as length and es:edx as the address. If your own 16 bit handler would call the original one, this one would think every time, that the interrupt caller would be 16 bit code and execute any call of 32 bit code incorrectly. This problem does not exist if your program contains only 16 bit code or if your 32 bit program code would clear the high word of all parameter registers before it calls an interrupt.

Please take notice that only software interrupts raised within Protected Mode, i.e. your user program, will be chained to the handler

Related topics:

knf_SetProtIntVec

knf_GetProtIntVec

knf_XchgProtIntVec

Hardware Interrupts

Internal Software Interrupt Management

Emulation of DOS/BIOS Functions


knf_GetUserIRQ

Get address of a hardware interrupt handler

The IRQ-numbers 0..7 specify the first PIC and the numbers 8..15 the second one.

Parameter:	bl	- IRQ number

Return:	cx:dx	- address of the handler

Related topics:

knf_SetUserIRQ

knf_XchgUserIRQ

Interrupt, Exception and V86-Mode Interface Management


PIC
PIC (Programmable Interrupt Controller)

This is the chip managing the hardware interrupts. The first PIC takes the hardware interrupts 0..7 and forces the CPU to raise the interrupts 8..0fh, and the second one takes 8..15 and lets the CPU raise interrupt 70h..77h. Under Swallow hardware and software interrupts are strictly distinguished, so you have to use different get/set functions for hardware and software interrupts, but you can avoid it by using the known DOS functions, which are redirected to the correct Swallow functions by the DOS/BIOS-Emulator.


knf_SetUserIRQ

Set address of a hardware interrupt handler

The IRQ-numbers 0..7 specifies the first PIC and number 8..15 the second one. The handler will be called even if the CPU is not in Protected Mode during raising. It gets an own stack of 512 bytes. The return must be done by "IRet", independent whether it is 16 or 32 bit program code.

Parameters:	bl	- IRQ number
cx:dx	- address of new handler

Related topics:

knf_GetUserIRQ

knf_XchgUserIRQ

Interrupt, Exception and V86-Mode Interface Management


knf_XchgUserIRQ

Exchange address of hardware interrupt handler

The address of the old handler will be saved into the DWord Ptr es:esi points to and then the new handler cx:dx will be set.

Parameters:	bl	- IRQ number
cx:dx	- address of new handler
es:esi	- DWord Ptr to store address of previous handler

Related topics:

knf_GetUserIRQ

knf_SetUserIRQ

Interrupt, Exception and V86-Mode Interface Management


Hardware Interrupts

These interrupts are raised by CPU external chips, like timer or keyboard, and managed by the PIC's. Under DOS hardware and software interrupts aren't distinguished. So you can call a hardware interrupt handler by software due a mistake, and so the handler will be confused, searching which chip has it done. Under Swallow these two kinds of interrupts are managed separately and by different functions

Please take notice, that an hardware interrupt handler gets all interrupts, even the one raised within V86-Mode. And it is not necessary to use a separate stack within the handler, because Swallow already switches to a new one of 512 bytes.

Related topics:

knf_SetUserIRQ

knf_GetUserIRQ

knf_XchgUserIRQ

Software Interrupts

Internal Hardware Interrupt Handling


knf_GetUserExc

Get address of an exception handler

The standard handler normally aborts the program with an error message, so you can use it by own handlers to do the abort.

Parameter:	bl	- exception number

Return:	cx:edx	- address of the handler

Related topics:

knf_SetUserExc

knf_XchgUserExc

Interrupt, Exception and V86-Mode Interface Management


knf_SetUserExc

Set address of an exception handler

When an exception was raised (within Protected Mode) the set handler will be called after completing a short internal handler. It gets an own stack of 512 bytes and a 32 bit stack frame (defined as _sta_uxc by Swallow.Inc). Via "IRetD" the program can be continued at the address _sta_exc_cs:_sta_exc_eip of the stack frame. If you want to call the previous handler you should clean up the stack before and jump to it by a Far Jump. Exceptions raised within the Kernel directly forces an abort.

The programming of exception handlers should be done with 32 bit program code.

Parameters:	bl	- exception number
cx:edx	- address of new handler

Related topics:

knf_GetUserExc

knf_XchgUserExc

Interrupt, Exception and V86-Mode Interface Management


knf_XchgUserExc

Exchange address of an exception handler

The address of the previous handler will be stored into the PWord es:esi points to, and then the new handler cx:edx will be set.

Parameters:	bl	- exception number
cx:edx	- address of new handler
es:esi	- PWord Ptr to store address of previous handler

Related topics:

knf_GetUserExc

knf_SetUserExc

Interrupt, Exception and V86-Mode Interface Management


Exceptions

If something violates against anything within Protected Mode an exception will be raised. This will be automatically chained to Swallow to handle the fault and to restart the invalid instruction or to abort the program with an error message.

Under Swallow virtual memory is simulated by exceptions and Protected Mode-incompatible instructions, e.g. of the run time library of Pascal or C, are adapted or patched by the handlers. If Swallow's internal handlers have no success, the handler installed by you will be called. If this one can't solve the program too it has to call the original handler to let to abort the program. Normally you do not need to install an own handler, but generally it is possible. Please do never forget, that invalid handlers can crash the whole system!

Related topics:

knf_GetUserExc

knf_SetUserExc

knf_XchgUserExc

Internal Exception Handling

Protected Mode-incompatible CPU instructions


knf_CallV86Int

Call V86-Mode interrupt handler

Before you call this function you have to create and fill a structure of the type _par_v86int (defined by Swallow.Inc). The current registers and the register values within the structure are passed to the V86-Mode interrupt handler and will be returned by registers and stored into the structure.

The V86-Mode handler gets a separate stack of 256 Bytes.

Please take notice, that even the flags returned by the handler will be returned by the flag register, so a set carry flag doesn't reflect an error than the return flag of the V86-Mode handler.

Parameter:	es:edx	- pointer to parameter block _par_v86int

Return:	[es:edx] is filled with return values of the handler
eax	- return value eax of the handler
efl	- return value efl of the handler

Related topics:

knf_CallV86Proc

knf_CallV86IRet

Calling of V86-Mode Code

Interrupt, Exception and V86-Mode Interface Management


_par_v86int

That is the structure has to be passed to knf_CallV86Int.

_par_V86Int	struc

_par_V86Int_ds	dw	?	; V86-Mode segment registers

_par_V86Int_es	dw	?

_par_V86Int_fs	dw	?

_par_V86Int_gs	dw	?

_par_V86Int_ax	equ	this word	; left common use registers

_par_V86Int_al	equ	this byte

_par_V86Int_ah	equ	this byte + 1

_par_V86Int_eax	dd	?

_par_V86Int_dx	equ	this word

_par_V86Int_dl	equ	this byte

_par_V86Int_dh	equ	this byte + 1

_par_V86Int_edx	dd	?

_par_V86Int_int	dd	?	; wished interrupt

_par_V86Int	ends

Only the lowest byte of _par_V86Int_int will be regarded.

Related topics:

knf_CallV86Int


knf_CallV86Proc

Call V86-Mode far procedure

The V86-Mode procedure has to return by "RetF".

For further information see also knf_CallV86Int.

Parameter:	es:edx	- pointer to parameter block _par_v86proc

Return:	[es:edx] is filled with return values of the procedure
eax	- return value eax of the procedure
efl	- return value efl of the procedure

Related topics:

knf_CallV86Int

knf_CallV86IRet

Interrupt, Exception and V86-Mode Interface Management


_par_v86proc

That is the structure has to be passed to knf_CallV86Proc and knf_CallV86IRet.

_par_V86Proc	struc

_par_V86Proc_ds	dw	?	; V86-Mode segment registers

_par_V86Proc_es	dw	?

_par_V86Proc_fs	dw	?

_par_V86Proc_gs	dw	?

_par_V86Proc_ax	equ	this word	; left common use registers

_par_V86Proc_al	equ	this byte

_par_V86Proc_ah	equ	this byte + 1

_par_V86Proc_eax	dd	?

_par_V86Proc_dx	equ	this word

_par_V86Proc_dl	equ	this byte

_par_V86Proc_dh	equ	this byte + 1

_par_V86Proc_edx	dd	?

_par_V86Proc_Addr	dd	?	; V86-Mode target address

_par_V86Proc	ends

Related topics:

knf_CallV86Proc

knf_CallV86IRet


knf_CallV86IRet

Call V86-Mode procedure returning with IRet

The V86-Mode procedure has to return via "IRet".

For further details see also knf_CallV86Int.

Parameter:	es:edx	- pointer to parameter block _par_v86proc

Return:	[es:edx] is filled with return values of the procedure
eax	- return value eax of the procedure
efl	- return value efl of the procedure

Related topics:

knf_CallV86Int

knf_CallV86Proc

Interrupt, Exception and V86-Mode Interface Management


API: DMA Management

If you say, the DMA management is treated in a stepmotherly way, I'm afraid you are right.

Generally Swallow doesn't support VDS, i.e. any using of DMA is not supervised and no one VDS-like function is supported. Instead Swallow offers a physical linear 16 KByte buffer, which isn't interrupted by a 64 KByte border. This buffer can be allocated and freed by your program. Under the DPMI-Servers of Novel, Quarter-deck, Windows 3.x and Linux this buffer does not exist, because these servers doesn't offer VDS needed by Swallow to create it.

knf_AllocDMABuffer

Allocate a DMA buffer

Parameter:	ecx	- size in bytes (<= 16 KByte)

Return:	ebx	- physical address of buffer
cx:dx	- address of buffer

knf_ReleaseDMABuffer

Release a DMA buffer

Parameter:	cx:dx	- address of buffer

Related topics:

Error Codes of Swallow Functions

Assembler-API


knf_AllocDMABuffer

Allocate a DMA buffer

At present the size of the buffer is ignored, because all 16 KByte are allocated or freed at once, and the offset is equal to zero. But this can be changed by future versions of Swallow!

Parameter:	ecx	- size in bytes (<= 16 KByte)

Return:	ebx	- physical address of buffer
cx:dx	- address of buffer

Related topics:

knf_ReleaseDMABuffer

DMA Management


knf_ReleaseDMABuffer

Release a DMA buffer

At current the address will be ignored, because there is only 1 buffer. But this can be changed by future versions of Swallow!

Parameter:	cx:dx	- address of buffer

Related topics:

knf_AllocDMABuffer

DMA Management


API: Other Swallow Functions

This group contains all functions I can't assign another one. Many of this functions are for internal use only or should be called only at program start.

knf_UseBaseMem

Enable use of DOS memory

Parameter:	bx	- size of left free DOS memory in paragraphs

knf_ShrinkMem

Shrink amount of extended memory used by Swallow

Parameter:	ebx	- size of left free Swallow memory

knf_Terminate

Terminate program

Parameter:	cl	- exit code

knf_InitSwapFile

Create swap file

Parameters:	bl	- drive
0	= current directory
3..	= C...
-1	= "TEMP" directory
ecx	- size of left free hard disk space (0=the half)

knf_SetBreakCheck

Set flag, wether Ctrl-Alt-Numlock should be checked.

Parameters:	bl	- new state
0	= do not check
1	= enable check

Return:	al	- old state

knf_GetSelName

Get name of a selector

Parameters:	bx	- selector
ecx	- size of buffer (up to 14 bytes)
es:edx	- ASCIIZ buffer (will be filled with space)

knf_SetSelName

Set name of a selector

Parameters:	bx	- selector
ecx	- size of name (incl. zero byte)
es:edx	- name (ASCIIZ, up to 14 characters + zero)

knf_GetPhysAddr

Calculate physical address of linear address (internally)

Parameter:	cx:edx	- linear address

Return:	ecx	- physical address

Related topics:

Error Codes of Swallow Functions

Assembler-API


knf_UseBaseMem

Enable use of DOS memory

Normally Swallow uses only the extended memory and no DOS memory. Therefore your program can easily call other DOS programs. But if you don't want to, you can use the DOS memory under Swallow too. But it is only possible to call this function one time.

If you want to use virtual memory too, you have to leave 32 KByte, or better: you should call knf_InitSwapFile at first. If you want to install an own Int 24h-handler (Critical Error) later, you have to leave 512 byte additionally.

Like knf_InitSwapFile this function should be called at the begin of your program. Moreover you cannot call knf_ShrinkMem after knf_UseBaseMem.

Parameter:	bx	- size of left free DOS memory in paragraphs

Related topics:

knf_InitSwapFile

Other Swallow Functions


knf_ShrinkMem

Shrink amount of extended memory used by Swallow

If your program should run in background, you can call this function before you start another DOS program. Unused extended memory will be returned by Swallow to other memory managers by it.

Please take notice, that you can call this function only one time and that it cannot be reversed! So if you want to allocate memory later, you should leave some memory (e.g. the C++ functions "exec" and "spawn" temporarily allocate 1 KByte of memory), or better 12 KByte more then you think you will need.

This function cannot be called after knf_UseBaseMem.

Parameter:	ebx	- size of left free Swallow memory

Related topics:

knf_UseBaseMem

Other Swallow Functions


knf_Terminate

Terminate program

This function terminates the program (like DOS function 4ch). All memory will be free and all interrupt handlers restored. Alternatively you can use the above mentioned DOS function. It will be automatically converted into knf_Terminate by the DOS/BIOS-Emulator.

Parameter:	cl	- exit code

Related topics:

Other Swallow Functions


knf_InitSwapFile

Create swap file

This function allocates a swap file on hard disk and adds its space to main memory as virtual memory.

Please take notice, that you can call this function only one time and that you cannot reverse it. Moreover the swap file will be created with its whole size, so you should not forget to leave enough hard disk space for own files. Swallow automatically checks, that at least 512 KByte hard disk space are left free.

This function implicit allocates 32 KByte DOS memory and so should be called before knf_UseBaseMem.

Parameters:	bl	- Drive
0	= current directory
3..	= C...
-1	= "TEMP" directory
ecx	- size of left free hard disk space (0=the half)

Related topics:

knf_UseBaseMem

Other Swallow Functions


knf_SetBreakCheck

Set flag, wether Ctrl-Alt-Numlock should be checked.

During debugging of your program you can force Swallow to abort the program with the error message "Debug Trap" if Ctrl-Alt-Numlock are pressed by calling this function with the parameter TRUE. So you can easily break endless loops and get an exact address too.

!!! Attention !!!

For safety within Swallow functions and V86-Mode the abort will be delayed, because else your DOS or BIOS could be damaged (e.g. if an hard disk access is in process).

Parameters:	bl	- new state
0	= do not check
1	= enable check

Return:	al	- old state

Related topics:

Other Swallow Functions


knf_GetSelName

Get name of a selector

This function fills the buffer with the name of the specified selector. Names of system selectors always end with "_System". This function is used by error messages to get the names of program segments.

Parameters:	bx	- selector
ecx	- size of buffer (up to 14 bytes)
es:edx	- ASCIIZ buffer (will be filled with space)

Related topics:

knf_SetSelName

Other Swallow Functions


knf_SetSelName

Set name of a selector

At program start the names of all program code segments are set to identify them within error messages. If you load your own program code, you should set the name of its selector(s), to recognise it at error messages.

Parameters:	bx	- selector
ecx	- size of name (incl. zero byte)
es:edx	- name (ASCZII, up to 14 characters + zero)

Related topics:

knf_GetSelName

Other Swallow Functions


knf_GetPhysAddr

Calculate physical address of linear address (internally)

The result is correct only if the page containing the address is locked, else it may be hidden changed in the future.

This function is not implemented under DPMI.

Only Swallow itself should call this function, never your own program!

Parameter:	cx:edx	- linear address

Return:	ecx	- physical address

Related topics:

Other Swallow Functions


Error Codes of Swallow Functions

If a Swallow function was proceeded successfully, the carry flag will be cleared. If an error occurs your program will normally be aborted or, if you added knf_NoCheck to the function number in ax, the error code will be stored in ax and the carry flag set. The error codes are normally equal to those ones of DPMI, except the ones with "NEW".

Exceptions are the functions knf_CallV86Int, knf_CallV86Proc and knf_CallV86IRet, which do never raise an error and never return one. The content of the carry flag returned by these functions is equal to that one returned by the called V86-Mode function.

The following error codes exist:

0000h-7FFFh - DOS error code

8001h - Unsupported function

8002h - Object is in wrong state for this function

8003h - System integrity would be endangered

8010h - Overflow of internal resources (e.g. DMA buffer)

8011h - Out of descriptors

8012h - Out of linear memory

8013h - Out of physical memory

8014h - NEW: Out of virtual memory

8015h - Out of callback entries

8016h - Out of handles

8017h - Maximum count of locks succeeded

801ah - NEW: Requested DOS memory not available

801bh - NEW: Function not supported by Real Mode emulator

8021h - Invalid value of numeric or flag parameter

8022h - Invalid segment selector

8023h - Invalid handle

8024h - Invalid callback address

8025h - Invalid linear address

8030h - NEW: Invalid Unwatched Memory address

8040h - NEW: Malfunction of external DPMI-server

Related topics:

Assembler-API


Pascal-API

If you use Turbo/Borland Pascal 6/7 you don't have direct contact with the Pascal-API normally, because most of the critical functions are replaced by the corresponding Swallow functions, i.e. GetMem, FreeMem, New, Dispose, MemAvail and MaxAvail are hidden replaced, even if they are called by already compiled units. So you can link third party units without problems and without any changes.

Following functions can be used additionally:

procedure GetLockedMem(var p: pointer; size: Word);

procedure InitSwapFile(Drive : Integer; LeftSpace : LongInt);

function MemAllocSeg(Size : word): Pointer;

function PointerIsValid(p : pointer): Boolean;

function SaveGetMem(size: Word): Pointer;

function SetBreakCheck(CheckBreak : Boolean): Boolean;

procedure ShrinkMem;

procedure UseBaseMem;

function UseUnwatchedMem(UseIt : Boolean): Boolean;

Following variables are declared (they are read only!):

SwallowIsActive : Integer;

seg0040 : Word;

sega000 : Word;

segb000 : Word;

segb800 : Word;

The variable "SwallowIsActive" contains one of the following constants:

swa_inactive = 0;

swa_protected = 1;

swa_emulated = 2;

Related topics:

How to use with Turbo/Borland Pascal 6/7

Problems adapting a Program

Run Time Error Messages

First Aid Kit

Assembler-API


MemAllocSeg (Pascal)
function MemAllocSeg(Size : word): Pointer;

Allocate a Watched Memory block

It is highly protected and always has the base offset 0, so you only have to save the segment part of the address (very useful with assembler). If there is not enough memory available, NIL will be returned. This function is declared by the standard unit Memory too. The allocated memory can be freed by FreeMem or Dispose.

Size:	size of block in bytes

Return:	pointer to allocated memory block
(NIL if failed)

Related topics:

Watched Memory

Pascal-API


GetLockedMem (Pascal)
procedure GetLockedMem(var p: pointer; size: Word);

Allocate a locked Watched Memory block

All data used by hardware interrupt handlers have to reside in those locked memory blocks. But locked memory combined with virtual memory reduces overall system performance! If there is not enough memory left, NIL will be returned. The returned memory can be freed with FreeMem or Dispose.

p:	pointer to fill with address of allocated block
(fill with NIL if allocation failed)

Size:	size of block in bytes

Related topics:

Watched Memory

Locked Memory

Pascal-API


UseBaseMem (Pascal)
procedure UseBaseMem;

Enable using of DOS memory

At program start only the extended memory will be used by Swallow. But if you don't need DOS memory, i.e. you do not call other DOS programs, you can add it to the memory space of Swallow. If you want to use virtual memory too, you should call InitSwapFile before, because it needs 32 KByte DOS memory as its buffer.

This function cannot be reversed!

Related topics:

InitSwapFile

ShrinkMem

Initialisation Module

Pascal-API


ShrinkMem (Pascal)
procedure ShrinkMem;

Release unused extended memory

If you write a background program, you can release unused Swallow memory by this function. But it can happen that nothing will be released, because e.g. the memory is to fragmented or UseBaseMem was called. So you can use it e.g. by a soundtracker, which loads the music data, goes to a DOS shell and terminates itself by "halt" after return. So you can save DOS memory, because Swallow itself only needs about 10 KByte, which is additionally loaded high automatically.

This function cannot be reversed and should not be used in conjunction with UseBaseMem or InitSwapFile!

Related topics:

UseBaseMem

InitSwapFile

Pascal-API


InitSwapFile (Pascal)
procedure InitSwapFile(Drive : Integer; LeftSpace : LongInt);

Create virtual memory on hard disk

At any time, but before UseBaseMem, you can call InitSwapFile to create a swap file. The created space will be added to the common available memory. Please take note to lock data of hardware interrupt handlers to prevent swapping of them to hard disk.

Drive: 	0	= current directory
3..	= C...
-1	= "TEMP"  directory

LeftSpace:	left free hard disk space (0 = the half)

Related topics:

UseBaseMem

Pascal-API


SetBreakCheck (Pascal)
function SetBreakCheck(CheckBreak : Boolean): Boolean;

Set flag, wether Ctrl-Alt-Numlock should be checked.

During debugging of your program you can force Swallow to abort the program with an error message if Ctrl-Alt-Numlock are pressed by calling this function with the parameter TRUE. So you can easily break endless loops and get an exact address too.

!!! Attention !!!

For safety within Swallow functions and V86-Mode the abort will be delayed, because else your DOS or BIOS could be damaged (e.g. if an hard disk access is in process).

CheckBreak:	= true - enable abort
= false - disable abort

Return: 	old state

Related topics:

Pascal-API


SaveGetMem (Pascal)
function SaveGetMem(size: Word): Pointer;

Allocate memory (like GetMem)

In contrary to GetMem NIL will always be returned, if the allocation fails.

Size:	size of block in bytes

Return:	pointer to allocated memory block
(NIL if failed)

Related topics:

Pascal-API


PointerIsValid (Pascal)
function PointerIsValid(p : pointer): Boolean;

Check validity of a pointer

Some older programs have problems to run in Protected Mode, because they access freed or not yet allocated memory. This need not to be an error, e.g. if the read data are discarded, but the CPU refuses such accesses (Turbo Vision 1.0 has some of these "errors"). To avoid it you can check a pointer before by this function, which checks the segment part of the pointer whether it is accessible.

p:	pointer, which segment is to check

Return:	true = segment part is valid
false = segment part is invalid

Related topics:

Pascal-API


UseUnwatchedMem(Pascal)
function UseUnwatchedMem(UseIt : Boolean): Boolean;

Enable use Unwatched Memory.

Normally only Watched Memory is used by the Pascal adapter of Swallow, because this is the most safest kind of memory provided by Swallow. But if your program cannot accept the restriction of 8192 possible Watched Memory blocks, you are free to enable the use of Unwatched Memory to be able to allocate much more memory blocks (but without special protection).

UseIt:	true = use Unwatched Memory
false = don't use Unwatched Memory

Return:	previous state

Related topics:

Watched Memory

Unwatched Memory

Pascal-API


SwallowIsActive (Pascal)
SwallowIsActive : Integer

This variable contains the state of Swallow:

swa_inactive	= normal Real Mode, Swallow is inactive, i.e. was never loaded

swa_protected	= Protected Mode under Swallow is active

swa_emulated	= Real Mode emulation of Swallow is active

Related topics:

C-API

Pascal-API


Variables (Pascal)

Under Turbo Pascal 6.0 the following variables are declared (Turbo Pascal 7.0 contains them per standard):

seg0040 : Word;

sega000 : Word;

segb000 : Word;

segb800 : Word;

They contain the selector of the V86-Mode segment $40, $a000 etc. They are initialised at program start automatically, even if Swallow is inactive.

Related topics:

Pascal-API


C-API

If you use Turbo C++ 1.0/ Borland C++ 3.1 you don't have direct contact with the C-API normally, because most of the critical functions are replaced by the corresponding Swallow functions, i.e. malloc, farmalloc, calloc, farcalloc, free, farfree, realloc, farrealloc, coreleft and farcoreleft are hidden replaced by linking the Swallow specific start-up code, which contains above-mentioned functions and overrides the standard run time library functions, even if they are called by already compiled units. So you can link third party units without problems and without any changes.

If you want to debug your program, you can start it with the command line option "/Real" to use the Real Mode-Emulator of Swallow instead of the Protected Mode original. In this case all functions and variables are emulated and you can debug the program within normal Real Mode, but you have to take care to avoid aborting the program manually, because some debuggers "forget" to free the memory used by the emulator. So another way is to define "_NOSWALLOW" and to add "Swallow.C" to your project. Then you have to remove the Swallow specific start-up code "c0?.obj" and link the program. Now it will be started without Swallow and the unit "Swallow.C" will emulate below-defined functions and variables. The goal is to start the program without any code of Swallow to be fully compatible with the debugger, without changing anything of your program.

Following functions can be used additionally:

void *GetLockedMem(int Size);

void InitSwapFile(int Drive, long int LeftSpace);

void far *Seg2Sel(int Segment);

int SetBreakCheck(int CheckBreak);

void ShrinkMem(long int LeftSpace);

void UseBaseMem(void);

int UseUnwatchedMem(int UseIt);

Following variables are declared (they are read only!):

int SwallowIsActive;

int Seg0040;

int SegA000;

int SegB000;

int SegB800;

Following constants are declared:

#define swa_inactive 0

#define swa_protected 1

#define swa_emulated 2

Additionally all constants of Swallow functions described at the Assembler-API are declared too, but they aren't defined as uppercases then in the same way as they are written in assembler.

Related topics:

How to use with Turbo C++1.0/ Borland C++3.1

Problems adapting a Program

Run Time Error Messages

First Aid Kit

Assembler-API


GetLockedMem (C)
void *GetLockedMem(int Size);

Allocate a locked Watched Memory block

All data used by hardware interrupt handlers have to reside in those locked memory blocks. But locked memory combined with virtual memory reduces overall system performance! If there is not enough memory left, NIL will be returned. The returned memory can be freed with FreeMem or Dispose.

Size:	size of block in bytes

Return:	pointer to allocated block
(NULL if allocation failed)

Related topics:

Watched Memory

Locked Memory

C-API


InitSwapFile (C)
void InitSwapFile(int Drive, long int LeftSpace);

Create virtual memory on hard disk

At any time, but before UseBaseMem, you can call InitSwapFile to create a swap file. The created space will be added to the common available memory. Please take note to lock data of hardware interrupt handlers to prevent swapping them to hard disk.

Drive: 	0	= current directory
3..	= C...
-1	= "TEMP"  directory

LeftSpace:	left free hard disk space (0 = the half)

Related topics:

UseBaseMem

C-API


Seg2Sel (C)
void far *Seg2Sel(int Segment);

Allocate an alias-descriptor of a V86-Mode segment

The returned selector points to the wished V86-Mode segment, has full read/write rights and a segment limit of 64 KByte. Multiple calls with the same segment return the same selector. Therefore it is not allowed to free the selector! Please take notice, that only a limited count of such selectors does exist.

Standard selectors are defined as "segXXXX".

Segment:	V86-Mode segment

Return:	Protected Mode selector

Related topic:

C-API


SetBreakCheck (C)
int SetBreakCheck(int CheckBreak);

Set flag, wether Ctrl-Alt-Numlock should be checked.

During debugging of your program you can force Swallow to abort the program with an error message if Ctrl-Alt-Numlock are pressed by calling this function with the parameter "1". So you can easily break endless loops and get an exact address too.

!!! Attention !!!

For safety within Swallow functions and V86-Mode the abort will be delayed, because else your DOS or BIOS could be damaged (e.g. if an hard disk access is in process).

CheckBreak:	= 1 - enable abort
= 0 - disable abort

Return: 	old state

Related topics:

C-API


ShrinkMem (C)
void ShrinkMem(long int LeftSpace);

Release unused extended memory

If you write a background program, you can release unused Swallow memory by this function. But it can happen that nothing will be released, because e.g. the memory is to fragmented or UseBaseMem was called. So you can use it e.g. by a soundtracker, which loads the music data, goes to a DOS shell and terminates itself by "halt" after return. So you can save DOS memory, because Swallow itself only needs about 10 KByte, which is additionally loaded high automatically.

If you want to allocate memory after ShrinkMem you have to pass the size of the memory to be left (plus 12 KByte for safety) to this function. Please take notice, that even "exec" and "spawn" allocate about 1 KByte memory before calling the wished DOS program!

This function cannot be reversed and should not be used in conjunction with UseBaseMem or InitSwapFile!

LeftSpace:	size of left free Swallow memory

Related topics:

UseBaseMem

InitSwapFile

C-API


UseBaseMem (C)
void UseBaseMem(void);

Enable using of DOS memory

At program start only the extended memory will be used by Swallow. But if you don't need DOS memory, i.e. you do not call other DOS programs, you can add it to the memory space of Swallow. If you want to use virtual memory too, you should call InitSwapFile before, because it needs 32 KByte DOS memory as its buffer.

This function cannot be reversed!

Related topics:

InitSwapFile

ShrinkMem

Initialisation Module

C-API


UseUnwatchedMem (C)
int UseUnwatchedMem(int UseIt);

Enable use Unwatched Memory.

Normally only Watched Memory is used by the C adapter of Swallow, because this is the most safest kind of memory provided by Swallow. But if your program cannot accept the restriction of 8192 possible Watched Memory blocks, you are free to enable the use of Unwatched Memory to be able to allocate much more memory blocks (but without special protection).

UseIt:	1 = use Unwatched Memory
0 = don't use Unwatched Memory

Return:	previous state

Related topics:

Watched Memory

Unwatched Memory

C-API


Variables (Cl)
Variables (C)

Under Turbo C++ 1.0 / Borland C++ 3.1 the following variables are declared:

seg0040 : Int;

sega000 : Int;

segb000 : Int;

segb800 : Int;

They contain the selector of the V86-Mode segment 0x40, 0xa000 etc. They are initialised at program start automatically, even if Swallow is inactive.

Related topics:

C-API


First Aid Kit

Q:	When I start my program, it says it cannot find "Swallow.Drv" and aborts!

A:	"Swallow.Drv" is the file containing the DOS-Extender. It must be in the path of the EXE-file or in a directory set by the "PATH"-environment variable.

Q:	I included Swallow into my program, anything was compiled correctly, but when I try to start it, an error message about something like "Segment Description File..." appears!

A:	To load your program into extended memory Swallow needs the above mentioned file. You have to create it after each linking by calling the SegReg.Exe program; calling convention:
 "SEGREG programname", e.g. "SEGREG PRIME"

Q:	I created such a "Segment Description File", but an error about it appears again!

A:	You have to create this file after each linking.

Q:	My program worked correctly, but after some little changes I get some strange error messages when I try to start it!

A:	You have to create a "Segment Description File" after each linking. If you forget it, your program will be loaded incorrect and so unbelievable and strange error messages will be shown.

Q:	At runtime I get thousands of error messages like "General Protection Fault", "Segment not present" or anything else. What's wrong?

A:	In Protected Mode anything you do is observed by the CPU. Obviously your program has many mistakes, which are perhaps unimportant but recognised by the CPU. Now you have to remove all these mistakes, so sorry.

Q:	When I include a unit, e.g. CRT, an error message at runtime occurs.

A:	It seems you included "Swallow" not as the first unit of your main program. It has to stand even before "CRT" or any other unit, e.g.
"uses Swallow, CRT, DOS".

Q:	I tried to execute a Turbo Vision application with Swallow, but I got error messages inside Turbo Vision units.

A:	Turbo Vision is not Protected Mode compatible, but there is a Turbo Pascal patch bundled with Swallow: "SwaTV1" and "SwaTV2". You have to link "SwaTV1" for Turbo Vision 1 and "SwaTV2" for Turbo Vision 2 to your main program as the second unit, directly after "Swallow", e.g.
"uses Swallow, SwaTV2" for Turbo Vision 2.

Q:	I'm using Borland's BGI drivers, but during "InitGraph" an error message appears!

A:	So sorry: you cannot use Borland's BGI drivers with Swallow, they are to incompatible with Protected Mode.

Q:	My program works in PM, but after a while it terminates with the message "Out of descriptors", but there is enough free memory left!?

A:	Normally all your data blocks are handled by a separate descriptor. That's very save but allows only 8192 blocks. Solution: call "UseUnwatchedMem(true)" in Pascal and "UseUnwatchedMem(1)" in C.

Q:	When I load my program into the external debugger an error message "Breakpoint" appears, even before I start it and so I cannot do anything!

A:	If you load a "C" program into the external debugger, the start-up code will be executed even before the message "Program loaded" appears. Because this code switches your program into Protected Mode, any further access by your debugger will be denied by the CPU. Look at the next question to get the solution.

Q:	I cannot debug my program! When I execute the first instruction, an error message "Breakpoint" appears.

A:	You found the main problem of Protected Mode: it is so protected that even a debugger cannot control your program. If you want to use a debugger, you have to remove Swallow from your program or to start it with the command line option "/Real" (it can be set under "Run|Parameters..."). The latter one forces Swallow to load its own Real Mode Emulator, which supports (nearby) all functions of Swallow but works in Real Mode and so it is debugger-compatible.

Q:	Hey, it works! But my DOS-memory disappears after debugging!?

A:	Some debuggers (especially the external debuggers) have problems to release the DOS memory allocated by the Real Mode Emulator if you terminate the program by hand. To avoid this, you have to remove Swallow completely or to

Pascal:	set the compiler option "NoSwallow" and recompile "Swallow.Pas" to deny loading of Swallow.

C:	define "__NOSWALLOW", compile "Swallow.C" and add "Swallow.C" to your project

Now nothing of Swallow is left and your debugger should work correctly.

Q:	I does the above step, but now my program should run in Protected Mode again, so I removed the option "NoSwallow" / "__NOSWALLOW", but I get linker errors / no Protected Mode!

A:	You have to recompile "Swallow.Pas" / "Swallow.C" to make the change active.

Q:	Can I use Protected Mode TPU's or DLL's of Borland Pascal 7 with Turbo Pascal 7 and Swallow?

A:	Sorry, but this is not an DOS-Extender then a linker problem, and Turbo Pascal does not support DLL's or Protected Mode TPU's and so Swallow does not too.

Related topics:

Run Time Error Messages


Run Time Error Messages

During runtime you will probable get sometimes an error message like the following one:

Error within Protected Mode of Swallow...

General Protection Fault, error code 0500 occurred.

Register contents:

eax=00000500 ebx=00000100 ecx=01250002 edx=00360000

ebp=00001D42 esi=00001F04 edi=00000000

cs:eip=01F7:00000C95 ss:esp=0287:00001D42 eflags=00010202

cs=01F7(*10FB<0000343D) ss=0287(*50F3<00001FFF) ds=027F(*10F3<000017C3)

es=05F7(*00F3<00000024) fs=0000(*xxxx<????????) gs=0000(*xxxx<????????)

Module name: Editors

Found callers:      01F7(Editors ):12E7 01F7(Editors ):2A93 01F7(Editors ):25C5

01F7(Editors ):0D8E 01F7(Editors ):2BC2 01F7(Editors ):3305 01EF(TVEdit  ):0049

cannot continue...

Possible hidden caller: 025F(Objects ):0A1E

Program aborted.

The parts describe:

General Protection Fault, error code 0500 occurred.

The kind of the error occurred, the standard errors are the "General Protection Fault" and the "Segment not present". The first one is raised by nearby all kinds of errors, e.g. if the bounds of an array were exceeded or you tried to access a memory block, which you cannot access, like the try to write something into a code segment, and the second one says definitively, that the program tried to access a memory block which was freed before. The error code is the selector the program tried to load, in the example above it was the selector 500h (which was not declared), or zero, if the exact reason of the exception is unknown. Normally a look at the code is the most easy way to see the mistake your program has done.

Register contents:

eax=00000500 ebx=00000100 ecx=01250002 edx=00360000

ebp=00001D42 esi=00001F04 edi=00000000

cs:eip=01F7:00000C95 ss:esp=0287:00001D42 eflags=00010202

Here you can read the values of the registers at the moment of the exception. Very important is the value of "cs:eip" which shows the address the exception was raised at.

cs=01F7(*10FB<0000343D) ss=0287(*50F3<00001FFF) ds=027F(*10F3<000017C3)

es=05F7(*00F3<00000024) fs=0000(*xxxx<????????) gs=0000(*xxxx<????????)

For each segment register the selector it contained (e.g. 27fh for ds), the access rights (e.g. 10f3h for ds) and the segment limit (e.g. 17c3h for ds) are shown. If the selector is invalid or unreachable, e.g. if the exception was raised by a module of Swallow itself, only xxx and ??? are shown (e.g. at fs). If your program accessed memory when the exception was raised, these information are very useful to find the reason of the exception.

Module name: Editors

This is the name of the module the exception was raised by.

Found callers:      01F7(Editors ):12E7 01F7(Editors ):2A93 01F7(Editors ):25C5

01F7(Editors ):0D8E 01F7(Editors ):2BC2 01F7(Editors ):3305 01EF(TVEdit  ):0049

cannot continue...

Possible hidden caller: 025F(Objects ):0A1E

Swallow tries to find the stack chain of the program, in our example the procedure raised the exception was called by Editors:12e7h, called by Editors:2a93h and so on. Each entry contains the selector of the calling segment (e.g. 1f7h), the name of it (e.g. Editors) and the offset of the instruction after the call instruction. The stack chain is even decoded if some of your units do not contain debug information. Please take note, that if a procedure do not build a standard stack frame, its caller cannot be detected by Swallow. That's why Swallow tries to search a caller hidden in such a way (Objects:a1eh in our example), but sometimes Swallow finds a hidden caller which does not exist really, so you can forget this caller.

An example: a procedure of you ("Caller") calls an other of your functions ("Proc"), which calls a function ("Func") of the unit "System" with invalid parameters. This function "Func" accesses the invalid parameter and raises an exception. If "Func" did not create a stack frame, Swallow shows the address of "Func" as the address the exception was raised at and "Caller" as the direct caller and skips "Proc" because the missed stack frame. But if you have luck, "Proc" is shown as the hidden caller.

If you get such an error message you should write it down, press a key and call your debugger to look at the code the exception raised at and remove the mistake.

Related topics:

First Aid Kit