n 小于 100 的倍数的 TASM 代码不显示输出
TASM code for multiples of n under 100 doesnt show output
这就是代码的作用。假设用户输入数字 20。它将此值与计数器 CX 相乘,设置为 1,并在每次迭代时增加。当结果达到或超过 100(数字 20 的 4 次迭代)时,它返回并通过压入和弹出堆栈来打印所有结果。
每个数字相加,然后分隔成单个数字,然后使用 02 函数显示。
MOV CX, 01D
MOV AX, 0
MOV DX, BX ; BX contains the number
MOV DI, 01 ;
MOV SI, 01 ; used for addressing offsets of BX
MULTIPLY:
MOV AX, DX
MUL CX
CMP AX, 100D
JGE FINISHED
MOV [BX+DI], AX
INC CX
INC DI ; will be changed to ADD DI, 2
JMP MULTIPLY
这段代码有问题。我试图让它将输入乘以 1,然后乘以 2,然后乘以 3,依此类推,直到结果达到 100。我在这里错过了什么?
代码中的一个问题:在标签 multiply
之前,您将输入的数字存储在 DX 中:
MOV DX, BX ; BX contains the number
但在标签 multiply
之后使用 mul
,这会破坏 DX。解决方案是使用另一个寄存器,例如 BP,而不是 DX。
另一个重要的问题是您将倍数存储在[BX+DI]
中,但是BX指向的数据段中没有变量。解决方案是创建一个数组 ("buf").
现在让我们将这两种解决方案应用到您的原始代码中,我添加了一个逗号来分隔数字,更改由箭头指出(;<===
):
.MODEL SMALL
.STACK 100H
.DATA
S1 DB 13,10, "Enter a number below 100 :" , '$'
S3 DB 13,10, "The multiples are: ", '$'
buf dw 100 dup(?) ;<=== ARRAY OF MULTIPLES.
comma db ",$" ;<=== SEPARATOR BETWEEN MULTIPLES ON SCREEN.
.CODE
MAIN PROC
MOV AX,@DATA
MOV DS,AX
LEA DX, S1
MOV AH, 09 ; ENTER A NUMBER?
INT 21H
CALL INPUT ;ACCEPTS A NUMBER
LEA DX, S3
MOV AH, 09 ; THE MULTIPLES ARE:
INT 21H
MOV CX, 01D
MOV AX, 0
MOV bp, BX ;<=== PRESERVE INPUT NUMBER IN BP.
mov di, offset buf ;<=== DI POINTS TO ARRAY OF MULTIPLES.
;MOV DI, 01
;MOV SI, 01
MULTIPLY: ;where the good stuff happens
MOV AX, BP ;<=== MUL WILL CHANGE DX, THAT'S WHY WE USE BP.
MUL CX ;AX*CX = RESULT IN DX:AX.
CMP AX, 100D
JGE FINISHED
MOV [di], AX ;<=== STORE MULTIPLE IN ARRAY.
INC CX
;INC DI
add di,2 ;<=== EACH MULTIPLE IS TWO BYTES.
JMP MULTIPLY
FINISHED:
;WHEN PREVIOUS BLOCK FINISHES "DI" POINTS AT THE END OF "BUF".
;MOV CX, 0 ;<=== CX IS ZERO 6 LINES BELOW.
MOV DX,0
MOV BX,10D
mov si, offset buf ;<=== SI POINTS TO THE ARRAY OF MULTIPLES.
OUTER:
MOV CX, 0 ;<=== CX MUST BE HERE (IT'S DIGIT COUNTER).
MOV AX,[SI] ;<=== RETRIEVE A MULTIPLE FROM ARRAY.
SHOW: ;push to stack and print digit by digit
MOV DX,0
DIV BX
PUSH DX
INC CX
CMP AX,0
JNZ SHOW
PRINT:
MOV DX,0
MOV AH,02H
POP DX
ADD DL,30H
INT 21H
LOOP PRINT
;DISPLAY COMMA SEPARATOR.
mov ah,9
mov dx,offset comma
int 21h
;INC SI
add si, 2 ;<=== NEXT MULTIPLE TO DISPLAY.
CMP SI, DI ;<=== IF SI IS NOT IN THE END
JNE OUTER
INPUT PROC
ACCEPT:
MOV AH,01H
INT 21H
CMP AL,0DH
JZ CONTINUE
MOV AH,0
SUB AL,30H
PUSH AX
MOV AX,10D
MUL BX
POP BX
ADD BX,AX
JMP ACCEPT
CONTINUE:
RET
INPUT ENDP
EXIT:
MOV AH,4CH
INT 21H
ENDP MAIN
END MAIN
我有点想知道为什么你甚至为此使用 MUL(除非你真的因为分配规则而使用)。如果这在生产中是真正的问题,您可以用更简单的方式编写整个内容,例如:
...
; bx = input number (valid 1..99)
; prepare es:di for STOS instruction to store results
MOV ax,@DATA
MOV es,ax
MOV di,OFFSET multipliedNumbers
CLD
; init loop variables
XOR ax,ax
XOR cx,cx
multiplyLoop:
ADD ax,bx ; calculate next result
CMP ax,100
JGE endMultiplyLoop
STOSW ; store result
INC cx ; counting results
JMP multiplyLoop
endMultiplyLoop:
; multipliedNumbers array contains cx numbers
...
; BTW, to process them, set ds:si to results array
MOV si,OFFSET multipliedNumbers
; I assume ds already points to @DATA here
; and DF is still 0 (no STD since CLD)
; cx should be still number of stored results
readNumbersLoop:
LODSW ; ax = number, si+=2
; do whatever you want with ax *here*
; but keep ds:si and cx intact (or PUSH/POP them)
LOOP readNumbersLoop
.DATA
multipliedNumbers DW 100 DUP (?)
虽然这个变体在现代 CPU 中绝不是表现最好的,但它应该 "human" 可读性强,而且不难理解。我喜欢在这些情况下使用 STOS/LODS,因为它们可以避免像原来的 INC DI
而不是 ADD DI,2
.
这样的错误
当我意识到 Pentium/686 附近的某处时,直截了当的 MOV [di],ax ADD di,2
更快(尤其是与其他操作交错时),我感到有些难过。就像现在人们用 ADD reg,1
而不是 INC
一样。可怜的增量,几乎从 CPU 开始就在这里,现在变得毫无用处……;) :D
编辑:
如果你真的不得不使用 MUL
,那么你就有点没做好功课(同时使用 dx
和 MUL r16
):/.
手头有一个指令参考指南,并经常检查指令的细节(比如哪些标志受到影响等)。
不时阅读所有这些内容也有帮助,如果您碰巧做了一些 256B 的介绍,回忆起 XLAT
之类的奇怪事物可能会使整个世界变得不同(在适应这 256 个字节之间) , 或者不是).
与 TASM 一起提供的原始参考指南非常方便,小巧而简短,基于英特尔文档,并且在我的修订版中已经包含 386 条指令。恐怕我忘了return它回到大学,因为我用得太多了...
这就是代码的作用。假设用户输入数字 20。它将此值与计数器 CX 相乘,设置为 1,并在每次迭代时增加。当结果达到或超过 100(数字 20 的 4 次迭代)时,它返回并通过压入和弹出堆栈来打印所有结果。 每个数字相加,然后分隔成单个数字,然后使用 02 函数显示。
MOV CX, 01D
MOV AX, 0
MOV DX, BX ; BX contains the number
MOV DI, 01 ;
MOV SI, 01 ; used for addressing offsets of BX
MULTIPLY:
MOV AX, DX
MUL CX
CMP AX, 100D
JGE FINISHED
MOV [BX+DI], AX
INC CX
INC DI ; will be changed to ADD DI, 2
JMP MULTIPLY
这段代码有问题。我试图让它将输入乘以 1,然后乘以 2,然后乘以 3,依此类推,直到结果达到 100。我在这里错过了什么?
代码中的一个问题:在标签 multiply
之前,您将输入的数字存储在 DX 中:
MOV DX, BX ; BX contains the number
但在标签 multiply
之后使用 mul
,这会破坏 DX。解决方案是使用另一个寄存器,例如 BP,而不是 DX。
另一个重要的问题是您将倍数存储在[BX+DI]
中,但是BX指向的数据段中没有变量。解决方案是创建一个数组 ("buf").
现在让我们将这两种解决方案应用到您的原始代码中,我添加了一个逗号来分隔数字,更改由箭头指出(;<===
):
.MODEL SMALL
.STACK 100H
.DATA
S1 DB 13,10, "Enter a number below 100 :" , '$'
S3 DB 13,10, "The multiples are: ", '$'
buf dw 100 dup(?) ;<=== ARRAY OF MULTIPLES.
comma db ",$" ;<=== SEPARATOR BETWEEN MULTIPLES ON SCREEN.
.CODE
MAIN PROC
MOV AX,@DATA
MOV DS,AX
LEA DX, S1
MOV AH, 09 ; ENTER A NUMBER?
INT 21H
CALL INPUT ;ACCEPTS A NUMBER
LEA DX, S3
MOV AH, 09 ; THE MULTIPLES ARE:
INT 21H
MOV CX, 01D
MOV AX, 0
MOV bp, BX ;<=== PRESERVE INPUT NUMBER IN BP.
mov di, offset buf ;<=== DI POINTS TO ARRAY OF MULTIPLES.
;MOV DI, 01
;MOV SI, 01
MULTIPLY: ;where the good stuff happens
MOV AX, BP ;<=== MUL WILL CHANGE DX, THAT'S WHY WE USE BP.
MUL CX ;AX*CX = RESULT IN DX:AX.
CMP AX, 100D
JGE FINISHED
MOV [di], AX ;<=== STORE MULTIPLE IN ARRAY.
INC CX
;INC DI
add di,2 ;<=== EACH MULTIPLE IS TWO BYTES.
JMP MULTIPLY
FINISHED:
;WHEN PREVIOUS BLOCK FINISHES "DI" POINTS AT THE END OF "BUF".
;MOV CX, 0 ;<=== CX IS ZERO 6 LINES BELOW.
MOV DX,0
MOV BX,10D
mov si, offset buf ;<=== SI POINTS TO THE ARRAY OF MULTIPLES.
OUTER:
MOV CX, 0 ;<=== CX MUST BE HERE (IT'S DIGIT COUNTER).
MOV AX,[SI] ;<=== RETRIEVE A MULTIPLE FROM ARRAY.
SHOW: ;push to stack and print digit by digit
MOV DX,0
DIV BX
PUSH DX
INC CX
CMP AX,0
JNZ SHOW
PRINT:
MOV DX,0
MOV AH,02H
POP DX
ADD DL,30H
INT 21H
LOOP PRINT
;DISPLAY COMMA SEPARATOR.
mov ah,9
mov dx,offset comma
int 21h
;INC SI
add si, 2 ;<=== NEXT MULTIPLE TO DISPLAY.
CMP SI, DI ;<=== IF SI IS NOT IN THE END
JNE OUTER
INPUT PROC
ACCEPT:
MOV AH,01H
INT 21H
CMP AL,0DH
JZ CONTINUE
MOV AH,0
SUB AL,30H
PUSH AX
MOV AX,10D
MUL BX
POP BX
ADD BX,AX
JMP ACCEPT
CONTINUE:
RET
INPUT ENDP
EXIT:
MOV AH,4CH
INT 21H
ENDP MAIN
END MAIN
我有点想知道为什么你甚至为此使用 MUL(除非你真的因为分配规则而使用)。如果这在生产中是真正的问题,您可以用更简单的方式编写整个内容,例如:
...
; bx = input number (valid 1..99)
; prepare es:di for STOS instruction to store results
MOV ax,@DATA
MOV es,ax
MOV di,OFFSET multipliedNumbers
CLD
; init loop variables
XOR ax,ax
XOR cx,cx
multiplyLoop:
ADD ax,bx ; calculate next result
CMP ax,100
JGE endMultiplyLoop
STOSW ; store result
INC cx ; counting results
JMP multiplyLoop
endMultiplyLoop:
; multipliedNumbers array contains cx numbers
...
; BTW, to process them, set ds:si to results array
MOV si,OFFSET multipliedNumbers
; I assume ds already points to @DATA here
; and DF is still 0 (no STD since CLD)
; cx should be still number of stored results
readNumbersLoop:
LODSW ; ax = number, si+=2
; do whatever you want with ax *here*
; but keep ds:si and cx intact (or PUSH/POP them)
LOOP readNumbersLoop
.DATA
multipliedNumbers DW 100 DUP (?)
虽然这个变体在现代 CPU 中绝不是表现最好的,但它应该 "human" 可读性强,而且不难理解。我喜欢在这些情况下使用 STOS/LODS,因为它们可以避免像原来的 INC DI
而不是 ADD DI,2
.
当我意识到 Pentium/686 附近的某处时,直截了当的 MOV [di],ax ADD di,2
更快(尤其是与其他操作交错时),我感到有些难过。就像现在人们用 ADD reg,1
而不是 INC
一样。可怜的增量,几乎从 CPU 开始就在这里,现在变得毫无用处……;) :D
编辑:
如果你真的不得不使用 MUL
,那么你就有点没做好功课(同时使用 dx
和 MUL r16
):/.
手头有一个指令参考指南,并经常检查指令的细节(比如哪些标志受到影响等)。
不时阅读所有这些内容也有帮助,如果您碰巧做了一些 256B 的介绍,回忆起 XLAT
之类的奇怪事物可能会使整个世界变得不同(在适应这 256 个字节之间) , 或者不是).
与 TASM 一起提供的原始参考指南非常方便,小巧而简短,基于英特尔文档,并且在我的修订版中已经包含 386 条指令。恐怕我忘了return它回到大学,因为我用得太多了...