我正在学习汇编语言( 16位DOS上的TASM),并尝试使用0Ah DOS服务将文本直接读入堆栈。它在emu8086中工作得很好,而当我用实际的TASM运行它时-它不会给任何用户输入(根本没有输入,看起来就像是跳过了INT 21h )。
下面是我是如何使用它的:
PROC _readNum USES BP AX BX CX DX SI
PUSH BP
MOV BP, SP
SUB SP, 7
MOV AH, 0Ah ;Buffered input
MOV [BP-7], 5 ;Max number of characters to read (4 + 1 for enter)
LEA DX, [BP-7]
INT 21h ;This interrupt seems to be doing nothing at all
...会有什么问题呢?我是否以错误的方式引用了堆栈?提前谢谢。
下面是完整的代码,以防万一:
ascii_offset EQU 30h
.model small
.stack 100h
.data
; add your data here!
outStrA DB "Input A: $"
outStrB DB "Input B: $"
resultStr DB "Result of $"
plusStr DB "+$"
equalsStr DB " is $"
eol DB 13, 10, "$"
cnt DB 10
rcnt DB 0
buf DB 11 dup("$")
PRINT MACRO op1
MOV AH, 09h
LEA DX, op1
INT 21h
ENDM
PRINTLN MACRO op1
PRINT op1
PRINT eol
ENDM
PRINTEOL MACRO
PRINT eol
ENDM
.code
PROC _printNum USES BP AX BX DX SI
PUSH BP
MOV BP, SP
SUB SP, 6 ;Max number to print is 5 + $ sign
MOV AX, [BP+2]
MOV DX, 0h ;Is required to divide a double pair
MOV BH, 0h
MOV BL, 10 ;Divisor
LEA SI, [BP-1] ;Our string stored in memory (from end)
MOV byte ptr [SI], "$" ;End of str
_printNum_loop:
DIV BX ;Result is in AX:DX
ADD DL, ascii_offset ;Convert to ASCII
DEC SI
MOV [SI], DL
MOV DX, 0h ;Reset DX to divide again
CMP AX, 0h ;If AX is 0
JNE _printNum_loop
PRINT [SI]
MOV SP, BP
POP BP
RET 2
ENDP
PROC _readNum USES BP AX BX CX DX SI
PUSH BP
MOV BP, SP
SUB SP, 7
MOV AH, 0Ah ;Output to screen
MOV [BP-7], 5 ;Max number of characters to read (4 + 1 for enter)
LEA DX, [BP-7]
INT 21h
MOV AX, 0h ;Result
MOV BX, 0h ;Temporary result
MOV CX, 0h ;Loop counter
MOV CL, [BP-6] ;Loop counter
LEA SI, [BP-5] ;Starting position to read number
_readNum_strloop:
MOV DX, 10 ; ;Will multiply AX by DX
MUL DX ; AX = AX * DX
MOV BL, [SI]
SUB BL, 30h
ADD AX, BX
INC SI
LOOP _readNum_strloop
MOV SP, BP
POP BP
RET 0
ENDP
start:
; set segment registers:
MOV AX, @data
MOV DS, AX
MOV ES, AX
PUSH 0ABCDh
JMP _printNum
MOV AX, 4c00h ; exit to operating system.
INT 21h
END start ; set entry point and stop the assembler.发布于 2012-09-19 23:06:44
详述Alexey Frunze的答案:
您的代码指定了一个小模型。该指令告诉汇编器假定DS = SS,但它不(不能)强制它。
当DOS加载EXE时,它将SS:SP设置为在文件头中找到的位置。它将DS设置为PSP的开头,这样您就可以获取命令行参数等等。
DOS缓冲读取功能期望缓冲区位于DS:DX。您已经使用BP-7 (缓冲区的开始)正确地加载了DX,但您还需要在进行函数调用之前设置DS←SP。您可以使用以下命令完成此操作
MOV AX,SS
MOV DS,AX或
PUSH SS
POP DS没有MOV DS、SS指令。
发布于 2012-09-19 21:52:53
可能会有一些bug。你能编译这个吗?
PROC _printNum USES BP AX BX DX SI
PUSH BP
MOV BP, SP
SUB SP, 6 ; Max number to print is 5 + $ sign 在这里,即使您不使用sp进行寻址,或者在此过程结束时在mov sp,bp之前使用a作为源,也要修改sp,因此此指令是不必要的。
MOV AX, [BP+2]这不是bug,只是想知道..。ax现在应该是0xABCD,对吗?
MOV DX, 0h ; Is required to divide a double pair
MOV BH, 0h
MOV BL, 10 ; Divisor
LEA SI, [BP-1] ; Our string stored in memory (from end)
MOV byte ptr [SI], "$" ; End of str
_printNum_loop:
DIV BX ; Result is in AX:DXdiv bx之后的结果不在ax:dx中。在ax中,你有商,在dx中,你有余数。
然后在这里:
PROC _readNum USES BP AX BX CX DX SI
PUSH BP
MOV BP, SP
SUB SP, 7为什么选择这个sub sp,7?首先,您应该保持sp对齐为2,其次,如果您不将sp用于寻址,或者在此过程结束时将其用作mov sp,bp之前的源,为什么要在此处修改它?
MOV AH, 0Ah ; Output to screen
MOV [BP-7], 5 ; Max number of characters to read (4 + 1 for enter)在这里你应该定义操作数的大小,例如。mov byte [bp-7],5或mov word [bp-7],5。或者对于ptr,如果需要的话,我不知道是mov byte ptr [bp-7],5还是mov word ptr [bp-7],5。
LEA DX, [BP-7]
INT 21h这是不合理的。根据DOS INT 21h - DOS Function Codes documentation,mov ah, 0x0a; int 21是缓冲的输入,而ds:dx应该指向输入缓冲区。可能你的评论“输出到屏幕”是不正确的?
MOV AX, 0h ; Result
MOV BX, 0h ; Temporary result
MOV CX, 0h ; Loop counter
MOV CL, [BP-6] ; Loop counter
LEA SI, [BP-5] ; Starting position to read number
_readNum_strloop:
MOV DX, 10 ; Will multiply AX by DX
MUL DX ; AX = AX * DX
MOV BL, [SI]
SUB BL, 30h
ADD AX, BX
INC SI
LOOP _readNum_strloop我认为在这个循环中你有溢出的风险。适合任何16位寄存器(如ax )的最大数字是2^16-1 == 65535。
发布于 2012-09-19 22:06:39
我没有看过所有的代码,但是有一个问题是你的数据和堆栈段是不同的,也就是说,程序以ds!=ss开头。
因此,LEA DX, [BP-7]不会为DOS输入函数提供正确的地址,因为它在ds:dx中应该是正确的地址,而您的缓冲区却在ss:dx和ds!=ss。下面的int 21h调用可能会覆盖一些内存并导致挂起或崩溃。
您需要使用正确的地址。
更新:一些细节。
下面是您的程序的一个略微修改的版本(问题中的代码在TASM 3.2中不能很好地组装):
UPDATE2:忘记提到所有更改的行都包含以;;;;开头的注释。
ascii_offset EQU 30h
.model small, C ;;;; added ", C"
.stack 100h
.data
; add your data here!
outStrA DB "Input A: $"
outStrB DB "Input B: $"
resultStr DB "Result of $"
plusStr DB "+$"
equalsStr DB " is $"
eol DB 13, 10, "$"
cnt DB 10
rcnt DB 0
buf DB 11 dup("$")
PRINT MACRO op1
MOV AH, 09h
LEA DX, op1
INT 21h
ENDM
PRINTLN MACRO op1
PRINT op1
PRINT eol
ENDM
PRINTEOL MACRO
PRINT eol
ENDM
.code
_printNum PROC USES BP AX BX DX SI ;;;; reordered PROC and _printNum
PUSH BP
MOV BP, SP
SUB SP, 6 ;Max number to print is 5 + $ sign
MOV AX, [BP+2]
MOV DX, 0h ;Is required to divide a double pair
MOV BH, 0h
MOV BL, 10 ;Divisor
LEA SI, [BP-1] ;Our string stored in memory (from end)
MOV byte ptr [SI], "$" ;End of str
_printNum_loop:
DIV BX ;Result is in AX:DX
ADD DL, ascii_offset ;Convert to ASCII
DEC SI
MOV [SI], DL
MOV DX, 0h ;Reset DX to divide again
CMP AX, 0h ;If AX is 0
JNE _printNum_loop
PRINT [SI]
MOV SP, BP
POP BP
RET 2
ENDP
_readNum PROC USES BP AX BX CX DX SI ;;;; reordered PROC and _readNum
PUSH BP
MOV BP, SP
SUB SP, 7
MOV AH, 0Ah ;Output to screen
MOV byte ptr [BP-7], 5 ;Max number of characters to read (4 + 1 for enter) ;;;; added "byte ptr"
LEA DX, [BP-7]
INT 21h
MOV AX, 0h ;Result
MOV BX, 0h ;Temporary result
MOV CX, 0h ;Loop counter
MOV CL, [BP-6] ;Loop counter
LEA SI, [BP-5] ;Starting position to read number
_readNum_strloop:
MOV DX, 10 ; ;Will multiply AX by DX
MUL DX ; AX = AX * DX
MOV BL, [SI]
SUB BL, 30h
ADD AX, BX
INC SI
LOOP _readNum_strloop
MOV SP, BP
POP BP
RET 0
ENDP
start:
; set segment registers:
MOV AX, @data
MOV DS, AX
MOV ES, AX
PUSH 0ABCDh
JMP _printNum
MOV AX, 4c00h ; exit to operating system.
INT 21h
END start ; set entry point and stop the assembler.汇编(使用TASM3.2)作为tasm stkkbinp.asm,没有错误或警告。
已链接(使用TLINK3.01)作为tlink /m /s stkkbinp,没有错误或警告。
链接器生成的映射文件是:
Start Stop Length Name Class
00000H 00088H 00089H _TEXT CODE
00090H 000C5H 00036H _DATA DATA
000D0H 001CFH 00100H STACK STACK
Detailed map of segments
0000:0000 0089 C=CODE S=_TEXT G=(none) M=STKKBINP.ASM ACBP=48
0009:0000 0036 C=DATA S=_DATA G=DGROUP M=STKKBINP.ASM ACBP=48
0009:0040 0100 C=STACK S=STACK G=DGROUP M=STKKBINP.ASM ACBP=74
Address Publics by Name
Address Publics by Value
Program entry point at 0000:0070现在在TD中打开程序并执行前3条指令:

您可以在屏幕上清楚地看到ds不等于ss。它们相隔4个,首先在0x4caf处进入.data段,然后在0x4cb3处进入.stack。就段值而言,这4的差值相当于4*0x10=0x40字节。
如果你看上面的地图文件,你会看到,实际上,这是这些线段之间的距离:
0009:0000 0036 C=DATA S=_DATA G=DGROUP M=STKKBINP.ASM ACBP=48
0009:0040 0100 C=STACK S=STACK G=DGROUP M=STKKBINP.ASM ACBP=74我无法解释为什么映射文件显示了这两个(9)的相同段,但链接的可执行文件却有不同的段。但这就是输入不能正常工作的原因。
https://stackoverflow.com/questions/12495236
复制相似问题