Search code examples
fileassemblycountletters

Assembly x86 - counting small letters in a file


I am working on an assignment and I got stuck somehow. This is the part of the code, that must be wrong. And what is actually wrong is that when the program should print the number (thtough procedure convertNumber, which works fine!), it doesn't do anything, neither it returns. It just doesn't react, I must close it manually.

read:
            mov ah, 3fh                 ; function for reading from file
            mov bx, handle              ; moving file handle to BX
            mov cx, 10000               ; how many bytes will be read
            mov dx, buffer              ; where the string will be saved
            int 21h                     ; system call ready

            mov bytesRead, 0            ; nulling my variable bytesRead, which represents how many bytes have been read
            mov bytesRead, ax           ; now moving the real value from AX

            cmp ax, 0                   ; if it is equal to 0, then there is nothing left to read                                                     
            je closeFile                ; close file

            mov cx, 0                   ; starting position

cycle:
            cmp cx, bytesRead           ; if I need to reload buffer
            jge read

            mov bx, 0                   ; nulling position counter
            mov bx, cx                  ; moving CX to BX -> mov ax, buffer[cx] was an illegal operation, so I did it this way
            mov ax, buffer[bx]          ; getting a character from BX. position from buffer

            cmp ax, 'a'
            jb next                     ; below 'a', skip
            cmp ax, 'z'
            ja next                     ; above 'z', skip
            inc count_task              ; if it passed, it is between 'a' and 'z'
            jmp next                    ; and moving on

next:
            inc cx                      ; increase position counter (always!)
            jmp cycle                   ; and repeat

finally:
            mov ax, count_task          ; move the final count to AX (needed by the procedure convertNumber)
            call convertNumber          ; convert number to its ASCII value (+48)
            ret

error_fopen:
            print fopenErr
            ret

error_fclose:
            print fcloseErr
            ret

closeFile:
            mov ah, 3eh
            mov bx, handle
            int 21h

            jc error_fclose
            jmp finally

convertNumber proc near
    push si

    mov buff, '$'
    lea si, buff
    mov cx, 10

conv:
    mov dx, 0
    div cx                          ; AX is being divided by CX, where value 10 is stored (direct dividing by 10 is not legal)
    add dx, 48                      ; getting ASCII value of the number (from 0 to '0')
    dec si                          ; going backwards
    mov [si], dl
    cmp ax, 0
    jz printNumber                  ; if AX is empty (0), print the number
    jmp conv                        ; else loop 

printNumber:
    mov ah, 9                       ; service to print string
    mov dx, si                      ; position of my string (number)
    int 21h

    pop si

    ret
convertNumber endp      

I hope you can understand what is going on in this code, if not, I can explain you. I would be very grateful for any help!


Solution

  • A version using lodsb and loop shown below, with a couple of other things tidied up. I added a little extra to print the index of each small letter found. I didn't check this for silly errors as I'm not set up to run DOS interrupts.

    This code will handle files longer than 65535 bytes long, but the "convertNumber" subroutine would need to be updated to print long integers.

    EDIT: The format below assumes the NASM assembler (the actual assembler used isn't mentioned in the problem statemet).

            mov     [count_task], 0
            mov     [count_task+2], 0
            mov     [index], 0
            mov     [index+2], 0
    read:
            mov     ah, 3fh                 ; function for reading from file
            mov     bx, [handle]            ; moving file handle to BX
            mov     cx, 10000               ; how many bytes will be read
            mov     dx, buffer              ; where the string will be saved
            int     21h                     ; system call ready
            jc      error_read
    
            cmp     ax, 0                   ; if it is equal to 0, then there is nothing left to read
            je      finally                 ; print result and close
    
            ; This code assumes your ds segment register already points to your data
    
            lea     si, [buffer]
            mov     cx, ax                  ; starting loop counter
            cld                             ; clear direction flag (increment)
    cycle:
            lodsb
            cmp     al, 'a'
            jb      next
            cmp     al, 'z'
            ja      next
            add     [count_task], 1
            adc     [count_task+2], 0
    
            ; Print the current index of the found small letter.
            ; Note it's assumed that convertNumber saves certain registers
            ; according to calling conventions. For example, if it uses "si"
            ; then it should save "si". AX, CX, and DX are fair game.
            ;
            push    cx
            mov     ax, [index]
            mov     dx, [index+2]
            call    convertNumber           ; print the index of the found small letter
            pop     cx
    next:
            add     [index], 1              ; increment overall index
            adc     [index+2], 0
            loop    cycle                   ; and repeat
    
            jmp     read                    ; read another buffer full
    finally:
            mov     ah, 3eh
            mov     bx, [handle]
            int     21h
            jc      error_fclose
    
            mov     ax, [count_task]        ; move the final count to DX:AX (needed by the procedure convertNumber)
            mov     dx, [count_task+2]
            call    convertNumber           ; convert number to its ASCII value (+48)
            ret
    
    error_read:
            print   freadErr
            ret
    
    error_fopen:
            print   fopenErr
            ret
    
    error_fclose:
            print   fcloseErr
            ret