; XMS Memory manager
; 2005 Christopher Evans - tekno1911@yahoo.com
; requires include toolbox.asm before includeing xms.asm in your source

xmsfound db 0
pfEmmFns dd ?
xmsvers  dw ? ; BCD
ixmsvers dw ?
HMA      dw ?
XMB      dw ? ; KB
XMSMSG   db 'XMS ',0

; xms extended memory data base

xms_handle	dw	0
xms_control	dd	0		;address of driver entry point
xms_initial_sz	dw	0		;1k bytes avail.

write_xms_packet:
  xms_write_len		dd	0  	;length	of write in bytes
  xms_from_write_handle	dw	0	;always zero for conventional memory
  xms_from_write_offset	dw	0	;store buffer offset here
  xms_from_write_segment dw	0	;store buffer segment here
  xms_to_write_handle	dw	0	;handle from	xms driver
  xms_to_write_address	dd	0	;byte address from start of	xms block

read_xms_packet:
  xms_read_len		dd	0  	;length of read in bytes
  xms_from_read_handle	dw	0	;from xms driver
  xms_from_read_address	dd	0	;byte address from start of xms block
  xms_to_read_handle	dw	0	;always zero
  xms_to_read_offset	dw	0	;store buffer offset here
  xms_to_read_segment	dw	0	;store buffer segment here

; XMS function numbers

XGetversion db 00
XRequestHMA db 01
XReleaseHMA db 02
XGlobalE20  db 03
XGlobalD20  db 04
XLocalE20   db 05
XLocalD20   db 06
XQuery20    db 07
XGetMemSize db 08
XAllocEMB   db 09
XFreeEMB    db 0aH
XMoveEMB    db 0bH
XLockEMB    db 0cH
XUnlockEMB  db 0dH
XGetHandleInfo db 0eH
XReallocEMB db 0fH
XRequestUMB db 10H
XReleaseUMB db 11H

XMS db 1   ; get XMS version
XMM db 0   ; get XMM version

; XMS functions return codes }

xmsrOk            db 00H ;  Function successful }
xmsrNotInitd      db 01H ;  XMS driver not initialized by XMS_Setup }
xmsrBadFunction   db 80H ;  Function not implemented }
xmsrVDiskDetected db 81H ;  VDisk was detected }
xmsrA20Error      db 82H ;  An A20 error occurred }
xmsrDriverError   db 8EH ;  A general driver error }
xmsrUnrecError    db 8FH ;  Unrecoverable driver error }
xmsrNoHMA         db 90H ;  HMA does not exist }
xmsrHMAInUse      db 91H ;  HMA is already in use }
xmsrHMAMinError   db 92H ;  HMAMIN parameter is too large }
xmsrHMANotAlloc   db 93H ;  HMA is not allocated }
xmsrA20Enabled    db 94H ;  A20 line still enabled }
xmsrNoMoreMem     db 0A0H ;  All extended memory is allocated }
xmsrNoMoreHandles db 0A1H ;  All available XMS handles are allocated }
xmsrBadHandle     db 0A2H ;  Invalid handle }
xmsrBadSourceH    db 0A3H ;  Source handle is invalid }
xmsrBadSourceO    db 0A4H ;  Source offset is invalid }
xmsrBadDestH      db 0A5H ;  Destination handle is invalid }
xmsrBadDestO      db 0A6H ;  Destination offset is invalid }
xmsrBadLength     db 0A7H ;  Length (size) is invalid }
xmsrBadOverlap    db 0A8H ;  Move has an invalid overlap }
xmsrParityError   db 0A9H ;  Parity error occurred }
xmsrBlkNotLocked  db 0AAH ;  Block is not locked }
xmsrBlkLocked     db 0ABH ;  Block is locked }
xmsrBlkLCOverflow db 0ACH ;  Block lock count overflowed }
xmsrLockFailed    db 0ADH ;  Lock failed }
xmsrSmallerUMB    db 0B0H ;  Only a smaller UMB is available }
xmsrNoUMB         db 0B1H ;  No UMB's are available }
xmsrBadUMBSegment db 0B2H ;  UMB segment number is invalid }

Thandle           dw 0 ; memory block handle type
XMSResult         db ? ; return status of last XMS operation

