简体   繁体   中英

Assembly x86 program. Counting numbers in an input

Hello I am just learning assembly so I don't really understand many things yet.

I have to write a program where the user inputs some kind of line of various letters numbers etc. And the program should count how many numbers there are in the input and print the counter out.

Here is my code:

    .model small
.stack 100h

.data

    buffer      db 100, ?, 100 dup (0)
    count       db 0

.code

start:

    mov ax, @data
    mov ds, ax

    mov dx, offset buffer
    mov ah, 0Ah
    int 21h

    mov ah, buffer
    xor si, si
    xor cx, cx
  .loop:

  .notdigit:

    mov dl, buffer[si]
    inc Si
    cmp dl, 0
    jz .end

    cmp dl, '0'
    jb .notdigit
    cmp dl, '9'
    ja .notdigit
    inc count
    jmp .loop

  .end:

; count contains the digit count


    mov dl, count
    mov ah, 2h
    int 21h

I get no errors but the program doesn't really work when I run it.

What is wrong here? And how should I change it?

Input

 buffer db 100, ?, 100 dup (0) 

This is the correct definition for an input buffer to be used by DOS's functions 0Ah, but later on when you want to traverse the actual input string, you need to skip the first 2 bytes since these are not part of the actual inputted text!
You can change xor si, si into mov si, 2 .

 cmp dl, 0 jz .end 

The input that DOS delivers you is terminated by a carriage return (ASCII 13) and so it is useless to test for a zero.


Below code uses AL instead of DL because the resulting assembly code will be a bit shorter.
Alternative solutions exist but this one is closest to what you got:

    mov     si, 2
.loop:
    mov     al, buffer[si]
    inc     si
    cmp     al, 13
    je      .end
    cmp     al, '0'
    jb      .loop      ;notdigit
    cmp     al, '9'
    ja      .loop      ;notdigit
    inc     count
    jmp     .loop
.end:

Output

 mov dl, count mov ah, 2h int 21h 

This DOS function expects a character in DL . Your count variable is just a number, most probably a very small number!
You can easily convert the small numbers from 0 to 9 into their respective characters by adding 48.

    mov     dl, count
    add     dl, '0'    ;ASCII code for '0' is 48
    mov     ah, 02h
    int     21h

It is programmer's responsibility to explicitly return the flow of control to the operating system (in your case, it is DOS). This is done using the following system call:

mov ah, 4Ch
mov al, 0
int 21h

You should put these three lines at the end of your assembly source file.

Also, it is true that count contains the number of digits in the user input, but in 2's complement format, which is not what you need. For example, if there are two digits in the user input, count will contain the value 0000 0010b , which is certainly not ASCII code for the number two (it's 32h or 0010 0000b ). If you allow up to 9 digits to appear in the user input, 2's complement to ASCII conversion is done very easily:

add dl, '0'

This line should come after mov dl, count and before mov ah, 2h .

The point to this code is to demonstrate one of a few methods that could be used to return a decimal integer representing the number of digit characters in a string, as that is the part that's missing from OP. Conventionally some sort of conversion algorithm is used after the fact, but I thought it might be interesting to see how it can be done while parsing the string.

As many examples of DOS applications are essentially nothing more than flat memory models, I dispense with superfluous things like sections (.data, .const, .text) and use RET to return to the command prompt as we're not really concerned about a return code. Rarely do I reserve space as in a COM file all that does is bloat the application.

Assembled using NASM name.asm -oname.com version 2.11.08

Tested using DOSBOX 0.74

      WRITE_STR equ  9
       READ_STR equ 0xc0a       ; Buffered input with initial flush
          DOS   equ 33

    org 100H            ; DOS 6.22 COM file 
; ----------------------------------------------------------------------------

Begin:

    ; Every program should have some sort of prompting so operator has an idea
    ; of what is expected.

    mov dx, Prompt
    mov ah, WRITE_STR
    int DOS

    ; Not absolutely required, but AH = 0CH & AL = 0AH accepts buffered input
    ; but assures there aren't any straggling characters as buffer pointed to by
    ; DX is flushed first.

    mov dx, Users_Entry
    mov ax, READ_STR
    int DOS 

    ; Setup pointer to string, count of characters entered and initial count
    ; of digit characters in string

    mov     si, dx          ; Point to input buffer
    inc     si          ; Bounce over maximum characters
    xor     ax, ax
    push    ax
    lodsb               ; Read # of characters entered
    mov     cx, ax          ; Move to counter register for loop

    ; Of the four possible outcomes, each message must be preceded with
    ; double return & tab.

    mov dx, PreAmb
    mov ah, WRITE_STR
    int DOS

    ; The reason AX is being used because a packed BCD value is going to be
    ; created on the fly, but for this to work DAA must be used and it only
    ; works on the accumulator.

    pop ax          ; Restore initial digits count
    mov dx, Err00       ; By default, assume nothing was entered.
    jcxz    Finished        ; Branch in buffer is empty

    .NextChr:
    mov bl, [si]        ; Read first or next character
    inc si          ; Bump pointer
    cmp bl, '0'
    jb  .notNum
    cmp bl, '9'
    ja  .notNum
    add al, 1
    daa             ; Bump counter and decimal adjust
    .notNum:
    loop    .NextChr

    mov dx, Msg01       ; Assume a single digit character.
    cmp al, 1
    jz  Finished
    mov dx, Msg00
    or  al, al
    jz  Finished        ; No digit characters found

    ; Now we are going to replace the word "No" with the BCD value in AX
    cld
    mov di, dx
    push    ax
    mov dx, di          ; Needed to ultimately display string

    ; There is a good change the value is < 10, so initially we are going
    ; to convert to space.

    shr ax, 4           ; Shift tens into low nibble
    or  al, ' '
    cmp al, ' '
    jz  $ + 4           ; I
    or  al, 10000b      ; Convert ot digit 1 - 9
    stosb               ; Write to destination string
    pop ax
    and ax, 1111b       ; Mask out high nibble (tens)
    or  al, '0'
    stosb               ; Write units digit.

    ; Now the appropriate final message can be displayed appending modified
    ; PreAmb to it.

Finished:
    call    Show            ; Display desired result string

    mov dx, PreAmb + 1      ; Do one less line-feed
    mov di, dx
    mov byte [di+2], '$'        ; Don't want to do TAB
Show:
    mov ah, WRITE_STR
    int DOS
    ret             ; Return to DOS or maybe DEBUG

; NOTE: alignment is for convenience sake so things can be identified a little
;       more readily using hex dumps in DEBUG.

    align   16
; ----------------------------------------------------------------------------

    Prompt: db  27, '[2J', 10   ; VT100 emulation clears screen and sets
                    ; cursor to 0:0
        db  ' Please enter alphanumeric string (max 48 chars)'
        db  10, 10, '    --> $'

     PreAmb:    db  10, 10, 9, '$'
     Err00: db  '--> FAILED <-- NULL string$'

     Msg00: db  'No digit characters were found$'
     Msg01: db  'Only a single digit character found$'

    align   8

    Users_Entry:    db  49      ; NOTE: change prompting text too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM