code	segment word public
assume	cs:code
include errno.inc
; Memory allocation routines.
; ---------------------------
;
; Memory is allocated by pointer rather than segment: these routines use
; DI as a pointer into a constant segment, thus there is a communal
; maximum limit of slightly less than 64k.
;
; Note: DS should always point to the same constant segment and is assumed to
;       be valid in these routines
;
; Memory control block structure:
;
; offs  type  description
; ----  ----  ----------------------
;  0    byte  signature "R"
;  1    byte  status - "F" free  "A" allocated
;  2    word  size of block in bytes
;  4    word  pointer: previous MCB (0=first block)
;  6    word  pointer: forward MCB (0=last block)
;  8

extrn _startofdynamic:near
extrn errno:word

public _malloc
_malloc:
;    BX = number of bytes to allocate
; returns:
;    DS:DI -> memory block
        push  bp
        mov   bp,sp
        push  bx si di
        mov   word ptr ds:[errno],0
        mov   bx,[bp+4]
        test  bx,1
        jz    L1
        inc   bx
L1:
        mov   di,offset _startofdynamic
L2:
        cmp   byte ptr [di],'R'
        jnz   L9                   ; error!!! pointing to invalid block
        cmp   byte ptr [di+1],'F'  ; free?
        jz    L4
L3:
        mov   di,[di+6]            ; point to next block
        or    di,di                ; null?
        jnz   L2                   ; nup, continue loop
; if we drop down here we're at the last block and therefore out of
; memory. Crash!
        mov   word ptr ds:[errno],ERR_MEM_NOFREE       ; out of memory
        jmp   errexit
L4:
        cmp   [di+2],bx            ; is the block big enough?
        jb    L3                   ; nup
        mov   word ptr [di],'AR'   ; set to allocated status
        mov   [di+2],bx            ; size of block
        lea   si,[di+bx+16]        ; SI temp. 16 is merely a nice number
        cmp   si,[di+6]            ; is there room to create new MCB?
        ja    L5                   ; nope: leave as-is

;       /~~~~~~~~~~~~~~~~~~~~~~~~\        old link
;      |                          |
;   previous MCB---new MCB---forward MCB  new links

        lea   si,[di+bx+8]         ; point to new MCB
        mov   word ptr [si],'FR'   ; sig+free
        mov   ax,[di+6]            ; pointer to forward MCB
        sub   ax,si                ; difference from new MCB
        sub   ax,8                 ; less space for previous+new MCB
; ***
        mov   [si+2],ax            ; AX contains new block size
        mov   [si+4],di            ; point to last MCB
        mov   ax,[di+6]
        mov   [si+6],ax            ; pointer to forward MCB
; New MCB has now been filled
        mov   bx,[di+6]
        mov   [bx+4],si

        mov   [di+6],si            ; previous MCB -> new MCB
L5:
        lea   ax,[di+8]            ; point to actual memory
funcexit:
        pop   di si bx
        pop   bp
        ret   2
L9:
        mov   word ptr ds:[errno],ERR_MEM_CORRUPT   ; corrupt MCB
errexit:
        mov   ax,-1
        jmp   funcexit

code ends
end
