简体   繁体   English

汇编x86程序。 计算输入中的数字

[英]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! 这是DOS函数0Ah使用的输入缓冲区的正确定义, 但是稍后要遍历实际输入字符串时,您需要跳过前2个字节,因为它们不是实际输入文本的一部分!
You can change xor si, si into mov si, 2 . 您可以将xor si, si更改为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. DOS为您提供的输入以回车符(ASCII 13)终止,因此测试零是没有用的。


Below code uses AL instead of DL because the resulting assembly code will be a bit shorter. 下面的代码使用AL而不是DL因为生成的汇编代码会短一些。
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 . 该DOS函数要求DL有一个字符 Your count variable is just a number, most probably a very small number! 您的count变量只是一个数字,很可能是一个非常小的数字!
You can easily convert the small numbers from 0 to 9 into their respective characters by adding 48. 您可以通过添加48将0到9的小数字轻松转换为它们各自的字符。

    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). 明确将控制流返回给操作系统(在您的情况下为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. 同样, count确实包含用户输入中的位数,但采用2的补码格式,这不是您所需要的。 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 ). 例如,如果用户输入中有两位数字,则count将包含值0000 0010b ,这肯定不是数字2的ASCII代码(它是32h0010 0000b )。 If you allow up to 9 digits to appear in the user input, 2's complement to ASCII conversion is done very easily: 如果您最多允许9位数字出现在用户输入中,则2到ASCII转换的补码很容易完成:

add dl, '0'

This line should come after mov dl, count and before mov ah, 2h . 该行应在mov dl, count ,在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. 该代码的重点是演示几种可用于返回表示字符串中数字字符数量的十进制整数的方法之一,因为这是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. 由于DOS应用程序的许多示例本质上仅是平面内存模型,因此我省去了诸如节(.data,.const,.text)之类的多余内容,并使用RET返回到命令提示符,因为我们并不真正关心返回码。 Rarely do I reserve space as in a COM file all that does is bloat the application. 我很少像COM文件中那样保留空间,这只会使应用程序肿。

Assembled using NASM name.asm -oname.com version 2.11.08 使用NASM name.asm -oname.com版本2.11.08组装

Tested using DOSBOX 0.74 使用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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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