
        include \nxdos\src\mem\ems.asm
        include \nxdos\src\mem\xms.asm




; MEM PKT STRUCTURE OFFSETS
mem_pkts     equ 50
MEM_PKT      dw  ? 
_MEM_PKT_FWD EQU 0
_MEM_PKT_REV EQU 2
_MEM_PKT_FLG EQU 3
_MEM_BLK_SZ  EQU 4
_MEM_LOC     EQU 8
mem_pkt_fwd dw ?                                         ; -1, end of chain
mem_pkt_rev dw ?                                         ;  0, start of chain
mem_pkt_flg db ?
; 80H, pkt and mem in use
; 40H, pkt and mem available to be allocated
; 03H, memory type, 1=dos, 2=xms, 3=ems
mem_blk_sz  dd ?                                         ; bytes
mem_loc     dd ?                    ; DOS=absolute, XMS=index, EMS=index,page
mem_pkts_sz equ 13

dos_chain	dw	0		;ptr to dos chain (0=no dos chain)
xms_chain	dw	0		;ptr to xms chain (0=no xms chain)
ems_chain	dw	0		;ptr to ems chain (0=no ems chain)


; expanded memory data base


current_logical_page	dw	-1

pkt_area    db (mem_pkts_sz*mem_pkts) dup(0)        ; All packets reside here

; ===========================================================================
find_memory proc far
; find_memory - scan one chain to look for free memory
;  inputs:  ds:bx - points to first packet on chain
;           dx,ax - number of bytes to allocate
;  output:  no carry, bx=packet with memory
;           carry = insufficient memory
; ===========================================================================
_fm:    cmp     bx,0
	je	fm_failure			;jmp if no chain
        test    [bx._mem_pkt_flg],040h               ;check if avail. memory here
	jz	fm_next_pkt			; jmp if memory in-use
        cmp     dx,word ptr [bx._mem_blk_sz+2]   ;check high word
	jb	fm_got_pkt			;jmp if sufficient mem in pkt
	ja	fm_next_pkt			;jmp if insufficient mem in pkt
        cmp     ax,word ptr [bx._mem_blk_sz]     ;check low word
	jbe	fm_got_pkt			;jmp if memory found
fm_next_pkt:
        mov     bx,[bx._mem_pkt_fwd]                 ;get next packet
	cmp	bx,-1
        jne     _fm                             ;jmp if more packets to check
fm_failure:	
	stc
	jmp	fm_exit				;no memory exit
fm_got_pkt:
	clc
fm_exit:
	ret		
find_memory endp

; ===========================================================================
memopen proc
; Input: AX = SIZE of buffer
;        BX = Strategy 0=Normal, 1=DOS
; ===========================================================================
        mov  msize, ax
        mov  mstrategy, bx
        xor  ax, ax
        mov  al, mopened
        or   al, al
        jnz  opened
; else open and initialize the memory manager
        call meminit
memopened:
; alloc msize block
        mov  bx, mstrategy
        cmp  bx,0
        je   normal_allocation
        cmp  bx,1
        je   dos_allocation
	stc
        jmp  memopenexit     ; jmp if -bx- not set correctly
normal_allocation:
	mov	bx,ems_chain
	call	find_memory			;check if any ems memory
	jnc	ma_got_pkt			; jmp if ems memory found

	mov	bx,xms_chain
	call	find_memory			;check if any xms memory
	jnc	ma_got_pkt			; jmp if xms memory found

	mov	bx,dos_chain
	call	find_memory			;check if any dos memory
	jc	ma_failure			; jmp if no dos memory
dos_allocation:
        mov     bx, dos_chain
	call	find_memory			;check if any DOS memory
	jnc	ma_got_pkt			;jmp if memory found
	jmp	ma_failure			;jmp if no dos memory
ma_got_pkt:      ; we have a packet with enough memory, check if exact match
        cmp     dx,word ptr [bx._mem_blk_sz+2]
	jne	ma_extract_mem
        cmp     ax,word ptr [bx._mem_blk_sz]
	je	ma_exit1			;jmp if exact match

ma_failure:

memopenexit:
        ret
msize   dw ? 
memopen endp


; ===========================================================================
mopenblock proc far                                         ; open a block for use
; ===========================================================================
dos_allocation:
normal_allocation:

