Metropoli BBS
VIEWER: frmi.asm MODE: TEXT (CP437)
;┌───────────────────────────────────────────────────────────────────────────┐
;│                     ≡ Flat Real Mode Interface V1.5 ≡                     │
;│               Blast the 64KB limit of real mode segments !                │
;├───────────────────────────────────────────────────────────────────────────┤
;│ CREATOR: Erwann Corvellec (corvelle@breizh.iut-lannion.fr until summer 95)│
;│              BP 112                                                       │
;│           29270 CARHAIX                                                   │
;│                 FRANCE                                                    │
;│                                                                           │
;│ CODERS : Erwann Corvellec (corvelle@breizh.iut-lannion.fr until summer 95)│
;│          ... TO BE CONTINUED ...                                          │
;│                                                                           │
;│ REVISIONS : ■ 1.0 (EC:94-06-05) : Original code, it works !               │
;│                                   + XMS support                           │
;│                                                                           │
;│             ■ 1.1 (EC:94-07-03) : * Rewrote it, to be FULLY compatible    │
;│                                     with DOS, INT's, and REAL MODE SEGTS !│
;│                                   * Source code cleaned up (IDEAL MODE)...│
;│                                                                           │
;│             ■ 1.2 (EC:94-07-24) : + Detection of DOS EXTENDERS...         │
;│                                   + QUERY_FREE_RAM, GET_RAM, and FREE_RAM │
;│                                     simplify alloc of mem either in       │
;│                                     conventionnal or XMS memory...        │
;│                                                                           │
;│             ■ 1.3 (EC:94-10-16) : * Examples programs modified a bit...   │
;│                                   + LOOPX macros => ECX used instead of CX│
;│                                     in LOOP instructions !                │
;│                                                                           │
;╞═══════════════════════════════════════════════════════════════════════════╡
;│                                                                           │
;│             ■ 1.5 (EC:94-11-28) : MAJOR REVISION ! MANY THINGS FIXED !    │
;│                                   * Fixed a BIG bug that would make an    │
;│                                     alloc with GET_RAM >= 1024 KB to be   │
;│                                     returned by the DOS call...           │
;│                                   * In GET_RAM alloc is first tried in XMS│
;│                                     to keep a maximum of conv. memory for │
;│                                     misc. operations (DOS calls, etc.)    │
;│                                   + FRMI is now supported under BC++ !    │
;│                                     BUT, don't forget /dLANG_C on the     │
;│                                     command line (cf. Makefile, & below). │
;│                                   * ╚> Returns of some functions have     │
;│                                        changed ! Here is the list:        │
;│                                                                           │
;│                                        XMS_LOCK                           │
;│                                         DESTROYS : EBX - EAX             │
;│                                                           ─═─             │
;│                                         OUT      : EBX - EAX=Address of  │
;│                                                           ─═─ memory block│
;│                                                                           │
;│                                        GET_RAM                            │
;│                                         DESTROYS : EBX - EAX             │
;│                                                           ─═─             │
;│                                         OUT      : EBX - EAX = Linear    │
;│                                                           ─═─   address   │
;│                                                                           │
;│                                   * FRMI is now LINKED no more INCLUDED...│
;│                                     ╚> GLOBAL names have been uppercased !│
;│                                   * In case of error, GET_RAM returns an  │
;│                                     error code from ENUM GET_RAM_ERR...   │
;│                                   * Examples programs modified according  │
;│                                     to the new specifications mentionned  │
;│                                     above...                              │
;│                                                                           │
;│             ... TO BE CONTINUED ...                                       │
;│                                                                           │
;├───────────────────────────────────────────────────────────────────────────┤
;│ What is this interface for ?                                              │
;│ Well, it permits you to allocate big amounts of data in conventionnal and │
;│   in XMS memory...                                                        │
;│ Imagine you have an array of 120KB, that would mean normaly one entire    │
;│   segment of 64KB and one segment containing 56KB...                      │
;│ When you want to access to one element of this array you must test if this│
;│   element is in the first or the second segment and then get it...        │
;│ But NOW, with FRMI you can address the whole array directly, by using the │
;│   386+ instructions reserved normaly to protected mode; but without the   │
;│   constraints of protected mode ! That means speed and easiness !         │
;│                                                                           │
;│ All you have to do is to load a segment register with 0 and to point to   │
;│   your data with a 32 bit expression (to point to a max of 4GB of data !).│
;│                                                                           │
;│ EX :                                                                      │
;│ Xor   AX, AX                                                              │
;│ Mov   FS, AX  ; FS = 0 now                                                │
;│                                                                           │
;│ Mov   ESI, 32_BIT_ADDRESS_OF_DATA_IN_MEMORY                               │
;│ Mov   AX, [FS:ESI]  ; Forbidden without FRMI                              │
;│                                                                           │
;│ But you can use more pliant formulas for speed access to arrays of WORDS, │
;│   DWORDS and QWORDS :                                                     │
;│                                                                           │
;│  Base   + (Index   * Factor) + Offset                                     │
;│                                                                           │
;│ │None│     │None│                                                         │
;│ │EAX │     │EAX │                                                         │
;│ │EBX │     │EBX │    │None│                                               │
;│ │ECX │     │ECX │    │2   │    │None  │                                   │
;│ │EDX │  +  │EDX │  * │4   │  + │8-Bit │                                   │
;│ │EDI │     │EDI │    │8   │    │32-Bit│                                   │
;│ │ESI │     │ESI │                                                         │
;│ │EBP │     │EBP │                                                         │
;│ │ESP │     │─── │                                                         │
;│                                                                           │
;│ EX:  Mov  EAX, [FS:EAX+ECX*8+12345678h]                                   │
;│                                                                           │
;│ Cool, isn't it ?                                                          │
;│ See the examples programs for more detail on how to use this interface.   │
;├───────────────────────────────────────────────────────────────────────────┤
;│ YOU MUST READ THE BELOWING LINES, AND AGREE WITH ALL THE TERMS TO USE THIS│
;│ INTERFACE !                                                               │
;│ I, Erwann Corvellec, RESERVE ALL RIGHTS TO THE SOURCE CODE.               │
;├───────────────────────────────────────────────────────────────────────────┤
;│ USE THIS CODE AT YOUR OWN RISKS ! I exclude any and all implied warranties│
;│ of merchantability and fitness for a particular purpose. I make no        │
;│ warranty representation, either express or implied, with respect to this  │
;│ source code, its quality, performance, merchantability, or fitness for a  │
;│ particular purpose. I shall have no liability for special, incidental, or │
;│ consequential damages arising out of or resulting from the use or         │
;│ modification of this source code.                                         │
;│                                                                           │
;│ THIS INTERFACE CAN BE IMPROVED BY EVERYBODY AGREEING THE FOLLOWING RULES :│
;│ - The procedures calls and returns won't be changed !                     │
;│   EX : CALL GET_RAM, KBYTES returning in EBX the address shouldn't be     │
;│        changed in :                                                       │
;│        CALL Want_MEM, BYTES returning in EAX the address                  │
;│                                                                           │
;│   WHY ? If somebody wrote a program using the FRMI V1.2, and want to      │
;│         upgrade to V1.5 for example, he shouldn't have to modify his code.│
;│         That's why I waited until the V1.5 was written to release a FULL  │
;│         stable and cleaned up interface...                                │
;│                                                                           │
;│   NOTE : But you can optimize instructions or the body of the procedures !│
;│          For example you could add PM_FOOL test to the procedure TEST_CPU.│
;│          But you must follow all the above and below steps.               │
;│                                                                           │
;│ - You have to write commentaries on the ENTRY/EXIT parameters of the      │
;│   procedures, and inside the body of the procedures (the hardest part !). │
;│                                                                           │
;│ - You must add your name to the coders list and add a revision entry.     │
;│   Format to use :                                                         │
;│   ■ V.R (CI:YY-MM-DD) : * entry for a modification                        │
;│                         + entry for a novelty                             │
;│                         - entry for a suppression (Boooooh !!!)           │
;│                                                                           │
;│          V.R : Version.Revision                                           │
;│           CI : Coder Initials                                             │
;│     YY-MM-DD : Year-Month-Day                                             │
;│                                                                           │
;│ - You MUST (e)mail to Erwann Corvellec your new revision.                 │
;│   WHY ? Because, I engage myself to unify all the evolutions brought to   │
;│         this interface. So, YOU don't have the right to distribute a      │
;│         modified version !                                                │
;│                                                                           │
;│ FINAL NOTES :   To distinguish two releases of FRMI with the same version │
;│               number, just look at the date of FRMI files...              │
;│               For example : "FRMI!130.EXE  94-10-16  01:30:00"            │
;│               It means : "Update of version 1.3 released on 94-10-16"     │
;│               Please, if U use this code just mention my name in your     │
;│               productions... Thanks in advance !                          │
;│                                                                           │
;│                 Use TASM IDEAL MODE (well, you're forced to do so; 8-D),  │
;│               it's GREAT !                                                │
;│                                                                           │
;│               ╒═════════════════════════════════════════════════════════╕ │
;│               │ PLEASE, IF U USE THIS CODE JUST MENTION MY NAME IN YOUR │ │
;│               │ PRODUCTIONS... THANKS IN ADVANCE !                      │ │
;│               ╘═════════════════════════════════════════════════════════╛ │
;└───────────────────────────────────────────────────────────────────────────┘

IDEAL
P386
NOJUMPS

IFNDEF MDL
   DISPLAY "You should have specified a memory model"
   DISPLAY "in the command line (LARGE assumed)"
   DISPLAY "I.E.: /dMDL=LARGE"

   MODEL LARGE, PASCAL
ELSE
%  MODEL MDL, PASCAL
ENDIF


SEGMENT FRMI_SEG PARA USE16 "CODE"
INCLUDE "FRMI.INC"
ASSUME  CS:FRMI_SEG, DS:FRMI_SEG
══════════════════════════════════════════════════════════════════════════════
ALIGN 4
; Protected mode segment descriptors
LABEL   GDT
   NullDsc  DQ 0              ; GDT entry 0 (null segment)

                              ; GDT entry 1
   DataDsc  DW 0FFFFh         ; Segment LIMIT (bits 0-15)
            DB 0, 0, 0        ; Segment BASE  (bits 0-23)
            DB 10010011b      ; TYPE=data,R/W; DPL=1; present
            DB 11001111b      ; size 4GB
            DB 0              ; Segment BASE  (bits 24-31)

LABEL   GDT_Ptr
            DW $-GDT          ; GDT size
LABEL   GDT_Ptr_Lin_Adr DWORD
            DD 0              ; GDT Linear address


; FRMI_START
;
; DESTROYS : ANY REGISTER EXCEPT DS ES SI DI
; OUT      :             AH = CPU
;                        AL = PROTECTED MODE STATE (see PM ENUM)
;            [FRMI_SEG:CPU] = CPU
;
;            ERROR => Carry flag set

PROC    FRMI_START  USES DS ES SI DI
   Mov   AX, FRMI_SEG
   Mov   DS, AX                    ; DS points to FRMI data

   CALL  TEST_CPU
   JC    @@FRMI_START_ERROR        ; Bad CPU or Bad CPU state => ERROR

   Push  AX                        ; Save the state returned by TEST_CPU
   Xor   EAX, EAX                  ; Calculate linear adress
   Mov   AX, SEG GDT
   Shl   EAX, 4

   Xor   EBX, EBX
   Mov   BX, OFFSET GDT

   Add   EAX, EBX
   Mov   [GDT_Ptr_Lin_Adr], EAX
   LGDT  [PWORD PTR GDT_Ptr]

   CLI                             ; Turn interrupts OFF
   In    AL, 70h                   ; Turn NMI OFF
   Mov   AH, AL
   Or    AL, 10000000b
   Out   70h, AL
   And   AH, 10000000b             ; Save NMI previous state
   Mov   CH, AH                    ; in CH
   In    AL, 71h                   ; Just in case of problem with RTC...

   Mov   EAX, CR0
   Or    AL, 1
   Mov   CR0, EAX

   JMP   SHORT @@PROTECTED_MODE    ; Clear the execution pipe
@@PROTECTED_MODE:
   Mov   BX, 1000b                 ; Load BX to point to GDT entry 1
   Mov   DS, BX                    ; Load segment shadow-registers
   Mov   ES, BX                    ; with GDT entry 1 (4GB segment limit !)
   Mov   FS, BX
   Mov   GS, BX

   And   AL, NOT 1                 ; Switch back to FAST! real-mode without
   Mov   CR0, EAX                  ; resetting the CPU...

   JMP   SHORT @@REAL_MODE         ; Clear the execution pipe
@@REAL_MODE:
   Xor   AX, AX
   Mov   FS, AX                    ; FS
   Mov   GS, AX                    ; and GS can be immediatly used...

   In    AL, 70h                   ; Restore NMI previous state
   And   AL, 01111111b
   Or    AL, CH
   Out   70h, AL
   In    AL, 71h                   ; Just in case of problem with RTC...
   STI                             ; Turn interrupts ON

   Pop   AX                        ; Restore the state returned by TEST_CPU

   CLC
   RET

@@FRMI_START_ERROR:
   STC
   RET
ENDP

══════════════════════════════════════════════════════════════════════════════
ALIGN 4
LABEL CPU BYTE
      DB  NOT 0

; TEST_CPU
;
; DESTROYS : ANY REGISTER
; OUT      :             AH = CPU
;                        AL = PROTECTED MODE STATE
;            [FRMI_SEG:CPU] = CPU
;
;            ERROR => Carry flag set

PROC    TEST_CPU  USES SI DI
   Pushf
   Xor   AX, AX                    ; This part tests the CPU type
   Push  AX
   Popf
   Pushf
   Pop   AX
   And   AX, 0F000h
   Cmp   AX, 0F000h
   JE    @@8086                    ; 8086 installed  => ERROR

   Mov   AX, 7000h
   Push  AX
   Popf
   Pushf
   Pop   AX
   And   AX, 7000h
   JZ    @@80286                   ; 80286 installed => ERROR

   Popf
   JMP   @@Test_386

@@8086:
   Mov   [CS:CPU], CPU_8086        ; Save CPU installed for later  USES ?
   Mov   AH, CPU_8086              ; CPU is a 8086
   Mov   AL, PM_NA                 ; The prot mode state is N/A
   Popf
   STC
   RET

@@80286:
   Mov   [CS:CPU], CPU_80286
   Mov   AH, CPU_80286
   Mov   AL, PM_NA
   Popf
   STC
   RET

@@Test_386:
   Mov   AX, 43E0h                 ; Part to test if protected mode interfaces
   Xor   BX, BX                    ; are installed
   Mov   CX, "DP"
   Mov   DX, "MS"
   INT   2Fh
   Or    AX, AX
   JZ    @@DPMS_INSTALLED          ; Running under DPMS => ERROR

   Xor   EAX, EAX
   Mov   AH, 30h
   Mov   EBX, "PHAR"
   INT   21h
   Shr   EAX, 16
   Cmp   AX, "DX"
   JE    @@PHAR_LAP_INSTALLED      ; Running under Phar Lap => ERROR

   Mov   AX, 0BF02h
   Xor   DX, DX
   Mov   SI, DX
   INT   15h
   Or    DX, DX
   JNZ   @@DOS4GW_INSTALLED        ; Running under DOS4GW => ERROR

   Mov   AX, 0F100h
   INT   2Fh
   Cmp   AL, NOT 0
   JE    @@DOSX_INSTALLED          ; Running under DOSX => ERROR

   Mov   AX, 1687h
   INT   2Fh
   Or    AX, AX
   JZ    @@DPMI_INSTALLED          ; Running under DPMI => ERROR

   SMSW  AX                        ; Get CPU state
   And   AL, 1
   JNZ   @@ERROR_RET               ; If we're in protected mode => ERROR

   Mov   [CS:CPU], CPU_80386       ; The 386+ CPU installed is in real mode;
   Mov   AH, CPU_80386             ; great ! Isn't it ?
   CLC
   RET

@@DPMS_INSTALLED:
   Mov   AL, PM_DPMS               ; Return the type of prot mode interface
   JMP   @@ERROR_RET

@@PHAR_LAP_INSTALLED:
   Mov   AL, PM_PHAR_LAP
   JMP   @@ERROR_RET

@@DOS4GW_INSTALLED:
   Mov   AL, PM_DOS4GW
   JMP   @@ERROR_RET

@@DOSX_INSTALLED:
   Mov   AL, PM_DOSX
   JMP   @@ERROR_RET

@@DPMI_INSTALLED:
   Mov   AL, PM_DPMI

@@ERROR_RET:
   Mov   [CS:CPU], CPU_80386p      ; The 386+ CPU installed is in prot mode
   Mov   AH, CPU_80386p
   STC
   RET
ENDP

══════════════════════════════════════════════════════════════════════════════
ALIGN 4
LABEL XMS_DRIVER_ADDRESS FAR PTR
      XMS_DRIVER_ADDRESS_LOW  DW 0
      XMS_DRIVER_ADDRESS_HIGH DW 0

LABEL XMS_READY BYTE
      DB FALSE


; XMS_INIT
;
; DESTROYS : AX BX DX
; OUT      :             AX = XMS version in BCD (AH=major, AL=minor)
;                        BX = XMS internal revision number (in BCD for HIMEM)
;                        DX = HMA state (1=>HMA exists; 0=>HMA does not exist)
;     [FRMI_SEG:XMS_DRIVER] = FAR XMS DRIVER ENTRY POINT
;
;               ERROR => Carry flag set
;                        AL = Error code (from ENUM XMS_INIT_ERR)

PROC    XMS_INIT  USES ES
   Mov   AX, 4300h
   INT   2Fh
   Cmp   AL, 80h
   JNE   @@XMS_ERR_NOT_INSTALLED   ; XMS not installed => ERROR

   Mov   AX, 4310h                 ; Get XMS driver address
   INT   2Fh

   Mov   [CS:XMS_DRIVER_ADDRESS_LOW ], BX  ; Store XMS driver address, so we
   Mov   [CS:XMS_DRIVER_ADDRESS_HIGH], ES  ; can use @XMS_CALL

   @XMS_CALL 0h                    ; Get XMS version number

   Cmp   AH, 2                     ; Version >= 2 ?
   JB    @@XMS_ERR_BAD_VER         ; NO => ERROR

   CALL  XMS_LOCAL_ENABLE_A20      ; Enable A20 gate, that permits a direct
   Or    AX, AX                    ; access to XM. SO, IT'S VERY IMPORTANT !
   JZ    @@XMS_ERR_A20             ; If we can't => ERROR

   Mov   [CS:XMS_READY], TRUE
   CLC
   RET

@@XMS_ERR_A20:
   Mov   AL, XMS_BAD_A20
   JMP   @@XMS_ERR

@@XMS_ERR_BAD_VER:
   Mov   AL, XMS_BAD_VERSION
   JMP   @@XMS_ERR

@@XMS_ERR_NOT_INSTALLED:
   Mov   AL, XMS_NOT_INSTALLED

@@XMS_ERR:
   STC
   RET
ENDP


; XMS_LOCAL_ENABLE_A20
;
; DESTROYS : AX BL
; OUT      :            AX = 0001h
;
;              ERROR => AX = 0000h
;                       BL = XMS error 80h, 81h, 82h

PROC    XMS_LOCAL_ENABLE_A20
   @XMS_CALL 5h
   RET
ENDP


; XMS_QUERY_FREE
;
; DESTROYS : AX BL
; OUT      : AX = Size of largest extended memory block in KB
;            BL = XMS error 00h, 80h, 81h, A0h

PROC    XMS_QUERY_FREE  USES DX
   @XMS_CALL 8h
   RET
ENDP


; XMS_ALLOC
;
; DESTROYS : AX BL
; IN       : STACK : KBYTES to allocate in extended memory
; OUT      :          AX = Handle for memory block
;
;
;                     BL = XMS error 80h, 81h, A0h, A1h

PROC    XMS_ALLOC  USES DX
   ARG  KBYTES:WORD

   @XMS_CALL 9h, [KBYTES]

   Or    AX, AX
   JZ    @@ALLOC_ERR

   Mov   AX, DX
   CLC
   RET

@@ALLOC_ERR:
   STC
   RET
ENDP


; XMS_FREE
;
; DESTROYS : BL
; IN       : STACK : HANDLE of block to free
; OUT      : ERROR => Carry flag set
;                     BL = XMS error 80h, 81h, A2h, ABh

PROC    XMS_FREE  USES AX DX
   ARG  HANDLE:WORD

   @XMS_CALL 0Ah, [HANDLE]

   Or    AX, AX
   JZ    @@FREE_ERR

   CLC
   RET

@@FREE_ERR:
   STC
   RET
ENDP


; XMS_LOCK
;
; DESTROYS : EAX (DX,AX for C language)
; IN       : STACK : HANDLE of block to lock
; OUT      :         EAX (DX,AX for C language) = Address of memory block
;
;            ERROR => Carry flag set
;                     BL = XMS error 80h, 81h, A2h, ACh, ADh

PROC    XMS_LOCK  USES BX
   ARG  HANDLE:WORD

IFNDEF LANG_C
   Push  DX
ENDIF

   @XMS_CALL 0Ch, [HANDLE]

   Or    AX, AX
   JZ    @@LOCK_ERR

   Mov   AX, BX

IFNDEF LANG_C
   Shl   EAX, 16                   ; Put DX,BX 32 bit linear address
   Mov   AX, DX
   Ror   EAX, 16                   ; into EAX

   Pop  DX
ENDIF

   CLC
   RET

@@LOCK_ERR:
IFNDEF LANG_C
   Pop  DX
ENDIF

   STC
   RET
ENDP


; XMS_UNLOCK
;
; DESTROYS : BL
; IN       : STACK : HANDLE of block to unlock
; OUT      : ERROR => Carry flag set
;                     BL = XMS error 80h, 81h, A2h, AAh

PROC    XMS_UNLOCK  USES AX DX
   ARG  HANDLE:WORD

   @XMS_CALL 0Dh, [HANDLE]

   Or    AX, AX
   JZ    @@UNLOCK_ERR

   CLC
   RET

@@UNLOCK_ERR:
   STC
   RET
ENDP


; XMS_REALLOC
;
; DESTROYS : BX
; IN       : STACK : HANDLE of block to reallocate
;                    NEW SIZE in KB
; OUT      : ERROR => Carry flag set
;                     BL = XMS error 80h, 81h, A0h, A1h, A2h, ABh

PROC    XMS_REALLOC  USES AX DX
   ARG  HANDLE:WORD, NEW_SIZE:WORD

   Mov   BX, [NEW_SIZE]
   @XMS_CALL 0Fh, [HANDLE]

   Or    AX, AX
   JZ    @@REALLOC_ERR

   CLC
   RET

@@REALLOC_ERR:
   STC
   RET
ENDP


══════════════════════════════════════════════════════════════════════════════

STRUC S_EMB_Table                  ; Structure used when allocating XMS memory
ALIGN 4                            ; to save the handle<=>address
   Address DD 0
ALIGN 4
   Handle  DW 0
ENDS

ALIGN 4
EMB_Table S_EMB_Table MAX_EMB DUP (<0,0>)


; QUERY_FREE_RAM
;
; DESTROYS :  AX BX
; OUT      :  AX = RAM free in conv. memory in KB (for CODE or DATA)
;                  <=> size of 1st largest block
;
;             BX = RAM free in XMS in KB (only for DATA)
;                  <=> size of 2nd largest block
;

PROC    QUERY_FREE_RAM  USES DX
   Mov   AH, 48h
   Mov   BX, 0FFFFh                ; Try to get the impossible to know
   INT   21h                       ; the size of the largest block (=BX)
   Shr   BX, 6                     ; Convert it into KB

   Push  BX
   CALL  XMS_QUERY_FREE
   Pop   BX

   Xchg  AX, BX
   RET
ENDP


; GET_RAM
;
; DESTROYS : EAX (DX,AX for C language)
; IN       : STACK = KBYTES to allocate
; OUT      :         EAX (DX,AX for C language) = Linear address
;
;            ERROR => Carry flag set
;                     AX = Error code (from ENUM GET_RAM_ERR)

PROC    GET_RAM  USES BX CX SI
   ARG  KBYTES:WORD

   Xor   CL, CL
   Cmp   [CS:XMS_READY], FALSE
   JE    @@Try_DOS

   CALL  XMS_ALLOC, [KBYTES]       ; Try to alloc in XMS
   JC    @@Try_DOS                 ; Not enough memory in XMS => Try DOS
   Mov   CX, AX                    ; Save returned handle

   Xor   SI, SI

@@Find_EMB_Loop:                   ; Loop to find a free entry in EMB_Table
   Cmp   [CS:(EMB_Table.Handle)+SI], 0
   JE    @@EMB_Entry_Found

   Add   SI, SIZE S_EMB_Table
   Cmp   SI, (SIZE S_EMB_Table)*MAX_EMB
   JB    @@Find_EMB_Loop

   JMP   @@ERROR_NO_FREE_EMB       ; All entries are occupied => ERROR

@@EMB_Entry_Found:
   CALL  XMS_LOCK, CX
   JC    @@ERROR_LOCK_XMS          ; Can't lock EMB => Free EMB and ERROR

IFDEF LANG_C
   Shl   EAX, 16
   Shrd  EAX, EDX, 16              ; Put DX,AX into linear address EAX
ENDIF

   Mov   [CS:(EMB_Table.Address)+SI], EAX  ; Store the linear address
   Mov   [CS:(EMB_Table.Handle )+SI], CX   ; Store the handle

   CLC
   RET

@@Try_DOS:
   Mov   CL, BL                    ; Save error returned by XMS

   Mov   BX, [KBYTES]              ; Try to alloc in conv. memory first
   Cmp   BX, 1024                  ; 1024 is a theorical number ! (640 is OK)
   JAE   @@ERROR_NO_FREE_RAM       ; Not worth to ask DOS => ERROR

   Shl   BX, 6                     ; Convert KB into paragraphs (KB < 1024 !)
   Mov   AH, 48h
   Int   21h
   JC    @@ERROR_NO_FREE_RAM       ; Not enough memory in conv. => ERROR

IFDEF LANG_C
   Xor   DX, DX                    ; Convert AX pointing to a segment
   Shld  DX, AX, 4                 ; into a 32 bit linear address contained
   Shl   AX, 4                     ; in the pair DX,AX
ELSE
   MovZX EAX, AX                   ; Convert AX pointing to a segment
   Shl   EAX, 4                    ; into a 32 bit linear address
ENDIF

   CLC
   RET


@@ERROR_NO_FREE_RAM:
   Cmp   CL, 0A1h
   JE    @@ERROR_NO_FREE_EMB       ; No more EMB available => ERROR

   Mov   AX, NO_FREE_RAM
   JMP   @@ERROR_GET_RAM

@@ERROR_NO_FREE_EMB:
   Mov   AX, NO_FREE_EMB
   JMP   @@ERROR_GET_RAM

@@ERROR_LOCK_XMS:
   CALL  XMS_FREE, CX
   Mov   AX, CANT_LOCK_XMS

@@ERROR_GET_RAM:
   STC
   RET
ENDP


; FREE_RAM
;
; IN       : STACK = Linear address to free
; OUT      : Carry flag set => ERROR

PROC     FREE_RAM  USES AX EBX SI
   ARG   ADDRESS:DWORD

   Mov   EBX, [ADDRESS]

   Cmp   EBX, 0FFFF0h              ; Address beyond 1MB (paragraph aligned) ?
   JA    @@Free_XMS                ; NO => Free XMS

   Shr   EBX, 4                    ; Convert address into a segment
   Push  ES
   Mov   ES, BX
   Mov   AH, 49h
   INT   21h                       ; Free the memory block
   Pop   ES

   JC    @@ERROR_Free              ; Carry flag set => ERROR
   JMP   @@OK_Free

@@Free_XMS:
   Mov   SI, (SIZE S_EMB_Table)*(MAX_EMB-1)

@@Find_EMB_Loop:                   ; Loop to find the handle associated to
                                   ; EBX (=address)
   Cmp   [CS:(EMB_Table.Address)+SI], EBX
   JE    @@EMB_Entry_Found

   Sub   SI, SIZE S_EMB_Table
   JNB   @@Find_EMB_Loop

   JMP   @@ERROR_Free              ; None was found => ERROR

@@EMB_Entry_Found:
   CALL  XMS_UNLOCK, [CS:(EMB_Table.Handle)+SI] ; Try to unlock the EMB
   JC    @@Bad_EMB                              ; Carry flag set => Bad EMB

   CALL  XMS_FREE, [CS:(EMB_Table.Handle)+SI]   ; OK, we can free XMS memory

@@Bad_EMB:
   Pushf
   Mov   [CS:(EMB_Table.Address)+SI], 0         ; Free the entry in our
   Mov   [CS:(EMB_Table.Handle )+SI], 0         ; local EMB table
   Popf

   JC    @@ERROR_Free

@@OK_Free:
   CLC
   RET

@@ERROR_Free:
   STC
   RET
ENDP

ENDS
END
[ RETURN TO DIRECTORY ]