; ===========================================================================
XMS_Setup proc far
; ===========================================================================
chkxms:
        mov  al, 0
        mov  xmsfound, al
        ; HIMEM.SYS XMS Support installed?
        mov  ax, 4300h
        int  2fh
        cmp  al, 80h  ; 80h OK
        jne  noxmsdriver
        mov  al, 1
        mov  xmsfound, al
        call cs:XMS_EntryAddr
        call cs:XMS_GetVersion
        call cs:XMS_MemorySize
noxmsdriver:
        xor  ax, ax
        mov  al, xmsfound
          ret
XMS_Setup endp

; ===========================================================================
XMS_EntryAddr proc far
; ===========================================================================
        mov  ax, 4310h ; get EMM entry address in ES:BX
        int  2fh
        mov  word ptr cs:[pfEmmFns], bx
        mov  word ptr cs:[pfEmmFns+2], es
        ret
XMS_EntryAddr endp

; ===========================================================================
XMS_GetVersion proc far
; get version
; ===========================================================================
        mov  ah, XGetVersion
        call cs:[pfEmmFns]
;       mov  XMSResult, xmsrOk
   ;     cmp  ax, 1
   ;     je   noxmsdriver

        mov  xmsvers, ax
        mov  ixmsvers, bx
        mov  HMA, dx
        ret
XMS_GetVersion endp

; ===========================================================================
XMS_MemorySize proc far
; query free extended memory
; ===========================================================================
        mov  ah, XGetMemSize
        call cs:[pfEmmFns]
        mov  XMB, ax
        ret
XMS_MemorySize endp

; ===========================================================================
XMS_Info proc far
; ===========================================================================
        _print XMSMSG, 1

        _echo 'v'
        mov  ax, xmsvers
        call put_hex

        _echo ' '
        mov  ax, ixmsvers
        call put_hex
        _echo ' '
        mov  ax, XMB
        call putnum
        _echo 'K'
        _echo 'B'
        _echo ' '
        mov  ax, HMA
        or   ax, ax
        jz   _noHMA
        _echo 'H'
        _echo 'M'
        _echo 'A'
_noHMA: 
         ret
XMS_Info endp

; ===========================================================================
XMS_HMAAvail proc far
; Out: AX = 1 true
; ===========================================================================
          mov  al, xmsfound
          cmp  al, 1
          jne  _xmshmano
          mov ah, XGetVersion
          call cs:[pfEmmFns]
          mov al, xmsrOk
          mov XMSResult, al
          mov al, dl 
_xmshmano:
          ret
XMS_HMAAvail endp

; ===========================================================================
XMS_AllocHMA proc far
; DX = Size requested
; ===========================================================================
        ; mov dx, Size       
        mov ah, XRequestHMA
        call cs:[pfEmmFns]
        or  ax, ax
        jz  xmshmano 
        xor bl,bl      
xmshmano:
        mov al,bl          
        mov XMSResult, bl 
        ret
XMS_AllocHMA endp

; ===========================================================================
XMS_FreeHMA proc far
; ===========================================================================
  MOV BL,xmsfound
  CMP bl, 1
  JNE xmsfreehmano
  MOV AH,XReleaseHMA
  CALL cs:[PfEmmFns] 
  OR  AX,AX
  JZ  xmsfreehmano          ;  If error then jump, else 
  XOR BL,BL        ;  clear error code
xmsfreehmano:
  MOV AL,BL
  MOV XMSResult, BL;  { Get return code in XMSResult }
          ret
XMS_FreeHMA endp

; ===========================================================================
XMS_GlobalEnableA20 proc far
; ===========================================================================
  MOV BL,xmsfound
  CMP bl,1
  JNE XMSGEA20NO
  MOV AH,XGlobalE20 
  CALL cs:[PfEmmFns] 
  OR  AX,AX
  JZ  XMSGEA20NO
  XOR BL,BL          
XMSGEA20NO:
  MOV AL,BL
  MOV XMSResult,BL 
          ret
XMS_GlobalEnableA20 endp

; ===========================================================================
XMS_GlobalDisableA20 proc far
; ===========================================================================
  MOV BL,xmsfound
  CMP bl,1
  JNE xmsGDA20no
  MOV AH,XGlobalD20 
  CALL cs:[PfEmmFns] 
  OR  AX,AX
  JZ  xmsGDA20no
  XOR BL,BL          
