装配宏打印错误

时间:2018-01-10 10:13:15

标签: assembly macros

所以我一直在尝试使用一些宏来在程序集中打印一些消息(我使用TASM作为编译器)并且直到一点都很好。我必须读取一个带有[0 ... 9]数字的字符串,并计算偶数的总和。问题是,一旦我输入字符串后按Enter键,程序就不会执行任何操作而只是阻塞。你能帮我解决这个问题吗? 这是代码:

DATA SEGMENT PARA PUBLIC 'DATA'
MSG1 DB 0DH, 0AH, 'PLEASE INPUT YOUR STRING', '$'
MSG2 DB 0DH, 0AH, 'THE SUM IS', '$'
NEWLINE DB 0DH, 0AH, '$'
TOTAL DB ?
DATA ENDS

WRITE MACRO MSG
MOV AH, 09H
LEA DX, MSG
INT 21H
ENDM WRITE

WRITE2 MACRO NR
MOV AH, 02H
MOV DL, NR
INT 21H
ENDM WRITE2

CODE SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CODE, DS:DATA
START PROC FAR
PUSH DS
XOR AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX

WRITE MSG1
WRITE NEWLINE


MOV AH,0AH
INT 21H ;READ INPUT

MOV SI,0002H ;INDEX USED TO ACCESS DS CONTENT, 0002h is where string begins
MOV DX,0 ;this is where I store the sum


MOV BL,2 ;used for even nr test
MOV CL,BYTE PTR DS:[0001] ;number of elements stored in the string

L:
    XOR AX,AX
    CMP BYTE PTR DS:[SI],20H ;check if current position is a space(' ')
    JE SPACE

    MOV AL,BYTE PTR DS:[SI] ;not a space, now we check if it's even
    SUB AL,30H
    DIV BL ;we divide by 2
    CMP AH,0 ;get the mod result(0 even, 1 odd)
    JNE SPACE
    CALL SUM 


    SPACE: INC SI
    LOOP L

MOV TOTAL,DL

WRITE MSG2
WRITE2 TOTAL

MOV AH,4CH
INT 21H
RET 
START ENDP

PROC SUM
ADD DL, BYTE PTR DS:[SI] ;ds content, where the string is stored
SUB DL, 30H ;ascii to digit
RET
SUM ENDP

CODE ENDS 
END START

1 个答案:

答案 0 :(得分:0)

根据OP的评论中的要求,这是我对同一任务的变体,源代码是TASM汇编程序(我有TASM 4.1和TLINK 7.1.30.1):

; I will use the "simplified" TASM model+segment definitions starting with dot
.MODEL SMALL
.STACK 500h
.DATA

NewLineText LABEL BYTE
    DB  13, 10, '$'
InputPrompt LABEL BYTE
    DB  "Please input your string:$"
AnswerLabel LABEL BYTE
    DB  "The sum of even digits is: $"
InputBuffer LABEL BYTE      ; input buffer for DOS service int 21h,0Ah
    DB  80, 0, 80 DUP (0)   ; max 79+enter characters
AsciiSumResult LABEL BYTE
    DB  "000$"              ; 79*8 = 632 is maximum possible result
    ; plus it also contains dollar terminator for printing by DOS 21h,09h

.CODE
.386
    ASSUME CS:@CODE, DS:@DATA

Start:
    ; set up ds to point to .data segment
    mov     ax,@DATA
    mov     ds,ax
    ; print input prompt
    call    PrintNewLine
    lea     dx,[InputPrompt]
    call    PrintDollarTerminatedString
    call    PrintNewLine
    ; run DOS service for input string (80 chars max)
    mov     ah,0Ah
    lea     dx,[InputBuffer]
    int     21h
    ; sum even digits from input
    lea     si,[InputBuffer+2]  ; pointer to first input character
    movzx   cx,BYTE PTR [si-1]  ; length of entered input
