This page contains information concerning Flat Real Mode. The information comes from various sources.
As many of you probably know, the 386+ has a few different processor modes, most important are:
The problem in real mode are the 64 kB segment limits. These limits reside in the segment descriptors. If we are in real mode we can change to pmode, access the descriptor table and change the segment limit to 4 GB, and then switch back to real mode.
To achive this we need to setup the follwing data tables:Code16GDT = 8
Data16GDT = 16
Data32GDT = 24
mem32_GDT dw 4 dup(0)
dw 0ffffh,0ffh,9a00h,0
dw 0ffffh,0ffh,9200h,0
dw 0ffffh,0ffh,9200h,8fh
GDTptr label fword
dw offset GDTptr-1-offset Mem32_GDT
dd offset Mem32_GDT ; Absolute adress GDTtable
dw 0
To change the segement descriptor we must change to protected mode first. This can only be done if the computer is running in real mode. Switching to protected mode is rather simple for this easy task we want to perform. We do not have to set interrupt tables etc. We must, however, switch interrupts of so no interrupt occurs while initialising protected mode as this would lock the computer. Then we set the segment limits and return to real mode.
This can be done with the following snippet:Set4Gig:
mov eax,cr0 ; check for V86 mode
ror eax,1
jc leave4gb ; exit routine if V86 mode is set.
mov ax,cs ; set up GDT for this code segment
mov ds,ax
movzx eax,ax
shl eax,4
add dword ptr ds:GDTptr+2,eax
lgdt fword ptr ds:GDTptr
mov ax,cs
and eax,65535
shl eax,4
mov word ptr ds:Mem32_GDT[Code16GDT+2],ax
mov word ptr ds:Mem32_GDT[Data16GDT+2],ax
ror eax,16
mov byte ptr ds:Mem32_GDT[Code16GDT+4],al
mov byte ptr ds:Mem32_GDT[Data16GDT+4],al
mov byte ptr ds:Mem32_GDT[Code16GDT+7],ah
mov byte ptr ds:Mem32_GDT[Data16GDT+7],ah
cli ; no interrupts
mov eax,cr0 ; set protected mode
or al,1
mov cr0,eax
db 0eah ; far jump to pmode label
dw offset pmode
dw Code16GDT
pmode: mov ax,Data32GDT ; now we are in protected mode
mov ds,ax ; set all selector limits to 4 GB
mov es,ax
mov fs,ax
mov gs,ax
mov eax,cr0 ; restore real mode
and al,0feh
mov cr0,eax
db 0eah ; far jump to rmode label
dw offset rmode
dw Code
rmode: clc ; now we are back in real mode, zero carry
sti ; to indicate ok and enable interrupts
leave4gb:
ret
The real theory behind the working of flat real mode is rather complex and not needed to use the routine. Interrested people should read a book dedicated to protected mode for more information. There are some good, free ones, on the net.
After initialising you have access to the full 4 GB addressing range. Most of that area is probably not present in your computer. Other parts can allready be occupied by other programs. It is wital that none of that memory is change or the computer will lock up.
To know which memory that is free it is possibly to use HIMEM.SYS. The following snippet allow you to initialize HIMEM.SYS and allocate/deallocate XMS memory, and some other usefull functions:;
; initialises HIMEM.SYS for FRM program. if HIMEM.SYS isn't present a carry
; will be returned.
;
; this routine must be called before any memory access outside the first
; meg is performed. (normally a call to the routines would follow immediately
; after a call to set4gig)
;
; note that the routines may not be called in protected mode since they
; write to the code segment.
;
; input:
; none
;
; output:
; cf = set if operation failed, else cleared
;
; destroys:
; ax, bx
; flags
;
himem_init:
mov ax,4300h
int 2Fh
cmp al,80h
je himemfound
stc
ret
himemfound:
push es
mov ax,4310h
int 2Fh
mov [word ptr cs:himem_entry],bx
mov [word ptr cs:himem_entry+2],es
pop es
clc
ret
himem_entry dd ?
;
; allocate XMS memory
;
; input:
; dx = size in kB
;
; output:
; cf = set if alloc failed (probably out of memory)
; dx = handle
;
; destroys:
; ax
; flags
;
alloc_xms:
mov ah,9
call [cs:himem_entry]
cmp dx,ax
jnz allocok
or ax,ax
jnz allocok
stc
ret
allocok:
clc
ret
;
; deallocate xms memory
;
; no error checking is performed.
;
; input:
; dx = handle
;
; output:
; none
;
; destroys:
; ax
; flags
;
dalloc_xms:
push dx
mov ah,0dh
call [cs:himem_entry]
pop dx
mov ah,0ah ; deallocate
call [cs:himem_entry]
ret
;
; returns absolute 32-bit adress of an xms memoryblock
;
; no error checking performed
;
; input:
; dx = handle
;
; output:
; edx = 32Bit start adress of memoryblock
;
; destroys:
; ax, bx
; flags
;
getlinearaddress:
mov ah,0ch
call [cs:himem_entry]
shl edx,16
mov dx,bx
ret
;
; returns the largest available xms memoryblock
;
; no error checking performed
;
; input:
; none
;
; output:
; dx = size of largets available memoryblock (in kB)
;
; destroys:
; ax
; flags
;
getmax_xms:
mov ah, 8 ; available memory
call [cs:Himem_Entry]
ret
To access the memory you must set a segment register to 0 and read or write to the absolute 32-bit address to the memory block.
Another important issue it to make sure that the A20 addressline is enabled (snippet follows).
If the A20 address line isn't enabled the address will be clipped at 1 MB. Some HIMEM function also switch the A20 address line off, so for these reasons it is safest to enable it everytime you have used any of the HIMEM functions. Here is the enable function:;
; enables the A20 adress line.
;
; this is needed to adress the memory above 1Mb. do this at the start of
; your program and everytime another program has been active. in an
; interrupt routine this means you should do it everytime the
; routine is being called, don't worry, it doesn't takes much time.
;
; input:
; none
;
; output:
; none
;
; destroys:
; ax
; flags
;
enablea20:
mov al,0d1h
out 64h,al
call a20wait
mov al,0dfh
out 60h,al
call a20wait
mov al,0ffh
out 64h,al
call a20wait
ret
a20wait:
in al,64h
jmp $+2
and al,2
jnz a20wait
ret
This is the big glitch with flat real mode. It does not work if the computer is allready in protected mode. Mostly protected mode is intialized by EMM386 or similar. If the computer is in protected mode what shall one do? In some cases the following solution might work:
Windows runs in protected mode, and it runs fine even if EMM386 is allready running. Andrew Schulman writes in his book called "Unauthorized Windows 95" that there exist a function to tell EMM386 to shut down and return the computer to real mode. What we need to do is call that function, in the same manner as Windows does, and hopefully EMM386 will shut down.
Windows generates four messages when calling INT 2Fh
:
INT 2Fh
will receive these messages. (i.e. If driver requires to load VxD). The first message is what we need. Let's look at it closer:Parameters:
ax = 1605h cx = 0 dx = flags (bit0=0 - enhanced mode, bit0=1 - standard mode) di = windows version (30ah = 3.1) es:bx = 0:0 or pointer to previous struct win386_startup_info_struct ds:si = 0:0 or pointer to v86 mode toggle function returns: cx = 0 if windows can be started, !=0 if not es:bx = pointer to new struct win386_startup_info_struct ds:si = 0:0 or V86 mode toggle functionAnd here follows two structures that are referenced to in the text
struct Instance_Item_Struct { void far *IIS_Ptr; // seg:ofs to Instance data WORD ISS_Size; // number of bytes } struct win386_startup_info_struct { WORD SIS_Version; Win386_Startup_Info_Struct far *SIS_Next_Dev_Ptr; DWORD SIS_Virt_Dev_File_Ptr // name of the VxD DWORD SIS_Reference_Date // data for VxD Instance_Item_Struct far *Instance_Data_Ptr; }To be able to run flat real mode under EMM386 we need to:
INT 2Fh
(AX = 1605h
)
DS:SI
INT 2Fh
(AX = 1608
- Start up complete)
DS:SI
with AX = 0
(switch to Real mode)
DS:SI
with AX = 1
(switch to V86 mode)
INT 2Fh
(AX = 1609h
- Begin exit)
INT 2Fh
(AX = 1606h
- Exit)
push ds ; get data segment pop es ; since it will be overwritten mov ax,01605h xor dx,dx xor cx,cx xor dx,dx xor si,si mov ds,si mov es,si mov di,030ah ; windows version 3.10 int 2fh test cx,cx ; if cx=0 we can proceed jnz some_error_handler ; address of V86 toggle function is in ds:si. we store it for later use. mov [es:v86switch],si mov [es:v86switch+2],ds mov ax,01608h ; send startup complete int 2fh xor ax,ax ; curcial part: we exit protected mode call v86switch ; by calling function 0 smsw ax ; check if protected mode is active test ax,1 ; and if so, jump to an error handler jnz some_error_handler call set4gb ; initialize flat real mode jc some_error_handler ; here we have initialised flat real mode under emm386. we can do many ; things here, but when you return do _not_ forget to restore segment ; registers because EMM386 might not like to have 4 GB segments. ; ; restoring segment limits can be done by modifying the variables used to ; initialise flat real mode and then call flat real mode init again. mov ax,1 ; switch to V86 mode with EMM function call v86switch mov ax,01609h ; begin exit int 2fh mov ax,01606h int 2fh mov ax,4c00h ; here is it safe to exit to dos int 21hThis code was tested with both EMM386.EXE and QEMM 8.0 and works. There are however some unexplained crashes that occur at random moments (upon exit mostly). The program will not work under Windows (since Windows is using its own protected mode extender).
There are two answers to this question: one short and one longer. The short answer first:
No it is not usefull, mostly because you would still be in 16-bit mode and any 32-bit instruction would carry a prefix taking 1 extra clock cycle to decode.
The longer answer:
Flat Real Mode was invented when demos was using 486:s and running Windows 3.1 and no good extender using VCPI/DPMI existed. Nowadays there are several good extenders (PMODE, PMODE/W, Watcom) which run entirely in protected mode with ability to setup flat mode (this is not flat real mode, it is 32-bit flat protected mode). When the Pentium was introduced, optimized to run 32-bit code, flat real mode became less attractive since it is still 16-bit. As noted above any 32-bit instruction would carry a prefix and slow down exection severely. The flat real mode routine is also quite instable and can, sometimes, easily crash the computer resulting in data loss or other not wanted things.
There may however be times when it is usefull, mostly then for 486 computers running Windows 3.11 or lower. Otherwise there is no real point implementing the algorithm.