xmsGDA20no:
  MOV AL,BL
  MOV XMSResult,BL  
          ret
XMS_GlobalDisableA20 endp

; ===========================================================================
XMS_LocalEnableA20 proc far
; ===========================================================================
  MOV BL,xmsfound
  CMP bl, 1
  JNE xmslea20no
  MOV AH,XLocalE20   
  CALL cs:[PfEmmFns] 
  OR  AX,AX
  JZ  xmslea20no
  XOR BL,BL          
xmslea20no:
  MOV AL,BL
  MOV XMSResult, BL  
          ret
XMS_LocalEnableA20 endp

; ===========================================================================
XMS_LocalDisableA20 proc far
; ===========================================================================
  MOV BL,xmsfound
  CMP bl,1
  JNE xmslda20no
  MOV AH,XLocalD20  
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  xmslda20no
  XOR BL,BL         
xmslda20no:
  MOV AL,BL
  MOV XMSResult,BL  
          ret
XMS_LocalDisableA20 endp

; ===========================================================================
XMS_QueryA20 proc far
; ===========================================================================
  MOV BL,xmsfound
  CMP bl,1
  JNE xmaqa20no
  MOV AH,XQuery20  
  CALL cs:[PfEmmFns]
xmaqa20no:
  MOV XMSResult,BL  
          ret
XMS_QueryA20 endp

; ===========================================================================
XMS_MemAvail proc far
; ===========================================================================
  mov bl, xmsfound
  cmp bl, 1
  jne xmsmemno
  mov  ah, XGetMemSize
  call cs:[PfEmmFns]
  mov  ax, dx             ; AX=KBytes
xmsmemno:
  mov  XMSResult, bl
          ret
XMS_MemAvail endp

; ===========================================================================
XMS_MaxAvail proc far
; ===========================================================================
  mov bl, xmsfound
  cmp bl, 1
  jne xmsmaxno
  mov  ah, XGetMemSize
  call cs:[PfEmmFns]
xmsmaxno:
  mov  XMSResult, bl
          ret
XMS_MaxAvail endp

; ===========================================================================
XMS_AllocEMB proc far
; DX = size
; ===========================================================================
  mov bl, xmsfound
  cmp bl, 1
  jne xmsAllno
  MOV AH,XAllocEMB  
; MOV DX,Size       ; { Number of K-Bytes to allocate }
  CALL cs:[PfEmmFns]   
  OR  AX,AX
  JZ  xmsAllerr
  MOV AX,DX          ;{ Store handle number in AX }
  XOR BL,BL          ;{ Set no error }
  JMP xmsAllno
xmsAllerr:
  XOR AX,AX          ;{ Return handle 0 if error }

xmsAllno:
  mov  XMSResult, bl
         ret
XMS_AllocEMB endp

; ===========================================================================
XMS_ReAllocEMB proc far
; AX = Handle
; BX = Size
; ===========================================================================
  mov dx, ax
;  MOV BX,Size       ; New size wanted in K-Bytes
  MOV AH,XReallocEMB 
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  @RAEMB
  XOR BL,BL       
@RAEMB:
  MOV AL,BL       
  MOV XMSResult,BL
         ret
XMS_ReAllocEMB endp

; ===========================================================================
XMS_FreeEMB proc far
; AX = Handle
; ===========================================================================
  MOV DX, AX ;
  MOV AH,XFreeEMB 
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  @MTEMB
  XOR BL,BL     ;      { No error }
@MTEMB:
  MOV AL,BL         ;  { Return result value }
  MOV XMSResult,BL ; { Store error code }
         ret
XMS_FreeEMB endp

; ===========================================================================
XMS_MoveFromEMB proc far
; AX    = Handle
; ES:DI = Seg:Ofs
; CX    = count
; ===========================================================================


         ret
XMS_MoveFromEMB endp

; ===========================================================================
XMS_MoveToEMB proc far
; AX    = Handle
; ES:SI = Seg:Ofs
; CX    = count
; ===========================================================================
         ret
XMS_MoveToEMB endp

