home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware 1 2 the Maxx
/
sw_1.zip
/
sw_1
/
PROGRAM
/
DJDOC106.ZIP
/
DOCS
/
INTERNAL.DOC
< prev
next >
Wrap
Text File
|
1992-03-11
|
15KB
|
433 lines
/* History:390,1 */
This is the file internals.doc
Contents:
* Methods that go32 uses to perform the DOS extender function.
* How to add new graphics card support.
Note: This file includes PC graphics characters in some illustrations.
It is intended to be viewed via PC or PC compatible printer. If you are
viewing it from Unix and the illustrations look strange, this is why.
Methods:
Go32 contains three basic parts:
* a real-mode control program (code/data segments)
* a 16-bit protected mode interface section
* a 32-bit flat program arena, loaded from a.out file
The interface section provides Turbo-C routines that acces memory in the
32-bit arena, as well as a method for executing code in protected mode.
Initialization:
Control.c is the starting point for the extender. main() is here. This
is where all the system tables are created, like the GDT and TSSs.
TABLES.ASM contains the stubs for the exception handlers, and the
exception tasks. The IDT is mass-loaded by main() to point to these
stubs, then the oddball exceptions are moved (ivec7, page fault handler, etc).
control assist functions:
utils.c, doutils.asm
All control assist functions use linear/physical addresses, not segment
addresses!
The control assist functions allow the controller to read/write memory
from real mode into the 32-bit arena. The available functions are:
word32 peek32(vaddr)
poke32(vaddr, word32)
word16 peek16(vaddr)
poke16(vaddr, word16)
word8 peek8(vaddr)
poke8(vaddr, word8)
memget(vaddr, void *, length)
memput(vaddr, void *, length)
zero32(vaddr) /* always 4K regions */
The main focal point for process control is a TSS structure with some
additional fields at the end. These additional fields record information
such as CR2, the exception number, a temporary stack, and other
information deduced by the extender. There are many TSSs used:
* c_tss is the control tasks. This is the "real mode" task that is
initially switched to as part of entering protected mode. A switch to
this task returns you to real mode.
* a_tss is the arena's task state. By using the fields in this
structure, the extender can get or change register contents. When the
arena task causes an exception, the state of the task is recorded here
for the extender to use in servicing the exception.
* o_tss is the task structure used by the utilities ("other" routines).
* p_tss is the task switched to when a page fault occurs.
* f_tss is the task used by the page fault handler when it calls the
utilities. This is in case the utilites cause a page fault.
* i_tss is the task most exceptions switch to. Exception 7 is
redirected to a protected mode handler if a 387 is present. Each
exception has a stub (in tables.asm) that saves the exception number
and jumps to this task. This task just un-does the effects of the
interrupt, so the TSS points to the exception and not the exception
handler, and then returns to real mode.
The variable tss_ptr points to the currently active TSS. This is the one
the debugger uses when exceptions happen.
Running protected mode code:
MSWITCH.ASM is the gateway into the protected mode arena. *ALL* accesses
to protected mode go into go32() and come out through it. A20 switching
is also handled here.
go32() does a number of functions:
* Set the active task gate in the GDT to refer to tss_ptr.
* Disable interrupts for all tasks except a_tss.
* Clear all the TaskBusy bits for all tasks, to prevent exceptions.
* Clear the exception flag (set when an exception occurs).
* save the real mode stack state.
* Disable sensitive interrupts (like the hard drive). IRQ0-7 remain
enabled.
* Make sure A20 is enabled (a20gate was a stupid idea).
* Set up the GDT and IDT registers.
* Go into protected mode.
* Load the debug registers from _dr[].
* Enable the paging hardware
* Load the 387 state from the structure "npx", if the state had been
stored there (by an exception handler or the "npx" instruction).
* Load the task register to point to c_tss
* Switch to tss_ptr's task
. . . wait for switch back to c_tss . . .
* Save DR6 in _dr6 (debug status).
* Reset IDT and stack
* Re-enable interrupts
* Handle certain time-critical exceptions, like timer tick and COM ports.
Keyboard and NPX interrupts, as well as all other exceptions, are
handled by exception_handler().
* return to caller.
The basic flow of control looks like this (in to top, out from bottom):
╔═══════════════════════╗
║ Start - Real Mode ║
╚═══════════╤═══════════╝
│ ┌───────────────────────────────────────┐
╔═══════════╧═══╧═══════╗ │
║ Protected Mode, c_tss ║ │
╚═══════════╤═══════╤═══╝ │
│ └────────┐ │
╔═══════════╧═══════════╗ ╔═╧═════════════════════╗ │
║ tss_ptr, a_tss ║ ║ tss_ptr, o_tss/f_tss ║ │
╚═══╤═══════════════╤═══╝ ╚═╤═════════════════════╝ │
│ │ │ │
╔═══════════════════╧═══╗ ╔═══╧════════╧══════════╗ │
║ Exception ║ ║ Page Fault, p_tss ║ │
╚═══════════╤═══════════╝ ╚═══════╤═══════╤═══════╝ │
╔═══════════╧═══════════╗ │ ╔══╧═════════════════╗ │
║ i_tss* ║ │ ║ Graphics handler ║ │
╚═══════════╤═══════════╝ │ ╚══════════════════╤═╝ │
└──────────┐ ┌───────┘ └───────┘
╔══════╧════════╧═══════╗
║ Back to Real Mode ║
╚═══════════════════════╝
* some hardware interrupts have their own tss's
GDT entries:
* mandatory zero entry
* GDT
* IDT
* real-mode code segment (for utilities)
* real-mode data segment (for utilities)
* real-mode code, but 32-bit (unused)
* real-mode data, but 32-bit (unused)
* core. 32-bits, mapped 1:1 with first 1M of memory. Used to access screen.
* arena code. 32-bit, starts at 0x10000000.
* arena data. 32-bit, starts at 0x10000000.
* c_tss task segment
* a_tss task segment - changes to reflect what tss_ptr points to
* p_tss task segment
* i_tss task segment
* graphics driver routines
* real code 32-bits, was used for NPX routines (unused)
* VCPI entries
* TSS's for some hardware interrupts
Mappings:
* First 1M mapped 1:1 to linear address 0x0000000
* Arena segments start at 0x10000000. All utilities must be
adjusted to reflect this by adding the constant ARENA to addresses.
* Symbol space is at 0xa000000 (used by debug32)
* Core (0-1M) remapped to 0xF0000000 (0xE0000000 to arena)
* VGA 256c paging at 0xE0000000 (0xD0000000 to arena)
Three 1M pages:
0xD0000000 - 0xD00FFFFF - read/write
0xD0100000 - 0xD01FFFFF - read only
0xD0200000 - 0xD02FFFFF - write only
Page management:
Valloc.c keeps track of what physical pages are available. Pages are
accessed via valloc() and vfree(), which use page *numbers* (not addresses).
Up to 128M of RAM can be managed by this routine. If valloc() is called
and there aren't any free pages, page_out() is called to free one up.
The application has the option of choosing memory from the lower 640K or
above 1M. The page directory and page tables are kept in low memory so
the real mode functions can access them without going into protected mode.
The page table entries include extra information about the status of a
given page, whether it is swappable, swapped, or uninitialized.
Paging.c keeps track of the page tables and directory. It reads the
a.out file and sets up various areas of memory (text, data, stack, etc).
When page faults occur, page_in() pages in memory from disk, swap, or
available memory (for bss and sbrk()). It detects accesses outside the
designated areas to flag a protection violation. Page_out() selects a
present page and swaps it to the swap file, to make space for an incoming
page.
Dalloc.c keeps track of the disk-based swap space, through dalloc() and
dfree(). Up to 128M of swap space can be used.
A20:
A20 is enabled if it was disabled, and never disabled by the extender.
If VCPI is running, then VCPI is used to enable and disable A20 as
appropriate. To determine if it is disabled, the extender compares 0:0
with FFFF:0010 to see if they map to the same physical memory.
Exceptions:
All exceptions are handled by exception_handler() in expnhdlr.c. This
function returns 1 if a fault happens, or 0 if it handles the exception.
Control returns to the arena after an exception is handled. Unhandled
exceptions return you to the debugger or to DOS.
Changes to the DOS INT 21H calls:
brk() and sbrk() replace the DOS memory management vector:
int 21,AH=4A (change memory allocation)
AL=0 brk(ebx) returns old brk in eax
AL=1 sbrk(ebx) returns old brk in eax
Some system calls are handled by the extender directly, instead of through
DOS interrupts:
int 21,AH=FF - turbo assist. Func in AL, parms (ebx,ecx,edx) ret eax
1: creat
2: open
3: fstat
4: gettimeofday
5: settimeofdat
6: stat
7: system
int 10, AH=FF - set video mode
0: 80x25 text
1: default text
2: text CX cols by DX rows
3: biggest text
4: 320x200 graphics
5: default graphics
6: graphics CX width by DX height
7: biggest non-interlaced graphics
8: biggest graphics
80387:
NPX.ASM has all the NPX code (except displaying it, that's in debug.c).
When a numeric exception happens, the 387 state is stored in [_npx], and
_npx_stored is set to 1. go32() will restore the 387 from [_npx] if it
is going to run a_tss (and if the state is stored there). Most of the
time, the 387 state remains in the 387 and _npx_stored remains 0. The
debugger can also move the 387 state from the 387 to [_npx] through
save_npx() if it needs to look at it's state (or fix it, which it doesn't
yet do).
Exception 7 (device not available) occurs the first time you use the 387
after a task switch. It just resets the task switch flag in CR0 and resumes.
This vector points to the real mode handler if the 387 is not installed,
to detect attempts to use the 387.
Exception 0x75 (IRQ13) is the numeric exception vector. Currently, only
divide-by-zero and invalid operation are unmasked. This includes
sqrt(-1), ln(-1), npx stack faults, acos(2), etc.
Debugging:
There are seven debug registers, and seven variables (_dr0.._dr7, or
_dr[0..7]) that control them. go32() loads the debug registers from
these variables going into protected mode, and clears them coming out.
Debug registers point to linear, not segment, addresses, so ad 0x10000000
to them for arena addresses.
syms.c handles all symbol management, including register names and
expression parsing. Symbols are stored in virtual memory beginning
at 0xa0000000.
unassmble.c handles unassembling machine instructions. It's very table
driven, and handles all known (I hope) 386/387 instructions and address
modes.
debug.c is the central point for debuggin instructions. The main loop
for the debugger is here. The utility function will longjump() to the
beginning of the debugger() loop (if available) if they detect an exception.
Graphics Card support:
The files graphics.c and grprot.asm control the graphics handling.
The system allows an external graphics driver to be loaded and used
instead of the built-in VGA graphics driver.
The Graphics Driver:
Go32 includes the standard VGA driver built-in, as well as in an external
driver. To select an external driver, enter a DOS command like:
C> set go32=driver c:\go32\drivers\tseng4k.grd tw 132 th 43 gw 800 gh 600
This says that the driver is "c:\go32\drivers\tseng4k.grd", that the
default text mode is 132x43, and that the default graphics mode is
800x600. The driver defines the resolutions for all the other modes.
The parameters may be listed in any order, or omitted:
C> set go32=driver c:\go32\drivers\tseng4k.grd gh 600 gw 800
There is also an "ansi" parameter that may be added to use an ansi
screen driver to use colors while debugging.
For a listing of what the various modes are, see any of the following:
* listing earlier in this document
* code for the drivers
* include/graphics.h
Example drivers are in vga.asm, tseng4k.asm, and tseng3k.asm.
The first two words point to the two routines a driver provides.
The next word indicates if "split page" is available (see below).
The next four words are the default text and graphics sizes.
The "split page" feature of some vga's (like the TSENG, unlike PS/2)
allow one bank to be used for reading and another for writing, so
reading 0xa000:0x0400 talks to one byte in VRAM, and writing
0xa000:0x0400 talks to a different byte. If your card supports this,
set the split-bank word to 1. If not, set it to 0. You can return
anything for PS/2's because they don't have enough memory to need page
swapping, so can't really tell that you don't have that feature.
GRAPHICS.C installs and calls the graphics driver for mode changes.
The mode change function is pointed to by the first pointer (note: the
pointers are only read when the file is installed). It takes the mode
in AX, and the size (when required) in CX and DX. It should return
the new size in CX and DX.
GRPROT.ASM handles the actual page faults. It uses a page table's
PRESENT bits to determine where the application can read/write to
without having to reconfigure the graphics card. There are two states
the graphics system can be in:
1: rw
Access to 0xd0000000 - 0xd00fffff are allowed. Reads and writes
to the same address refer to the same physical byte of video memory
┌───R───┐ 0xD00xxxxx
────────┼───────┼────────────────────────────────────── video ram
└───W───┘ 0xD00xxxxx
2: r_w
Reads to 0xd0100000 - 0xd01fffff and writes to 0xd0200000 - 0xd02fffff
are allowed. The card is configured to read from one bank and
write to another. This is "split bank" mode, and is used for fast
blits across the screen.
┌───R───┐ 0xD01xxxxxx
────────┴───────┴───────┬───────┬────────────────────── video ram
└───W───┘ 0xD02xxxxxx
When grprot determines that the VGA has to be reprogrammed, it calls the
function pointed to by the second pointer (in protected mode), passing
the read page in AH and the write page in AL.
That's it! All knowlegde of card specifics are contained in the drivers,
so libgr.a and your programs don't have to be recompiled!
If you can't manage to get your card to work, I may be persuaded to add
it for you (as long as you provide technical manuals!). If you add a
card, please send me the sources for the driver so I can integrate it
into future releases!