home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 19
/
CD_ASCQ_19_010295.iso
/
dos
/
prg
/
midas
/
ems.asm
< prev
next >
Wrap
Assembly Source File
|
1994-08-06
|
24KB
|
907 lines
;* EMS.ASM
;*
;* EMS heap manager, v1.00
;*
;* Copyright 1994 Petteri Kangaslampi and Jarno Paananen
;*
;* This file is part of the MIDAS Sound System, and may only be
;* used, modified and distributed under the terms of the MIDAS
;* Sound System license, LICENSE.TXT. By continuing to use,
;* modify or distribute this file you indicate that you have
;* read the license and understand and accept it fully.
;*
P386
IDEAL
JUMPS
INCLUDE "lang.inc"
INCLUDE "errors.inc"
INCLUDE "ems.inc"
INCLUDE "mmem.inc"
DATASEG
emsHandles DD ? ; pointer to first EMS handle
emsSaveArray DD ? ; EMS Page Map Save Array pointer
emsPageFrame DW ? ; EMS Page Frame segment
emsIsSafe DW ? ; emsSafe flag
emsMapped DW 4 dup (?) ; from which handle are pages
; mapped
emsMemPtr DD ? ; temporary pointer used by EMS
; Heap Manager functions
IDATASEG
EMMname DB "EMMXXXX0" ; EMM device name
EMMNAMELEN = 8 ; length of EMM device name
CODESEG
;/***************************************************************************\
;*
;* Function: int emsInit(int *emmOK);
;*
;* Description: Initializes EMS heap. Must be called before other EMS heap
;* manager functions.
;*
;* Input: int *emmOK pointer to variable containing EMM
;* status
;*
;* Returns: MIDAS error code.
;* *emmOK contains 1 if Expanded Memory Manager was found (EMS
;* initialized succesfully) or 0 if not. Note that the lack
;* of Expanded Memory Manager is _not_ an error.
;*
;\***************************************************************************/
PROC emsInit FAR emmOK : dword
USES si,di
cld
mov ax,3567h ; get EMM interrupt vector
int 21h
mov bx,0Ah
mov cx,EMMNAMELEN ; offset 0Ah of the interrupt vector
xor si,si ; si = offset to EMM driver name str
; offset 0Ah in the EMM interrupt vector segment should contain the
; driver name, or else it does not point to a valid EMM
@@cmp:
mov al,[EMMname+si] ; get character from string
inc si
cmp [es:bx],al ; compare it to EMM driver string
jne @@noemm ; if different, there is no EMM
inc bx
loop @@cmp
mov ah,40h ; EMS Get Status function
int 67h
test ah,ah ; non-zero status means error
jnz @@emmerr
mov ah,41h ; EMS Get Page Frame function
int 67h
test ah,ah ; non-zero status means error
jnz @@emmerr
mov [emsPageFrame],bx ; save Page Frame segment address
mov ax,4E03h ; EMS Get Size of Page Map Save Array
int 67h
test ah,ah ; non-zero status means error
jnz @@emmerr
; allocate memory for EMS Save Array:
call memAlloc LANG, ax, seg emsSaveArray offset emsSaveArray
test ax,ax
jnz @@err
mov [emsHandles],0 ; no EMS handles allocated
mov [emsIsSafe],0
les bx,[emmOK] ; store 1 in *emmOK to mark that EMS
mov [word es:bx],1 ; can be used
xor ax,ax ; success
jmp @@done
@@noemm:
les bx,[emmOK] ; store 0 in *emmOK to mark that EMS
mov [word es:bx],0 ; can not be used
xor ax,ax ; success
jmp @@done
@@emmerr:
; Expanded Memory Manager internal error:
mov ax,errEMMFailure
@@err:
ERROR ID_emsInit
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsClose(void);
;*
;* Description: Uninitializes EMS heap freeing all allocated blocks. Must be
;* called before program exits if emsInit() has been called.
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsClose FAR
USES si
@@free:
cmp [emsHandles],0 ; any EMS handles allocated?
je @@empty
les bx,[emsHandles]
lgs si,[es:bx+emsHandle.block] ; point gs:si to first block
@@fblk: cmp [gs:si+emsBlock.used],1 ; is block used?
je @@dealloc ; if is, deallocate it
cmp [gs:si+emsBlock.next],0 ; is there a next block?
je @@err ; if not, heap is corrupted
lgs si,[gs:si+emsBlock.next] ; point gs:si to next block
jmp @@fblk
@@dealloc:
push gs
call emsFree LANG, gs si ; free this block
pop gs
test ax,ax
jnz @@err
jmp @@free ; continue deallocating blocks
@@empty:
; deallocate Page Map Save Array:
call memFree LANG, [emsSaveArray]
test ax,ax
jnz @@err
xor ax,ax ; success
jmp @@done
@@err:
ERROR ID_emsClose
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsAlloc(ushort bytes, emsBlock **ems);
;*
;* Description: Allocates an EMS memory block
;*
;* Input: ushort bytes number of bytes to be allocated
;* emsBlock **ems Pointer to EMS Block pointer
;*
;* Returns: MIDAS error code.
;* EMS block pointer stored in *ems, NULL if failure
;*
;\***************************************************************************/
PROC emsAlloc FAR bytes : word, ems : dword
USES si, di
LOCAL handle : dword
cmp [emsHandles],0 ; are there any EMS handles allocated?
je @@newhandle ; if not, allocate
mov cx,[bytes]
lgs si,[emsHandles] ; point gs:si to first handle
@@hlp:
les bx,[gs:si+emsHandle.block] ; point es:bx to first block
; in handle
@@blp:
cmp [es:bx+emsBlock.used],0 ; is current block in use?
je @@unused
@@nextblk:
cmp [es:bx+emsBlock.next],0 ; is there a next block?
je @@lastblk
les bx,[es:bx+emsBlock.next] ; point es:bx to next block
jmp @@blp ; and continue searching
@@unused:
cmp [es:bx+emsBlock.bytes],cx ; is this unused block long
jb @@nextblk ; long enough?
je @@blksame ; if same length, don't alloc
; a new block
; allocate memory for new block:
push cx bx es
call memAlloc LANG, SIZE emsBlock, seg emsMemPtr offset emsMemPtr
pop es bx cx
test ax,ax
jnz @@err
push ds
lds di,[emsMemPtr] ; point ds:di to new block
mov eax,[es:bx+emsBlock.next] ; add this new block after
mov [ds:di+emsBlock.next],eax ; the current empty block
lgs si,[es:bx+emsBlock.next]
mov [word gs:si+emsBlock.prev],di
mov [word gs:si+2+emsBlock.prev],ds
mov [word es:bx+emsBlock.next],di
mov [word es:bx+2+emsBlock.next],ds
mov [word ds:di+emsBlock.prev],bx
mov [word ds:di+2+emsBlock.prev],es
mov eax,[es:bx+emsBlock.handle] ; copy EMS handle pointer
mov [ds:di+emsBlock.handle],eax
mov [ds:di+emsBlock.used],0 ; new block is not used
mov ax,[es:bx+emsBlock.addr] ; address of this block is
add ax,cx ; address of the previous
mov [ds:di+emsBlock.addr],ax ; + bytes
mov ax,[es:bx+emsBlock.bytes]
sub ax,cx ; decrease number of bytes
mov [ds:di+emsBlock.bytes],ax
mov [es:bx+emsBlock.bytes],cx ; size of current block is
mov [es:bx+emsBlock.used],1 ; [bytes] and it is in use
pop ds
jmp @@ok ; block allocated
@@blksame:
mov [es:bx+emsBlock.used],1 ; block sizes are the same
jmp @@ok ; - only used status needs
; to be changed.
@@lastblk:
mov ax,[es:bx+emsBlock.addr] ; address in handle
add ax,[es:bx+emsBlock.bytes] ; plus number of bytes
neg ax ; negated == bytes free
cmp ax,cx ; enough space after this blk?
jae @@addblk ; if is, add new block here
cmp [gs:si+emsHandle.next],0 ; is there a next handle?
je @@newhandle ; if not, allocate new
lgs si,[gs:si+emsHandle.next] ; continue searching for space
jmp @@hlp ; from next handle
@@addblk:
; allocate memory for new block:
push cx bx es
call memAlloc LANG, SIZE emsBlock, seg emsMemPtr offset emsMemPtr
pop es bx cx
test ax,ax
jnz @@err
push ds
lds di,[emsMemPtr] ; point ds:di to new block
mov ax,[es:bx+emsBlock.addr] ; address of new block
add ax,[es:bx+emsBlock.bytes]
mov [ds:di+emsBlock.addr],ax
mov [ds:di+emsBlock.bytes],cx ; number of bytes in new block
mov [ds:di+emsBlock.next],0 ; this is the last block
mov [word ds:di+emsBlock.prev],bx ; previous block is the
mov [word ds:di+2+emsBlock.prev],es ; current one
mov [word es:bx+emsBlock.next],di ; next block to current one is
mov [word es:bx+2+emsBlock.next],ds ; the new one
mov [ds:di+emsBlock.used],1 ; new block is in use
mov eax,[es:bx+emsBlock.handle] ; new block has the same
mov [ds:di+emsBlock.handle],eax ; handle as the current one
mov bx,di
mov ax,ds ; return block pointer in
mov es,ax ; es:bx
pop ds
jmp @@ok
@@newhandle:
; allocate EMS pages:
call emsAllocPages LANG, seg emsMemPtr offset emsMemPtr
test ax,ax
jnz @@err
mov eax,[emsMemPtr]
mov [handle],eax
; allocate memory for EMS block:
call memAlloc LANG, SIZE emsBlock, seg emsMemPtr offset emsMemPtr
test ax,ax
jnz @@err
les bx,[emsMemPtr] ; point es:bx to new block
mov [es:bx+emsBlock.addr],0 ; in the beginning of handle area
mov ax,[bytes]
mov [es:bx+emsBlock.bytes],ax
mov [es:bx+emsBlock.next],0 ; no next block
mov [es:bx+emsBlock.prev],0 ; no previous block
mov [es:bx+emsBlock.used],1 ; block is in use
mov eax,[handle]
mov [es:bx+emsBlock.handle],eax
lgs si,[handle]
mov [word gs:si+emsHandle.block],bx ; add block to handle
mov [word gs:si+2+emsHandle.block],es
@@ok: ; es:bx points to allocated EMS block
lgs si,[ems]
mov [word gs:si],bx ; store EMS block pointer to *ems
mov [word gs:si+2],es
xor ax,ax ; success
jmp @@done
@@err:
les bx,[ems]
mov [dword es:bx],0 ; store NULL to *ems
ERROR ID_emsAlloc
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsFree(emsBlock *ems);
;*
;* Description: Deallocates an EMS block allocated with emsAlloc
;*
;* Input: emsBlock *ems pointer to block to be deallocated
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsFree FAR ems : dword
LOCAL handle : dword
USES si,di
les bx,[ems]
mov eax,[es:bx+emsBlock.handle] ; save handle pointer
mov [handle],eax
cmp [es:bx+emsBlock.next],0 ; the last block of handle?
je @@last
mov [es:bx+emsBlock.used],0 ; current block is not in use
; now combine all unused blocks that are after another
@@combfree:
lgs si,[handle] ; point gs:si to handle
les bx,[gs:si+emsHandle.block] ; point es:bx to first block
@@cunused:
cmp [es:bx+emsBlock.used],0 ; is block unused?
jne @@nextb
lgs si,[es:bx+emsBlock.next] ; point gs:si to next block
cmp [gs:si+emsBlock.used],0 ; is next block unused?
jne @@nextb ; if not, move on
@@comb:
; block pointed by es:bx is free and so is the next one, gs:si
mov ax,[gs:si+emsBlock.bytes] ; increase the length of
add [es:bx+emsBlock.bytes],ax ; current block
mov eax,[gs:si+emsBlock.next]
mov [es:bx+emsBlock.next],eax
or eax,eax
jz @@nonext
push ds ; remove block pointed by
lds di,[gs:si+emsBlock.next] ; gs:si from list
mov [word ds:di+emsBlock.prev],bx
mov [word ds:di+2+emsBlock.prev],es
pop ds
@@nonext:
push gs es bx
call memFree LANG, gs si ; deallocate block
pop bx es gs
test ax,ax
jnz @@err
cmp [es:bx+emsBlock.next],0 ; is there a next block?
je @@chkfree
lgs si,[es:bx+emsBlock.next] ; point gs:si to next block
cmp [gs:si+emsBlock.used],0 ; is next block used?
je @@comb ; if not, combine to this one
@@nextb:
cmp [es:bx+emsBlock.next],0 ; is there a next block?
je @@chkfree ; if not, we are finished
les bx,[es:bx+emsBlock.next] ; point es:bx to next block
jmp @@cunused ; and continue
@@chkfree:
; all free blocks combined. Now check if there is only one free
; block in the handle
lgs si,[handle] ; point gs:si to handle
les bx,[gs:si+emsHandle.block] ; point es:bx to first block
cmp [es:bx+emsBlock.next],0 ; is this the last block?
jne @@ok
cmp [es:bx+emsBlock.used],0 ; is this block unused?
jne @@ok
; only one unused block in the handle - deallocate it.
push es bx
call emsFreePages LANG, gs si ; deallocate handle
pop bx es
test ax,ax
jnz @@err
call memFree LANG, es bx ; deallocate block structure
test ax,ax
jnz @@err
jmp @@ok
@@last:
cmp [es:bx+emsBlock.prev],0 ; the only block of handle?
je @@only
les bx,[es:bx+emsBlock.prev] ; point es:bx to prev block
mov [es:bx+emsBlock.next],0 ; no next block
call memFree LANG, [ems] ; deallocate block
test ax,ax
jnz @@err
jmp @@chkfree ; check if handle should be
; deallocated
@@only:
call emsFreePages LANG, [es:bx+emsBlock.handle] ; free handle
test ax,ax
jnz @@err
call memFree LANG, [ems] ; free current block
test ax,ax
jnz @@err
@@ok:
xor ax,ax ; success
jmp @@done
@@err:
ERROR ID_emsFree
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsMap(emsBlock *ems, void **memPtr);
;*
;* Description: Maps an EMS block to conventional memory.
;*
;* Input: emsBlock *ems pointer to block to be mapped
;* void **memPtr pointer to conventional memory ptr
;*
;* Returns: MIDAS error code.
;* Pointer to the conventional memory area where the block
;* was mapped is stored in **memPtr, NULL if failure.
;*
;\***************************************************************************/
PROC emsMap FAR ems : dword, memPtr : dword
USES si,di
les di,[ems] ; point es:di to EMS block
lgs si,[es:di+emsBlock.handle] ; point gs:si to EMS handle
mov dx,[gs:si+emsHandle.handle] ; handle number
mov cx,[es:di+emsBlock.addr] ; cx = first page to map
shr cx,14
mov si,[es:di+emsBlock.addr]
add si,[es:di+emsBlock.bytes] ; si = last page to map
shr si,14
@@mlp: cmp [emsIsSafe],1 ; is safety-flag on?
jne @@map ; if not, always map the page
mov bx,cx ; if is, check if this page
shl bx,1 ; is already mapped
cmp [emsMapped+bx],dx
je @@mapped
mov [emsMapped+bx],dx ; if not, map it normally and
; mark mapped
@@map:
mov al,cl ; al = physical page number
mov bx,cx ; bx = logical page number
mov ah,44h ; EMS Map Handle Pages funct
int 67h
or ah,ah
jnz @@emmerr
@@mapped:
inc cx ; map next page
cmp cx,si ; should next page still be
jbe @@mlp ; mapped?
mov ax,[es:di+emsBlock.addr]
les bx,[memPtr] ; store pointer to the conventional
mov [es:bx],ax ; memory area to *memPtr
mov ax,[emsPageFrame]
mov [es:bx+2],ax
xor ax,ax ; success
jmp @@done
@@emmerr:
; EMM mapping function failed
mov ax,errEMMFailure
@@err:
ERROR ID_emsMap
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsSave(void);
;*
;* Description: Saves the EMS status. To be used by TempoTimer. Can only be
;* called once.
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsSave FAR
USES di
mov ax,4E00h ; EMS Get Page Map subfunction
les di,[emsSaveArray] ; pointer to Page Map Save Array
int 67h
or ah,ah ; nonzero status means error
jnz @@emmerr
xor ax,ax ; success
jmp @@done
@@emmerr:
; EMM Save function failed
mov ax,errEMMFailure
@@err:
ERROR ID_emsSave
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsRestore(void);
;*
;* Description: Restores EMS status saved with emsSave(). To be used by
;* TempoTimer. Can only be called once.
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsRestore FAR
USES ds, si
mov ax,4E01h ; EMS Set Page Map subfunction
lds si,[emsSaveArray] ; point ds:si to Page Map Save Array
int 67h
or ah,ah ; nonzero status means error
jnz @@emmerr
xor ax,ax ; success
jmp @@done
@@emmerr:
; EMM Restore function failed
mov ax,errEMMFailure
@@err:
ERROR ID_emsRestore
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsAllocPages(emsHandle **emsh);
;*
;* Description: Allocate 4 pages of EMS memory to a handle. Used internally
;* by EMS heap manager.
;*
;* Returns: MIDAS error code.
;* Pointer to a emsHandle structure for the pages stored in
;* *emsh, NULL if failure.
;*
;\***************************************************************************/
PROC emsAllocPages FAR emsh : dword
USES si
LOCAL handle : dword
; allocate memory for handle structure:
call memAlloc LANG, size emsHandle, seg emsMemPtr offset emsMemPtr
test ax,ax
jnz @@err
les bx,[emsMemPtr] ; point es:bx to handle structure
mov [word handle],bx ; store handle pointer in handle
mov [word handle+2],es
mov [es:bx+emsHandle.handle],0
mov [es:bx+emsHandle.block],0
mov [es:bx+emsHandle.next],0
push bx
mov ah,43h ; EMS Allocate Pages function
mov bx,4 ; allocate 4 pages (64k)
int 67h
pop bx
or ah,ah ; successful if status is zero
jz @@allocok
; check if the cause of the error was lack of EMS. If so, return
; errOutOfEMS, otherwise errEMMFailure
cmp ah,85h ; out of EMS handles?
je @@outofems
cmp ah,87h ; not enough EMS pages?
je @@outofems
cmp ah,88h ; not enough unallocated pages?
je @@outofems
jmp @@emmerr
@@outofems:
mov ax,errOutOfEMS ; out of EMS memory
jmp @@err
@@allocok:
mov [es:bx+emsHandle.handle],dx ; store EMS handle number
cmp [emsHandles],0 ; first EMS handle?
jne @@notfirst
mov [word emsHandles],bx ; point emsHandles to this page as
mov [word emsHandles+2],es ; it is the first
mov [es:bx+emsHandle.prev],0 ; no previous handle (first)
jmp @@prevset
@@notfirst:
push ds
lds si,[emsHandles] ; start searching for last handle
@@flast:
cmp [ds:si+emsHandle.next],0 ; is this the last handle?
je @@last
lds si,[ds:si+emsHandle.next] ; next handle
jmp @@flast
@@last:
mov [word ds:si+emsHandle.next],bx ; ds:si now points to
mov [word ds:si+2+emsHandle.next],es ; last handle which
mov [word es:bx+emsHandle.prev],si ; is the previous for
mov [word es:bx+2+emsHandle.prev],ds ; this one
pop ds
@@prevset:
les bx,[emsh]
mov eax,[handle] ; store handle pointer in *emsh
mov [es:bx],eax
xor ax,ax ; success
jmp @@done
@@emmerr:
mov ax,errEMMFailure ; Expanded Memory Manager failure
@@err:
ERROR ID_emsAllocPages
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsFreePages(emsHandle *handle);
;*
;* Description: Deallocates an EMS handle allocated by emsAllocPages(). Used
;* internally by EMS heap manager.
;*
;* Input: emsHandle *handle pointer to handle to be deallocated.
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsFreePages FAR handle : dword
USES si
les bx,[handle] ; point es:bx to handle structure
cmp [es:bx+emsHandle.prev],0 ; first handle?
je @@first
push ds
mov eax,[es:bx+emsHandle.next] ; eax = pointer to next handle
lds si,[es:bx+emsHandle.prev] ; point ds:si to prev handle
mov [ds:si+emsHandle.next],eax ; prev->next = this->next
pop ds
or eax,eax ; is this the last handle?
jz @@dealloc
push ds
mov eax,[es:bx+emsHandle.prev] ; eax = pointer to prev handle
lds si,[es:bx+emsHandle.next] ; point ds:si to next handle
mov [ds:si+emsHandle.prev],eax ; next->prev = this->prev
pop ds
jmp @@dealloc
@@first:
mov eax,[es:bx+emsHandle.next] ; set emsHandles to next
mov [emsHandles],eax ; handle (might be 0)
or eax,eax ; is there a next handle?
jz @@fnn
lgs si,[es:bx+emsHandle.next] ; if is, its previous handle
mov [gs:si+emsHandle.prev],0 ; is now zero (it's the first)
@@fnn:
@@dealloc:
mov ah,45h ; EMS Deallocate Pages function
mov dx,[es:bx+emsHandle.handle] ; EMS handle number
int 67h
test ah,ah ; failure if nonzero status
jnz @@emmerr
call memFree LANG, es bx ; deallocate handle structure memory
test ax,ax
jnz @@err
xor ax,ax ; success
jmp @@done
@@emmerr:
mov ax,errEMMFailure ; Expanded Memory Manager failure
@@err:
ERROR ID_emsFreePages
@@done:
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsSafe(void);
;*
;* Description: Sets the EMS safety flag on so that the EMS heap manager
;* can optimize page mappings. Until emsStopSafe() is restored,
;* no other routine than emsMap() must touch the EMS page
;* mappings
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsSafe FAR
mov [emsIsSafe],1 ; it is now safe to assume that EMS
xor bx,bx ; mappings remain untouched by other
mov cx,4 ; routines.
mov ax,-1
@@lp: mov [emsMapped+bx],ax ; clear mapping status table to
add bx,2 ; indicate that no pages are mapped
loop @@lp
xor ax,ax ; always successful
ret
ENDP
;/***************************************************************************\
;*
;* Function: int emsStopSafe(void);
;*
;* Description: Sets the EMS safety flag off.
;*
;* Returns: MIDAS error code
;*
;\***************************************************************************/
PROC emsStopSafe FAR
mov [emsIsSafe],0 ; it is no longer safe to assume that
; EMS mappings remain untouched
xor ax,ax ; always successful
ret
ENDP
END