; ===========================================================================
XMS_LockEMB proc far
; AX = Handle
; ===========================================================================
  push ax
  MOV AH,XLockEMB  
  pop  dx
  CALL cs:[PfEmmFns] 
  OR  AX,AX         ;  { Was the call successful? }
  JNZ @XMSLEMB2          
@XMSLEMB:
  XOR AX,AX
  XOR DX,DX     ;      { Return NIL }
  mov bl, xmsrLockFailed
  MOV XMSResult, bl
  JMP @XMSLEMB3
@XMSLEMB2:
  MOV AX,BX     ;      { Offset in AX, Segment in DX }
  mov bl, xmsrOk
  MOV XMSResult, bl
@XMSLEMB3:
        ret
XMS_LockEMB endp

; ===========================================================================
XMS_UnLockEMB proc far
; AX = Handle
; ===========================================================================
  push ax
  MOV AH,XUnlockEMB
  pop dx
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  @ULEMB
  XOR BL,BL
@ULEMB:
  MOV AL,BL
  MOV XMSResult,BL
        ret
XMS_UnLockEMB endp

; ===========================================================================
XMS_EMBHandlesAvail proc far
; AX = Handle
; ===========================================================================
  MOV DX, AX ; Handle
  MOV AH,XGetHandleInfo 
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  @EMBHA
  MOV AL,BL            ; { Save number of free handles }
  XOR BL,BL
@EMBHA:
  MOV XMSResult, BL
        ret
XMS_EMBHandlesAvail endp

; ===========================================================================
XMS_EMBLockCount proc far
; AX = Handle
; ===========================================================================
  push ax
  MOV AH,XGetHandleInfo
  pop  dx  ; MOV DX,Handle   
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  EMBLOCKCNO
  MOV AL,BH      ;       { Save lock count }
  XOR BL,BL
EMBLOCKCNO:
  MOV XMSResult,BL
        ret
XMS_EMBLockCount endp

; ===========================================================================
XMS_EMBSize proc far
; AX = Handle
; ===========================================================================
  push ax
  MOV AH,XGetHandleInfo
  pop  dx   ; MOV DX,Handle
  CALL cs:[PfEmmFns]
  OR  AX,AX
  JZ  XMSEMBSNO
  MOV AX,DX       ;      { Save EMB size in K-bytes }
  XOR BL,BL
XMSEMBSNO:
  MOV XMSResult,BL
        ret
XMS_EMBSize endp

; ===========================================================================
XMS_AllocUMB proc far
; AX = Size
; Out: AX = NZ if error
; ===========================================================================
  MOV DX, AX ; Size        ; { Number of paragraphs we want }
  MOV AH,XRequestUMB 
  CALL cs:[PfEmmFns] 
  OR  AX,AX
  JZ  xmsAllocUMBno
  MOV AX,BX    ;       { Return segment of UMB in low-order word }
               ;       { Actual block size in high-order word }
  XOR BL,BL
xmsAllocUMBno:
  MOV XMSResult,BL

        ret
        XMSAllocUMBS dw ?
XMS_AllocUMB endp

; ===========================================================================
XMS_FreeUMB proc far
; AX = Segment
; Out: AX = NZ if error
; ===========================================================================
  mov cs:XMSFreeUMBS, ax
  mov bl, xmsfound
  cmp bl, 1
  jne xmsFreeUMBno

  MOV AH,XReleaseUMB  
  MOV DX,cs:XMSFreeUMBS     ; { Segment of UMB to release }
  CALL cs:[PfEmmFns]  
  OR  AX,AX
  JZ  xmsFreeUMBno
  XOR BL,BL
xmsFreeUMBno:
  MOV AL,BL
  MOV XMSResult,BL

        ret
        XMSFreeUMBS dw ?
XMS_FreeUMB endp

; ===========================================================================
XMS_GetErrorMsg proc far
; AX = ErrorCode
; Out: DS:SI = string\0 
; ===========================================================================
    _echo 'X'
    _echo 'M'
    _echo 'S'
    _echo ' '
    _echo 'W'
    _echo '#'
    call putnum
    ret
XMS_GetErrorMsg endp

xmsendcode    db 'X'
xmsendblock   EQU xmsendcode - xmsfound

