模拟器使用 int21h/ah=09h 显示 "error byte 24h not found after 2000 bytes"
Emulator displays "error byte 24h not found after 2000 bytes" using int21h/ah=09h
我必须使用 EMU8086 在汇编中做一个简单的计算器,但每次我尝试启动它时 EMU8086 都会出现此错误:
INT 21h, AH=09h -
address: 170B5
byte 24h not found after 2000 bytes.
; correct example of INT 21h/9h:
mov dx, offset msg
mov ah, 9
int 21h
ret
msg db "Hello$"
我检查了其他的东西,没有错误:
data segment
choice db ?
snum1 db 4 dup(?)
snum2 db 4 dup(?)
sres db 4 dup(?)
num1 db ?
num2 db ?
res db ?
;;menu1 db "Chose a function to procced", 10, 13, "Add [+]", 10, 13, "Sub [-]", 10, 13
;;menu2 db "Mul [*]", 10, 13, "Div [/]", 10, 13, "Mod [%]", 10, 13, "Pow [^]", 10, 13, "Exit [x]$"
messStr db "Enter Your Choice:",10,13,"",10,13,"Add --> +",10,13,"Sub --> -",10,13,"Mul --> *",10,13,"Div --> /",10,13,"Mod --> %",10,13,"Pow --> ^",10,13,"Exit --> X",10,13,"$"
msg1 db "Enter first number$"
msg2 db "Enter second number$"
msg3 db "Press any key to procced$"
msg4 db "The result is $"
ends
stack segment
dw 128 dup(0)
ends
code segment
assume cs:code, ds:data, ss:stack
newline proc ;; new line
push ax
push dx
mov ah, 2
mov DL, 10
int 21h
mov ah, 2
mov DL, 13
int 21h
pop dx
pop ax
ret
endp
printstr proc ;; print string
push BP
mov BP, SP
push dx
push ax
mov dx, [BP+4]
mov ah, 9
int 21h
pop ax
pop dx
pop BP
ret 2
endp
inputstr proc ;; collect input
push BP
mov BP, SP
push bx
push ax
mov bx, [BP+4]
k1:
mov ah, 1
int 21h
cmp al, 13
je sofk
mov [bx], al
inc bx
jmp k1
sofk:
mov byte ptr [bx], '$'
pop ax
pop bx
pop BP
ret 2
endp
getNums proc ;; get the numbers
call newline
push offset msg1
call printstr
call newline
push offset snum1
call inputstr
call newline
push offset msg2
call printstr
call newline
push offset snum2
call inputstr
ret
endp
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
;; print the main menu
call newline
push offset msg4
call printstr
;; collect the input
call newline
mov bx, offset choice
mov ah, 1
int 21h
mov [bx], al
;; check it
mov al, choice
cmp al, '+'
jne cexit
call getNums
jmp cont
cexit:
cmp al, 'x'
je cend
cont:
;; pause before going to the main menu
call newline
push offset msg3
call printstr
mov bx, offset choice
mov ah, 1
int 21h
call newline
call newline
call newline
jmp start
cend:
mov ax, 4c00h
int 21h
ends
end start
我删掉了大部分代码段,因为它在这里不重要。
在试验代码后,我发现问题与数据段中消息的长度有关。 menu1
和 menu2
太长,它们之后的任何消息都无法打印(msg1
和 msg2
被打印,但它们之后什么也没有)。我检查了是否应该合并 menu1
& menu2
,但它没有帮助。请帮我找出问题所在。
我查看了您的代码并专注于以下指令序列:
mov bx, offset choice ; here you set BX to the address of 'choice'
mov ah, 1
int 21h ; here you 'READ CHARACTER FROM STANDARD INPUT, WITH ECHO'
mov [bx], al ; because INT 21h does preserve BX, you are writing back the result of the interrupt call (AL) back to the memory location at BX, which is named 'choice'
;; check it
mov al, choice ; HERE you are moving a BYTE variable named 'choice' to AL, overwriting the result of the last INT 21h call
cmp al, '+' ; ... and compare this variable to the ASCII value of '+'
jne cexit ; if this variable is unequal to '+' you jump to 'cexit'
call getNums ; otherwise you try to get another number from the input/STANDARD CONSOLE
所以你的序列
mov bx, offset choice ; here you set BX to the address of 'choice'
...
mov [bx], al ; because INT 21h does preserve BX, you ...
...
mov al, choice
本质上意味着,您将 BX 设置为 'choice' 的地址,然后将 'choice'([BX]) 设置为 AL 并将其复制回 AL。
这是多余的。
之后,将该字符与“+”进行比较,然后...
- 如果该字符等于“+”,您将使用
call getNums
获得下一个字符,然后继续使用 cont:
。
- 如果该字符不等于“+”,则将其与退出字符 'x' 进行比较。如果不是 'x',你会掉到
cont:
这里没有错误。
因此,menu1
和 menu2
的问题可能源于字符串中包含的一些转义字符,例如 %
、/
、\
。例如,%
在某些汇编程序中是一个 MACRO 字符,可能会产生问题。
错误消息表示您在未以 $
(ASCII 24h) 结尾的字符串上使用 int 21h
/ AH=09h
。系统调用处理程序检查了 2000 个字节,但没有找到一个。
通常,这意味着您的代码或数据存在错误,例如在固定字符串中,您忘记了最后的 $
,或者如果将字节复制到缓冲区中,那么您可能首先覆盖或从未存储 '$'
。
但在这种情况下,似乎EMU8086在组装时存在错误push offset msg4
。 (以一种将 00B5h
16 位地址截断为 8 位,并将符号扩展回 16 的方式,创建一个错误的指针,指向数据中任何 $
字符所在的位置。)
根据下面的错误消息,我知道您正在使用 EMU8086 作为您的开发环境。
INT 21h, AH=09h -
address: 170B5
byte 24h not found after 2000 bytes.
; correct example of INT 21h/9h:
mov dx, offset msg
mov ah, 9
int 21h
ret
msg db "Hello$"
无论怎么想,我都不是 EMU8086 方面的专家。我知道为什么你的补偿不起作用。我无法告诉您是否有解决此问题的正确方法,或者它是否是 EMU8086 错误。对这个模拟器有比较了解的人应该知道。
您已经创建了一个包含一些变量的 data
片段。对我来说似乎没问题(但我可能会遗漏一些东西)。我决定加载 EMU8086 来实际尝试这段代码。它组装没有错误。使用调试器,我单步执行到程序开头附近的 push offset msg1
行。我立即从指令编码中知道发生了什么。这是我看到的解码指令:
它表明指令被编码为 push 0b5h
,其中 0b5h 是偏移量。问题是它被编码为 push imm8
。左侧窗格中两个突出显示的字节显示它是用这些字节编码的:
6A B5
如果您查看用 6A
编码的 instruction set reference you'll find the encodings for PUSH 指令列为:
Opcode* Instruction Op/En 64-Bit Mode Compat/Leg Mode Description
6A ib PUSH imm8 I Valid Valid Push imm8.
你可能会说 B5
适合一个字节 (imm8) 那么问题是什么?在 16 位模式下,可以用 push
压入堆栈的最小值是一个 16 位字。由于一个字节小于一个字,因此处理器采用该字节并将其符号扩展为 16 位值。指令集参考实际上是这样说的:
If the source operand is an immediate of size less than the operand size, a sign-extended value is pushed on the stack
B5
是二进制 10110101 。符号位是最左边的位。因为它是 1,所以放入堆栈的高 8 位将是 11111111b (FF)。如果符号位为 0,则将 00000000b 置于高 8 位。模拟器没有将 00B5
放入堆栈,而是放置了 FFB5
。那是不正确的!如果我单步执行 push 0b5h
指令并查看堆栈,就可以确认这一点。这是我看到的:
观察到栈上的值是FFB5
。我找不到合适的语法(甚至使用 word
修饰符)来强制 EMU8086 将其编码为 push imm16
。 push imm16
可以将整个单词编码为 push 00b5
,这样就可以了。
您可以做两件事。您可以像这样在 data
段中放置 256 字节的虚拟数据:
data segment
db 256 dup(?)
choice db ?
... rest of data
为什么这行得通?在虚拟数据之后定义的每个变量都是一个不能用单个字节表示的偏移量。因为这个 EMU8086 被强制编码 push offset msg1
作为一个词 push.
更简洁的解决方案是使用 LEA 指令。这是 load effective address
指令。它接受一个内存操作数并计算地址(在本例中是相对于数据段的偏移量)。您可以将所有使用 offset
的代码替换为:
lea ax, [msg1]
push ax
AX 可以是任何通用 16 位寄存器。进入寄存器后,将 16 位寄存器压入堆栈。
有人可能对此有更好的解决方案,或者知道解决此问题的方法。如果是这样,请随时发表评论。
鉴于以上信息,您可能会问,为什么移动数据时似乎有效?原因是您重组所有字符串的方式(将长字符串放在最后)导致所有变量 start 的偏移量小于 < 128。因此 PUSH 的 8 位立即数偏移符号在放入堆栈时在顶部位扩展了一个 0。偏移量是正确的。一旦偏移量 >= 128(且 < 256),符号位为 1,堆栈符号上的值将具有高 8 位 1 而不是 0。
您的程序中还有其他错误,我正在关注与您收到的错误直接相关的问题。
简单的解决方案是您的字符串应始终以“$”结尾
将 DUO(?) 更改为 DUO('$') 并且所有其他字符串以 ,'$'
结尾
我必须使用 EMU8086 在汇编中做一个简单的计算器,但每次我尝试启动它时 EMU8086 都会出现此错误:
INT 21h, AH=09h - address: 170B5 byte 24h not found after 2000 bytes. ; correct example of INT 21h/9h: mov dx, offset msg mov ah, 9 int 21h ret msg db "Hello$"
我检查了其他的东西,没有错误:
data segment
choice db ?
snum1 db 4 dup(?)
snum2 db 4 dup(?)
sres db 4 dup(?)
num1 db ?
num2 db ?
res db ?
;;menu1 db "Chose a function to procced", 10, 13, "Add [+]", 10, 13, "Sub [-]", 10, 13
;;menu2 db "Mul [*]", 10, 13, "Div [/]", 10, 13, "Mod [%]", 10, 13, "Pow [^]", 10, 13, "Exit [x]$"
messStr db "Enter Your Choice:",10,13,"",10,13,"Add --> +",10,13,"Sub --> -",10,13,"Mul --> *",10,13,"Div --> /",10,13,"Mod --> %",10,13,"Pow --> ^",10,13,"Exit --> X",10,13,"$"
msg1 db "Enter first number$"
msg2 db "Enter second number$"
msg3 db "Press any key to procced$"
msg4 db "The result is $"
ends
stack segment
dw 128 dup(0)
ends
code segment
assume cs:code, ds:data, ss:stack
newline proc ;; new line
push ax
push dx
mov ah, 2
mov DL, 10
int 21h
mov ah, 2
mov DL, 13
int 21h
pop dx
pop ax
ret
endp
printstr proc ;; print string
push BP
mov BP, SP
push dx
push ax
mov dx, [BP+4]
mov ah, 9
int 21h
pop ax
pop dx
pop BP
ret 2
endp
inputstr proc ;; collect input
push BP
mov BP, SP
push bx
push ax
mov bx, [BP+4]
k1:
mov ah, 1
int 21h
cmp al, 13
je sofk
mov [bx], al
inc bx
jmp k1
sofk:
mov byte ptr [bx], '$'
pop ax
pop bx
pop BP
ret 2
endp
getNums proc ;; get the numbers
call newline
push offset msg1
call printstr
call newline
push offset snum1
call inputstr
call newline
push offset msg2
call printstr
call newline
push offset snum2
call inputstr
ret
endp
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
;; print the main menu
call newline
push offset msg4
call printstr
;; collect the input
call newline
mov bx, offset choice
mov ah, 1
int 21h
mov [bx], al
;; check it
mov al, choice
cmp al, '+'
jne cexit
call getNums
jmp cont
cexit:
cmp al, 'x'
je cend
cont:
;; pause before going to the main menu
call newline
push offset msg3
call printstr
mov bx, offset choice
mov ah, 1
int 21h
call newline
call newline
call newline
jmp start
cend:
mov ax, 4c00h
int 21h
ends
end start
我删掉了大部分代码段,因为它在这里不重要。
在试验代码后,我发现问题与数据段中消息的长度有关。 menu1
和 menu2
太长,它们之后的任何消息都无法打印(msg1
和 msg2
被打印,但它们之后什么也没有)。我检查了是否应该合并 menu1
& menu2
,但它没有帮助。请帮我找出问题所在。
我查看了您的代码并专注于以下指令序列:
mov bx, offset choice ; here you set BX to the address of 'choice'
mov ah, 1
int 21h ; here you 'READ CHARACTER FROM STANDARD INPUT, WITH ECHO'
mov [bx], al ; because INT 21h does preserve BX, you are writing back the result of the interrupt call (AL) back to the memory location at BX, which is named 'choice'
;; check it
mov al, choice ; HERE you are moving a BYTE variable named 'choice' to AL, overwriting the result of the last INT 21h call
cmp al, '+' ; ... and compare this variable to the ASCII value of '+'
jne cexit ; if this variable is unequal to '+' you jump to 'cexit'
call getNums ; otherwise you try to get another number from the input/STANDARD CONSOLE
所以你的序列
mov bx, offset choice ; here you set BX to the address of 'choice'
...
mov [bx], al ; because INT 21h does preserve BX, you ...
...
mov al, choice
本质上意味着,您将 BX 设置为 'choice' 的地址,然后将 'choice'([BX]) 设置为 AL 并将其复制回 AL。
这是多余的。
之后,将该字符与“+”进行比较,然后...
- 如果该字符等于“+”,您将使用
call getNums
获得下一个字符,然后继续使用cont:
。 - 如果该字符不等于“+”,则将其与退出字符 'x' 进行比较。如果不是 'x',你会掉到
cont:
这里没有错误。
因此,menu1
和 menu2
的问题可能源于字符串中包含的一些转义字符,例如 %
、/
、\
。例如,%
在某些汇编程序中是一个 MACRO 字符,可能会产生问题。
错误消息表示您在未以 $
(ASCII 24h) 结尾的字符串上使用 int 21h
/ AH=09h
。系统调用处理程序检查了 2000 个字节,但没有找到一个。
通常,这意味着您的代码或数据存在错误,例如在固定字符串中,您忘记了最后的 $
,或者如果将字节复制到缓冲区中,那么您可能首先覆盖或从未存储 '$'
。
但在这种情况下,似乎EMU8086在组装时存在错误push offset msg4
。 (以一种将 00B5h
16 位地址截断为 8 位,并将符号扩展回 16 的方式,创建一个错误的指针,指向数据中任何 $
字符所在的位置。)
根据下面的错误消息,我知道您正在使用 EMU8086 作为您的开发环境。
INT 21h, AH=09h - address: 170B5 byte 24h not found after 2000 bytes. ; correct example of INT 21h/9h: mov dx, offset msg mov ah, 9 int 21h ret msg db "Hello$"
无论怎么想,我都不是 EMU8086 方面的专家。我知道为什么你的补偿不起作用。我无法告诉您是否有解决此问题的正确方法,或者它是否是 EMU8086 错误。对这个模拟器有比较了解的人应该知道。
您已经创建了一个包含一些变量的 data
片段。对我来说似乎没问题(但我可能会遗漏一些东西)。我决定加载 EMU8086 来实际尝试这段代码。它组装没有错误。使用调试器,我单步执行到程序开头附近的 push offset msg1
行。我立即从指令编码中知道发生了什么。这是我看到的解码指令:
它表明指令被编码为 push 0b5h
,其中 0b5h 是偏移量。问题是它被编码为 push imm8
。左侧窗格中两个突出显示的字节显示它是用这些字节编码的:
6A B5
如果您查看用 6A
编码的 instruction set reference you'll find the encodings for PUSH 指令列为:
Opcode* Instruction Op/En 64-Bit Mode Compat/Leg Mode Description 6A ib PUSH imm8 I Valid Valid Push imm8.
你可能会说 B5
适合一个字节 (imm8) 那么问题是什么?在 16 位模式下,可以用 push
压入堆栈的最小值是一个 16 位字。由于一个字节小于一个字,因此处理器采用该字节并将其符号扩展为 16 位值。指令集参考实际上是这样说的:
If the source operand is an immediate of size less than the operand size, a sign-extended value is pushed on the stack
B5
是二进制 10110101 。符号位是最左边的位。因为它是 1,所以放入堆栈的高 8 位将是 11111111b (FF)。如果符号位为 0,则将 00000000b 置于高 8 位。模拟器没有将 00B5
放入堆栈,而是放置了 FFB5
。那是不正确的!如果我单步执行 push 0b5h
指令并查看堆栈,就可以确认这一点。这是我看到的:
观察到栈上的值是FFB5
。我找不到合适的语法(甚至使用 word
修饰符)来强制 EMU8086 将其编码为 push imm16
。 push imm16
可以将整个单词编码为 push 00b5
,这样就可以了。
您可以做两件事。您可以像这样在 data
段中放置 256 字节的虚拟数据:
data segment
db 256 dup(?)
choice db ?
... rest of data
为什么这行得通?在虚拟数据之后定义的每个变量都是一个不能用单个字节表示的偏移量。因为这个 EMU8086 被强制编码 push offset msg1
作为一个词 push.
更简洁的解决方案是使用 LEA 指令。这是 load effective address
指令。它接受一个内存操作数并计算地址(在本例中是相对于数据段的偏移量)。您可以将所有使用 offset
的代码替换为:
lea ax, [msg1]
push ax
AX 可以是任何通用 16 位寄存器。进入寄存器后,将 16 位寄存器压入堆栈。
有人可能对此有更好的解决方案,或者知道解决此问题的方法。如果是这样,请随时发表评论。
鉴于以上信息,您可能会问,为什么移动数据时似乎有效?原因是您重组所有字符串的方式(将长字符串放在最后)导致所有变量 start 的偏移量小于 < 128。因此 PUSH 的 8 位立即数偏移符号在放入堆栈时在顶部位扩展了一个 0。偏移量是正确的。一旦偏移量 >= 128(且 < 256),符号位为 1,堆栈符号上的值将具有高 8 位 1 而不是 0。
您的程序中还有其他错误,我正在关注与您收到的错误直接相关的问题。
简单的解决方案是您的字符串应始终以“$”结尾 将 DUO(?) 更改为 DUO('$') 并且所有其他字符串以 ,'$'
结尾