;┌───────────────────────────────────────────────────────────────────────────┐
;│ ≡ 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