|  |    | 
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:
        retThe 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]
        retTo 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.