Search code examples
cassemblymasmx86-16real-mode

How can I copy memory data from pointer to array ASSEMBLY 8086


I'm working on a C program that calls an assembly function passing an array as argument. In the assembly code (for 8086), I'm able to get the address of the array in memory and to save the address in ES:BX but after that I need to copy the values to the array BARCODE but I can't find any way of achieving that.

My code looks something like this:

Code:

unsigned char computeControlDigit(char* barCodeASCII);

int main( void ){
char barCodeStr[14]
unsigned char controlDigitCheck;

controlDigitCheck = computeControlDigit(barCodeStr);
}

Assemby code:

_DATA SEGMENT WORD PUBLIC 'DATA'
    BARCODE DB 13 DUP(?)
_DATA ENDS

PUBLIC _computeControlDigit                     
_computeControlDigit PROC FAR
    PUSH BP 
    MOV BP, SP
    PUSH ES
    LES BX, [BP+6]

    ; code to copy from memory to
    ; array and code of operations on the array

    POP ES
    POP BP
    RET
_computeControlDigit ENDP                           
_TEXT ENDS
END

Any help would be wery welcome.


Solution

  • In the large memory model all data and code is FAR and must be referenced through the proper segment. In the code below I load the pointer to the source string barcodestr into DS:SI and BARCODE into ES:DI. I then read the character from the barcodestr array with LODSB and save it to BARCODE with STOSB. The copy is finished when the NUL terminator has been reached.

    Assuming the Direction Flag (DF) is set to 0 (forward movement):

    • STOSB is similar1 to doing:

      mov [ES:DI], al
      lea di, [DI + 1]              ; Increment DI by 1 without altering flags
      
    • LODSB is similar1 to doing:

      mov al, [DS:SI]
      lea si, [SI + 1]              ; Increment SI by 1 without altering flags
      

    I don't know if you are using MASM or TASM as an assembler so I provide a version for both. Example TASM code that simply copies a NUL terminated string is as follows:

    .MODEL LARGE, C
    
    PUBLIC computeControlDigit
    
    _DATA SEGMENT WORD PUBLIC 'DATA'
        BARCODE DB 13 DUP(?)
    _DATA ENDS
    
    _TEXT SEGMENT WORD PUBLIC 'TEXT'
    ASSUME DS:_DATA, CS:_TEXT
    
    computeControlDigit PROC C FAR
        ARG %%barcodestr:DWORD      ; barcodestr is a FAR pointer (DWORD)
        USES DS, SI, DI             ; Save non-volatile registers
    
        MOV AX, SEG BARCODE         ; Get segment and offset (FAR PTR) of BARCODE
        MOV ES, AX                  ; into ES:DI
        MOV DI, OFFSET BARCODE
    
        LDS SI, %%barcodestr        ; Load barcodestr FAR pointer into DS:SI
        JMP %%GETCHAR               ; Get next character
    
    %%NEXTCHAR:
        STOSB                       ; Store character to ES:DI (BARCODE), DI++
    %%GETCHAR:
        LODSB                       ; Read character from DS:SI (barcodestr), SI++
        TEST AL, AL                 ; Is it a NUL terminator?
        JNZ %%NEXTCHAR              ;     If not go back and get next character
    
    %%ENDLOOP:
        STOSB                       ; Store NUL terminator at end of BARCODE
    
        RET
    
    computeControlDigit ENDP
    _TEXT ENDS
    END
    

    Of course you do whatever processing you choose. I just did a straight copy of the data as an example.

    If using MASM you may have to use a slightly different syntax:

    .MODEL LARGE, C
    
    PUBLIC computeControlDigit
    
    _DATA SEGMENT WORD PUBLIC 'DATA'
        BARCODE DB 13 DUP(?)
    _DATA ENDS
    
    _TEXT SEGMENT WORD PUBLIC 'TEXT'
    ASSUME DS:_DATA, CS:_TEXT
    
    computeControlDigit PROC FAR C USES DS SI DI barcodestr:DWORD    
    ; DS, SI, DI are saved as they are non-volatile registers
    ; barcodestr is a FAR pointer (DWORD)
    
        MOV AX, SEG BARCODE         ; Get segment and offset (FAR PTR) of BARCODE
        MOV ES, AX                  ; into ES:DI
        MOV DI, OFFSET BARCODE
    
        LDS SI, barcodestr          ; Load barcodestr FAR pointer into DS:SI
        JMP GETCHAR                 ; Get next character
    
    NEXTCHAR:
        STOSB                       ; Store character to ES:DI (BARCODE), DI++
    GETCHAR:
        LODSB                       ; Read character from DS:SI (barcodestr), SI++
        TEST AL, AL                 ; Is it a NUL terminator?
        JNZ NEXTCHAR                ;     If not go back and get next character
    
        STOSB                       ; Store NUL terminator at end of BARCODE
    
        RET
    
    computeControlDigit ENDP
    _TEXT ENDS
    END
    

    A raw version without using special assembler directives that may look more natural to you would be:

                    PUBLIC  _computeControlDigit
    
    _DATA           SEGMENT WORD PUBLIC USE16 'DATA'
    BARCODE:
        DB  13 DUP(?)    
    _DATA           ENDS
    
    _TEXT           SEGMENT WORD PUBLIC USE16 'TEXT'
                    ASSUME CS:_TEXT, DS:_DATA
    
    _computeControlDigit:
            push            bp
            mov             bp,sp
            push            ds
            push            si
            push            di
            mov             ax,seg BARCODE
            mov             es,ax
            mov             di,offset BARCODE
            lds             si,dword ptr 6[bp]
            jmp             GETCHAR
    NEXTCHAR:
            stosb
    GETCHAR:
            lodsb
            test            al,al
            jne             NEXTCHAR
            stosb
            pop             di
            pop             si
            pop             ds
            pop             bp
            retf
    _TEXT           ENDS
                    END
    

    Footnote

    • 1LODSB and STOSB are similar to the equivalent code shown with the exception that LODSB and STOSB are executed in their entirety as one instruction each.