How do I use a buffer in an assembly procedure?
Asked Answered
M

3

7

So, I understand the general abstract concept of a buffer: it's an allocation in memory that holds data before it gets processed. I'm trying to complete a homework problem which requires me to write an ASCII string into a buffer within a procedure. So, I understand that I'm supposed to pass an address of an array to the procedure when calling it, for example...

main PROC
mov  EAX, packed           ; pass a packed decimal to EAX
mov  ESI, OFFSET ascArray  ; pass the offset of an empty array to ESI
call PackedToAsc           ; call the function

So the function is supposed to return 'a pointer to a buffer with the ASCII decimal string'. I'm probably being stupid here, but I'm not quite clear on what exactly a buffer is in this instance.

  • Is it an array?
  • Do I need to declare it as such in the .data section?
  • How do I declare the pointer to the ASCII string inside the procedure?
  • What is meant by a buffer in this context?

More practically, I need to access the buffer that the data is put into when the procedure finishes, and I'm not sure how to do that.

EDIT   --   I'm in x86, and I'm using MASM.

Mancuso answered 20/11, 2012 at 8:13 Comment(0)
S
5

Assuming x86, this depends on whether your buffer starts with data in it or not. It also depends on if your buffer is variable size.

Taking case one, and you know that your buffer will never exceed, say, 20 bytes, you can declare it in the data section (NASM syntax):

buffer: times 20 db 0

In the data section declares 20 0 bytes that you can now use. If you don't need to initialize with data, you use the .bss section (NASM syntax):

buffer: resb 20

Which tells NASM to reserve 20 bytes.

If the size of your buffer is variable, however, things aren't so easy. You have to dynamically allocate memory from your OS, and this is very OS dependent. You basically have 2 options:

  • Use you C library's malloc: This may be easier or harder, depending on platform calling conventions. There's a reasonable reference here
  • Use your system call: All systems provide some way to get more memory, but none of them are as pretty or easy as malloc. They usually involve adding another page of memory to your process and letting you manage it yourself. This is what malloc does internally and is the only option if you can't use the C library.
Sirloin answered 20/11, 2012 at 14:52 Comment(2)
Allocating a buffer on the stack is as easy as sub esp, ecx / and esp, -16. It's the best option for scratch buffers for local use, or for passing to other functions.Mydriatic
Note that this answer is fully NASM syntax; the question was updated to specify MASM after this answer was posted :( The concepts are right, but neither syntax construct works in MASM.Mydriatic
M
5

Yes, a buffer is just an array, which in assembly is a sequence of bytes.

You have 3 main options for allocating it, exactly like in C:

  • static storage: like C static char buf[100];

      section .bss                   ; this might not be proper MASM syntax
       my_buffer:  db   100 dup(?)   ; but this is definitely MASM
    

    Putting a : between the label name and the db makes it just a plain label, like NASM, not a MASM "variable" with an implied operand-size. (If MASM lets you do that in a .data / .bss section. It might not.)

    100 dup means to repeat the next thing 100 times. ? means uninitialized storage. It's actually going to be zeroed in a program that runs under an OS like Windows, because it can't let programs see stale data left over from kernel data or other processes on the same machine. So 100 dup(0) would also work and maybe be a better description of what you want, especially if your code ever reads any of these bytes without writing first.

  • dynamic storage: call malloc, or invoke an OS function directly like mmap or VirtualAlloc. You can return a pointer to it from the function that allocated it.

  • automatic storage (on the stack): like a C local variable. Deallocated automatically when the allocating function returns. Very cheap and easy, use this for scratch buffers unless you know they need to be multiple megabytes.

The easiest way to deal with buffers is to accept a pointer to an already-allocated buffer, and give your caller the choice of what buffer to pass.

For example, a function that uppercases ASCII letters could just take a src and dst pointer. If you want it to operate in-place, you can just pass the same pointer for input and output, if it's written to support that. It doesn't have to care about memory management, it just operates between two buffers.

A function like C strdup makes a new copy of a string, and that only makes sense with dynamic storage. Copying the string into a static buffer and returning that wouldn't work well, because there's only one instance of that static buffer. The next call to it would overwrite the old contents.


Allocating a buffer on the stack:

A variable-size buffer on the stack is no problem; you just need a way to clean up the stack afterwards. Making a stack-frame with EBP / RBP is an easy way to do that. Consider this example function that allocates a buffer as large as needed, and uses it to hold the output from a string-reverse function so it can pass it to a print function. You can see what compilers do in this case.

void string_reverse(char *d, const char*s, int len);
void print(const char*s, int len);  // modify this to an fwrite or whatever.

void print_reversed(const char *s, int len) {
    char buf[len];
    string_reverse(buf, s, len);
    print(buf, len);
}

This is what you might do by hand, if string_reverse doesn't need 16-byte stack alignment and it doesn't clobber its stack arg. (The ABI / calling convention doesn't guarantee either of those things, so we're taking advantage of special knowledge of the function we're calling to simplify print_reversed.)

; MSVC __fastcall convention
; args: ecx, edx    (const char *string,  size_t length)
print_reversed PROC
    push   ebp
    mov    ebp, esp         ; make a stack frame

    sub    esp, edx         ; reserve space for a buffer
    and    esp, -16         ; and realign the stack
    ; allocate buf[length] on the stack, address = esp
      ; mov eax, esp       ; if you want to copy it somewhere

        ;sub    esp, 12          ; ensure 16-byte stack alignment before CALL

    push   edx              ; 3rd arg and later args go on the stack
    mov    edx, ecx         ; 2nd arg = string
    lea    ecx, [esp+4]     ; 1st arg = output buffer = what we allocated.  (PUSH offset ESP by 4, LEA corrects for that)

    call    string_reverse   ; (dst=buf (ECX),  src=string (EDX), length=length (stack))
      ; clean up the stack after the call and set up args for print
    pop    edx              ; assuming string_reverse doesn't modify its stack arg
    mov    ecx, esp         ; esp is once again pointing to our buffer
    call   print            ; print(ECX=buf, EDX=length)

      ; lea     esp, [ebp-8]  ; if you needed to push stuff after EBP, restore this way
      ; pop  ebx  / pop esi / pop ebp
    
    leave                   ; mov esp, ebp / pop ebp to clean up the stack frame
    ret
ENDP

This is how most C compilers implement alloca or C99 variable-length arrays.

Mydriatic answered 22/5, 2018 at 5:37 Comment(1)
Correction: 32-bit fastcall is a callee-pops convention, it's already removed the stack arg from the stack by the time the caller regains control after a call. So instead of pop edx to recover the original size_t len, we need to save the incoming EDX arg somewhere. (e.g. push it before allocating the VLA, so it's at [ebp-4].)Mydriatic
B
-2

I'm use this method for get char at buffer and Compare with other string

CompareText macro colorScreen,string1,ScoreofLabel,loop_1,endText,notText,backprint

    mov h3,cx
    ClescrColor colorScreen                                     
    mov cx,h3

    printstr string1

    lea SI,string1[0]
    loop_1:       
        mov AH,01 
        int 16H
        jz backprint

    mov AH,00      
    int 16H       
    mov AH, [SI]   
    inc SI    

    cmp ah,36
        jne endText
    mov ah,13           

    endText:
    cmp AL, AH
        jne notText
    cmp AL, 13    
        jne loop_1


    jmp ScoreofLabel
    notText:
        mov si,62


endm 

I hope help you

Breakout answered 22/5, 2018 at 4:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.