Search code examples
assemblyemu8086yasm

how to properly calculate sum of digits from specific place in file? (YASM 8086)


I have one line of text in my input file, which is divided into six columns. Columns are separated from each other by a semicolon.

Here is input file example:

A1;example;10;0;55;2.44

I am looking only into third column number. In this case it would be number 10, so the sum of its digits is 1. According to the task requirements, the sum has to be 7

I read the line, save it into buffer. Then byte by byte I move to the place where third comumn starts.

    ; Save line into buffer
    call procGetLine
    
    ; Skip one column
    mov di, inputBuffer
    .whileNotSemicolon:
        cmp di, byte ';'
        je .semicolon
        inc di
        jmp .whileNotSemicolon
        .semicolon:
            inc di

    ; Do some calculations with second column and move to the start of third column
    ; ............

    ; Call function that calculates sum of number digits in third column
    call procDigitsSum
    cmp [digitsSum], word 7
    je .isSeven

procDigitsSum function:

    procDigitsSum
    push si
    push ax
    push dx
    push cx
    push bx

        mov [digitsSum], word 0
        mov si, 0
        .loop:
            mov cl, [di]
            inc di

            cmp cl, byte ';'
            je .columnEnd

            ; ASCIIZ for using it in function later
            mov byte [thirdColumnNumber+si], cl     
            mov byte [thirdColumnNumber+si+1], 0    
            inc si
            jmp .loop

        .columnEnd
            mov dx, thirdColumnNumber
            mov di, dx
            .secondLoop:
                ; Guessing mistake lies somewhere here
                mov dl, [di]
                inc di
                cmp dl, byte 0
                je .endLoop

                add [digitsSum], dl
                add [digitsSum], byte 30h

                jmp .secondLoop

        .endLoop
    pop bx
    pop cx
    pop dx
    pop ax
    pop si
    ret

procGetLine function just in case, however it seems to work fine:

procGetLine:
    push ax
    push bx
    push cx
    push si

        mov bx, [inputFileDescriptor]
        mov si, 0

        .loop
            call procFGetChar
            cmp ax, 0
            je .endOfFile
            jc .endOfFile

            mov [inputBuffer+si], cl
            inc si

            cmp cl, 0Ah
            je .endOfLine

            jmp .loop

        .endOfFile:
            mov [lastLine], byte 1
        .endOfLine:
            mov [inputBuffer+si], byte '$'
            mov [bytesRead], si

    pop si
    pop cx
    pop bx
    pop ax
    ret

Solution

  • ; Skip one column
    mov di, inputBuffer
    .whileNotSemicolon:
        cmp di, byte ';'
        je .semicolon
        inc di
        jmp .whileNotSemicolon
        .semicolon:
            inc di
    

    This code can't possibly skip one column! The cmp di, byte ';' makes no sense. It compares the pointer instead of the byte that the pointer points at. You need:

    .whileNotSemicolon:
        cmp  [di], byte ';'
        je   .semicolon
        inc  di
        jmp  .whileNotSemicolon
        .semicolon:
            inc di
    

    But an even better solution tries to minimize the branching like in next code:

    .whileNotSemicolon:
        mov  al, [di]
        inc  di
        cmp  al, ';'
        jne  .whileNotSemicolon
    .semicolon:
    

    In this case it would be number 10, so the sum of it's digits is 1. According to the task requirements, the sum has to be 7

    I don't see where this 7 should come from!


    Your post doesn't mention this, but what are the chances that you are not solving a task related to this extremely similar question where the 3rd, 4th, and 5th columns contain numbers in the range [-100,100].
    Knowing that the 3rd column can only contain {-0123456789;}, the task to sum the digits comprises:

    • loading a byte from the 3rd column
    • converting the character into the corresponding digit subtracting 48
    • if it was the minus1 (ASCII=45), the result will be -3 which will have produced a carry on sub al, '0'
    • if it was the semicolon (ASCII=59), the result will be 11 which is above 9
    ; IN (di) OUT (di)
    procDigitsSum
        push ax
        push cx
        xor  cx, cx
        xor  ax, ax
    .loop:
        add  cx, ax
    .minus:
        mov  al, [di]
        inc  di
        sub  al, '0'
        jb   .minus          ; It's a minus
        cmp  al, 9
        jbe  .loop           ; It's a digit
    .above:                  ; It's a semicolon
        mov  [digitsSum], cx
        pop  cx
        pop  ax
        ret                  ; DI points at next column
    

    1 If whitespace were allowed, it would follow the same path as the minus did.