ma_extract_mem:	
	mov	si,bx
; -si- points to a packet with memory, strip out some for the caller
; -ax,dx- has amount of memory requested by caller
;        call    get_free_pkt                    ;returns pkt in -bx-
	jc	ma_failure			;jmp if out of packets
; remove memory from the mother packet
        sub     word ptr [si._mem_blk_sz],ax
        sbb     word ptr [si._mem_blk_sz+2],dx
;
        mov     cl,[si._mem_pkt_flg]                 ;copy flg data
        mov     [bx._mem_pkt_flg],cl

        mov     word ptr [bx._mem_blk_sz],ax     ;put size in new pkt
        mov     word ptr [bx._mem_blk_sz+2],dx
; compute address for new packet
;   dx,ax=memory allocated  si=mother pkt  bx=new packet
;
; Note: we must take the memory from the end of the mother packet, so that
;	insert_mem_packet never has to insert at front of chain.
        mov     ax,word ptr [si._mem_loc]        ;get mother pkt's location
        mov     dx,word ptr [si._mem_loc+2]

        add     ax,word ptr [si._mem_blk_sz]        ;compute end of mother's
        adc     dx,word ptr [si._mem_blk_sz+2]      ;  memory (start of new pkt)

        mov     word ptr [bx._mem_loc],ax        ;store new pkt's
        mov     word ptr [bx._mem_loc+2],dx      ;  memory location

;        call    insert_mem_packet
ma_exit1:
        and     [bx._mem_pkt_flg],3fh                        ;remove flags from pkt
        or      [bx._mem_pkt_flg],80h                        ;set in-use flag
	clc
	jmp	ma_exit
ma_failure:
mof:    stc
ma_exit:
        ret
mopenblock   endp

; ===========================================================================
malloc proc far
; inputs:     dx,ax=number of bytes to allocate, dx=high word ax=low word
;             bx=allocation method, 0=normal allocation
;                                   1=only allocate DOS memory

; output:  no carry = success, ax=total 1k blocks allocated
;                     bx=memory mgr handle, use to read/write/release
;          carry    = error/warning, if ax=7 DOS memory was not found.
;                                       ax=2 fatal programming error
;                     insufficient memory, or too many allocations active
;                     or -bx- not set correctly
; processing: 1. determine memory available and setup database
;             2. allocate all available memory
; ===========================================================================
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es
	cld
	mov	ax,cs
	mov	ds,ax
	mov	es,ax

	cmp	dos_initial_sz,0
	je	mop_1			;jmp if first entry
        call    mopen
        jnc     mopp 
mop_f: 	mov	ax,2
	stc
mopp:   jmp     mop_exit                ;jmp if memory already allocated
; clear the packet area
mop_1:	mov	di,offset pkt_area
	mov	cx,(size mem_pkt)*mem_pkts
	cld
	mov	al,0
	rep	stosb
; check if any DOS memory is available, and if not return warning
	mov	bx,-1
	mov	ah,48h
	int	21h			;try to allocate impossible amount
; we should have the carry set here (error) and -bx- has maximum amount
; of memory available.
	cmp	bx,0			;check if any memory is available
	je	mop_error7		;jmp if no memory
; When using PS (periscope) it takes all of DOS memory, so we need to
; debug by using PSKEY and not RUN.COM.
	mov	dos_initial_sz,bx	
	mov	ah,48h
	int	21h			;now allocate the maximum amount.
	jnc	mop_dos_setup		;jmp if DOS memory allocated ok
mop_error7:	
	mov	ax,7			;get error code
	stc
	jmp	mop_exit
; build one DOS packet with all available memory
mop_dos_setup:
	mov	dos_initial_seg,ax
	call	get_free_pkt		;returns free pkt ptr -bx-
	mov	dos_chain,bx
        mov     [bx._mem_pkt_fwd],-1         ;end of chain
        mov     [bx._mem_pkt_bak],0          ;start of chain
        mov     [bx._mem_pkt_flg],41h        ;pkt has memory, type=DOS
	mov	ax,dos_initial_sz
	call	seg_to_abs
	mov	word ptr [bx.mem_blk_sz],ax
	mov	word ptr [bx.mem_blk_sz+2],dx

	mov	ax,dos_initial_seg
	call	seg_to_abs
	mov	word ptr [bx.mem_loc],ax	;store abs loc
	mov	word ptr [bx.mem_loc+2],dx	; in packet
