问题
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?
回答1:
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
回答2:
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
.
回答3:
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.
来源:https://stackoverflow.com/questions/52826899/assembly-x86-program-counting-numbers-in-an-input