Search code examples
assemblydosx86-16tasm

Filling dynamically allocated memory freezes program execution


Using TASM I'm trying to allocate some memory which should serve as a buffer.

To do this I first deallocate all the memory which has been given to the executable using:

    MOV     BX, SS 
    MOV     AX, ES  
    SUB     BX, AX  
    MOV     AX, SP 
    ADD     AX, 0fh
    SHR     AX, 4
    ADD     BX, AX
    MOV     AH, 4ah
    INT     21h

Afterwards I'm trying to allocate 64000 bytes using:

    MOV     AH, 48h
    MOV     BX, 64000/16 
    INT     21h
    MOV     buffer, AX

which seems to work flawlessly as the CARRY flag ain't set after executing instruction 21h.

Later on I'm ultimately trying to fill the just allocated memory like:

    MOV     BX, OFFSET BUFFER
    MOV     ES, BX

    XOR     DI,DI
   
    MOV     CX,64000/2
    MOV     AL,12
    MOV     AH,AL
    REP     STOSW

which unfortunately fails as the program freezes at the REP STOSW instruction. Now I'm a little lost as I can't find something obviously wrong. So why is this happening?

Here's my full source:

.MODEL LARGE

.STACK 100h
.DATA? 
buffer      DW ?

.CODE
Main:
    MOV     BX, SS 
    MOV     AX, ES  
    SUB     BX, AX  
    MOV     AX, SP 
    ADD     AX, 0fh
    SHR     AX, 4
    ADD     BX, AX
    MOV     AH, 4ah
    INT     21h

    MOV     AH, 48h
    MOV     BX, 64000/16 
    INT     21h
    MOV     buffer, AX

    MOV     BX, OFFSET BUFFER
    MOV     ES, BX

    XOR     DI,DI
   
    MOV     CX,64000/2
    MOV     AL,12
    MOV     AH,AL
    REP     STOSW

    MOV     AH,4ch  
    INT     21h 
   
END Main      

Solution

  • MOV     BX, OFFSET BUFFER
    MOV     ES, BX
    

    The info that DOS function 48h gave you is a paragraph address.

    The above code loads the offset to the variable that contains this address. You need to dereference it:

    MOV     ES, buffer
    

    Directly from AX:

    MOV     AH, 48h
    MOV     BX, 64000/16 
    INT     21h          ; -> AX CF
    
    JC      Fail         ; Never forget to check for failure
    
    MOV     buffer, AX
    MOV     ES, AX
    XOR     DI, DI
    MOV     CX, 64000/2
    MOV     AX, 0C0Ch
    CLD                  ; Clear direction flag at least once in your program
    REP     STOSW
    
    Fail:
    MOV     AX, 4C00h  
    INT     21h
    

    [edit]

    .MODEL LARGE
    
    .STACK 100h
    .DATA? 
    buffer      DW ?
    
    .CODE
    

    With the above code, the high end of your program memory finally looks like:

        .DATA?          .STACK           func 48h alloc
    ... <-- 16 bytes --><-- 256 bytes --><-- 64000 bytes --> ...
        94h,08h,0,0,..,0
        ^               ^                ^
        0883h           0884h            0894h    <== paragraph addresses
        <-- 272 bytes = 17 paragraphs -->
    

    buffer is the name of the first (and only) variable in your BSS (.DATA?).

    mov ax, offset buffer loads AX with 0 because the buffer variable occupies the very first word in the BSS.
    mov ax, seg buffer loads AX with 0883h, the paragraph address of the BSS as assigned at program load time.
    mov ax, buffer loads AX with 0894h, the contents of the buffer variable and that is the paragraph address that DOS assigned to the 64000-byte allocation.

    For some more explanation about segments and paragraphs read this: What are Segments and how can they be addressed in 8086 mode?.