; check if any XMS memory is available, and setup database if it is
check_for_xms:
	mov	ax,4300h
	int	2fh
	cmp	al,80h		;check if driver found
	jne	check_ems	;jmp if no xms memory present
;
	mov	ax,4310h
	int	2fh		;request control address
	mov	word ptr xms_control,bx
	mov	word ptr xms_control+2,es
; request size of largest block in -ax- (k bytes)
	mov	ah,8
	call	xms_control	;returns ax=largest k-blk  dx=total k-blks
	cmp	ax,0
	je	check_ems	;jmp if no xms available
; allocate largest xms block
mop_lp:	mov	dx,ax		;put largest block in -dx-
	mov	xms_initial_sz,ax
	mov	ah,9
	call	xms_control
	cmp	ax,01
	je	mop_2		;jmp if allocation sucessful
; xms will not allocated this block size, reduce size and retry
	mov	ax,xms_initial_sz
	shr	ax,1
	cmp	ax,0
	jne	mop_lp		;jmp if reduced allocation greater than zero
	mov	ax,2
	stc
	jmp	mop_exit
mop_2:	mov	xms_from_read_handle,dx
	mov	xms_to_write_handle,dx
	mov	xms_handle,dx
; setup the XMS packet with all available memory
	call	get_free_pkt		;returns free pkt ptr -bx-
	mov	xms_chain,bx
	mov	[bx.mem_fwd],-1		;end of chain
	mov	[bx.mem_bak],0		;start of chain
	mov	[bx.mem_flg],42h	;pkt has memory, type=XMS

	mov	ax,xms_initial_sz	;get size in 1k blocks
	mov	dx,1024			;size of 1k blk
	mul	dx
	mov	word ptr [bx.mem_blk_sz],ax	  ;save size
	mov	word ptr [bx.mem_blk_sz+2],dx	;save size

	mov	word ptr [bx.mem_loc],0		;store relative loc
	mov	word ptr [bx.mem_loc+2],0	; in packet
; check if any EMS memory is available, and setup database if it is
check_ems:
	call	CHECK_FOR_EMS
	jc	mop_exit1		;jmp if no ems avail
	mov	ems_frame,bx		;save page frame
	
	mov	ah,42h			;get avail. page count
	int	67h
	or	ah,ah
	jz	mop_3			;jmp if no error
	mov	ax,2
	stc
	jmp	mop_exit
; allocate ems pages, bx=unallocated page count
mop_3:	mov	ems_initial_sz,bx	;save # 16k pages avail.
	cmp	bx,0
	je	mop_exit1		;jmp if no ems pages avail.
	mov	ah,43h			;allocate pages request code
	int	67h
	cmp	ah,0
	je	mop_4			;jmp if success
; try to allocate reduced amount
	shr	bx,1
	jmp	mop_3	
; setup the EMS packet with all available ems memory
mop_4:	mov	ems_handle,dx		;save ems handle
	call	get_free_pkt		;returns free pkt ptr -bx-
	mov	ems_chain,bx
        mov     [bx._mem_pkt_fwd],-1         ;end of chain
        mov     [bx._mem_pkt_bak],0          ;start of chain
        mov     [bx._mem_pkt_flg],43h        ;pkt has memory, type=EMS
	mov	ax,ems_initial_sz
	mov	dx,1024*16		;16k page size
	mul	dx
        mov     word ptr [bx._mem_blk_sz],ax       ;save size
        mov     word ptr [bx._mem_blk_sz+2],dx   ;save size

        mov     word ptr [bx._mem_loc],0         ;store relative loc
        mov     word ptr [bx._mem_loc+2],0       ; in packet
; compute total amount of memory allocated in 1k blocks
mop_exit1:
	mov	ax,dos_initial_sz
	mov	cx,3
	shr	ax,cl
	push	ax
	mov	ax,ems_initial_sz
	mov	cx,3
	shl	ax,cl
	pop	bx
	add	ax,bx
	add	ax,xms_initial_sz
	clc
mop_exit:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
       ret
malloc endp
