18.5 Allocating Memory

Allocating memory is something that was never needed in the MP's up to this point. So why is it necessary to learn about it now? Primarily because we're going to start working with some really big (multi-megabyte) data such as images. Once a program's memory usage goes beyond a few kilobytes, it's smart to dynamically allocate memory at run time, and PModeLib provides a function to make this task much easier.

AllocMem() takes just a single parameter: Size, which specifies the number of bytes to allocate. It returns the starting offset of the newly allocated block, which you can use just like any other offset (such as to a variable). Generally it's a smart idea to store this offset in a variable, which adds a layer of indirection, but makes it easier to allocate and keep track of several memory blocks at once. There is no way to free memory once it's allocated; the memory is freed when the program exits.

Example 18-3. Allocating Memory

%include "lib291.inc"

GLOBAL _main

test1size equ 4*1024*1024       ; 4 MB
test2size equ 1*1024*1024       ; 1 MB

SECTION .bss    ; Uninitialized data

test1off        resd 1   ; stores offset of test1 data
test2off        resd 1   ; stores offset of test2 data

SECTION .text

_main
        push    esi              ; Save registers
        push    edi

        call    _LibInit         ; You could use invoke here, too
        test    eax, eax         ; Check for error (nonzero return value)
        jnz     near .initerror

        ; Allocate test1 memory block
        invoke  _AllocMem, dword test1size
        cmp     eax, -1          ; Check for error (-1 return value)
        je      near .error
        mov     [test1off], eax  ; Save offset in variable

        ; Allocate test2 memory block
        invoke  _AllocMem, dword test2size
        cmp     eax, -1          ; Check for error (-1 return value)
        je      near .error
        mov     [test2off], eax  ; Save offset in variable

        ; Fill the test1 block with 0's.
        ; We don't need to set es=ds, because it's that way at start.
        xor     eax, eax         ; Fill with 0
        mov     edi, [test1off]  ; Starting address (remember indirection)
        mov     ecx, test1size/4 ; Filling doublewords (4 bytes at a time)
        rep stosd                ; Fill!

        ; Copy from last meg of test1 to test2
        mov     esi, [test1off]  ; Starting address of source
        add     esi, test1size-1024*1024 ; Move offset to last meg
        mov     edi, [test2off]  ; Destination
        mov     ecx, test2size/4 ; Copying dwords
        rep movsd

.error:
        call    _LibExit
.initerror:
        pop     edi              ; Restore registers
        pop     esi
        ret                      ; Return to DOS