SummingLoop:
    lodsb                   ; al = next input character (from ds:[si++])
    ; only ASCII digits are valid input
    cmp     al,'9'
    ja      NotDigit
    sub     al,'0'          ; does both compare the value and
    jb      NotDigit        ; convert it from ASCII digit to integer
    ; here AL=0..9, test if the value is even
    test    al,1            ; if it is not divisible by 2 (odd)
    jnz     NotDigit        ; first bit is one (not zero)
    ; add the value to the ASCII (!!!) sum
    ; (to avoid conversion from multi-digit integer to string later)
    add     [AsciiSumResult+2],al   ; add value to last digit, check overflow
    cmp     BYTE PTR [AsciiSumResult+2],'9'
    jbe     NotDigit        ; no overflow, just continue
    ; adjust result (subtract 10 from last digit, and +1 to middle one)
    add     WORD PTR [AsciiSumResult+1],-10*256+1
    ; check overflow on middle digit
    cmp     BYTE PTR [AsciiSumResult+1],'9'
    jbe     NotDigit        ; no overflow, just continue
    ; adjust result (subtract 10 from middle digit, and +1 to first one)
    add     WORD PTR [AsciiSumResult],-10*256+1
    ; first digit can't overflow (max possible result is 632) => no check
NotDigit:
    dec     cx
    jnz     SummingLoop     ; validate+sum all input characters
    ; output sum, show the label text first
    call    PrintNewLine
    lea     dx,[AnswerLabel]
    call    PrintDollarTerminatedString
    ; output sum, show the value (skip leading zeroes)
    lea     si,[AsciiSumResult]
    cmp     BYTE PTR [si],'0'
    jnz     OutputSum       ; first digit is non-zero, print it
    inc     si
    cmp     BYTE PTR [si],'0'
    jnz     OutputSum       ; second digit is non-zero, print it
    inc     si              ; print third digit (even when zero)
OutputSum:
    ; leading zeroes skipped, SI is pointing to result sum string
    mov     dx,si
    call    PrintDollarTerminatedString
    ; terminate application
    mov     ah,4Ch
    int     21h

; --- helper procedures ---

; ds:dx = pointer of string to print, modifies ax
PrintDollarTerminatedString PROC
    mov     ah,9
    int     21h
    ret
PrintDollarTerminatedString ENDP

; prints new line, modifies ax, dx
PrintNewLine PROC
    lea     dx,[NewLineText]
    jmp     PrintDollarTerminatedString
PrintNewLine ENDP

END Start

要构建简单使用:TASM <filename> TLINK <filename>,应该在没有警告的情况下生成EXE文件。

示例输出(来自“dosemu”,因为“dosbox”不支持在文本模式下复制,而在“dosemu”复制/粘贴中的工作方式与普通的X-window应用程序一样(我使用linux作为操作系统)): / p>

D:\asm\tasm>so_sum

Please input your string:
123456 1 2 3 4 5 6
The sum of even digits is: 24

有关dosbox与dosemu的另一个有趣的事情,在dosbox中输入可以输入80个字符,但是当你达到最大字符数量时你无法点击输入(这可能是OP的的来源“它确实没有“评论与1 2 3 4 5 6 7输入)。删除带退格键的第80个字符后,输入works,代码继续。

在dosemu中,模拟的DOS只允许输入79个字符,保留第80个字节为仅用于输入,这看起来更符合逻辑行为。

源代码主要以简单直接的方式编写,除了在对数字求和方面有点棘手。它不是将它们相加为整数值,而是将其作为内存中的ASCII字符串求解(解析ASCII数字的溢出,如'9'+ 5 =&gt;应变为'4',并将+1发送到左边的数字)。溢出调整使用WORD值的little-endian编码使用ADD指令同时命中两位数,对右边的数字执行-10,并{{1到左边的数字。

如果你注意到如何在计算机中以位/字节/字/等编码值...你应该解读它,它不是那么高级,但对于不理解值如何在计算机和内存中编码的人来说是难以辨认的

奇数/偶数测试当然不使用+1指令。计算机中的整数值已经以位为“二进制”编码,因此测试第一位(可能是编码值2 0 = 1,当被解释为整数时)对于偶数/奇数测试是决定性的,并且CPU计算除法运算比计算除法运算更简单+更快。