5

我必须使用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,但没有帮助。请帮我找出它有什么问题。

4

3 回答 3

5

错误消息意味着您在不以(ASCII 24h)结尾的字符串上使用int 21h/ 。系统调用处理程序检查了 2000 个字节,但没有找到。AH=09h$

通常,这意味着您的代码或数据有问题,例如,在一个固定的字符串中,您$最后忘记了 a,或者如果将字节复制到缓冲区中,那么您可能一开始就覆盖或从未存储 a '$'

但在这种情况下,EMU8086 似乎有一个错误组装push offset msg4。(以一种将00B5h16 位地址截断为 8 位并符号扩展回 16 的方式,创建一个错误的指针,该指针指向$数据中任何字符所在的位置。)


根据下面的错误消息,我知道您正在使用EMU8086作为您的开发环境。

INT 21h,AH=09h - 地址:170B5 字节 24h 在 2000 字节后未找到。; 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 

如果您查看指令集参考,您会发现编码为PUSH指令的编码6A如下所示:

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 位值。指令集参考实际上是这样说的:

如果源操作数是大小小于操作数大小的立即数,则将符号扩展值压入堆栈

B5是二进制 10110101 。符号位是最左边的位。由于它是 1,放置到堆栈上的高 8 位将为 11111111b (FF)。如果符号位为 0,则 00000000b 放置在高 8 位中。模拟器没有放入00B5堆栈,它放置了FFB5. 这是不正确的!push 0b5h如果我单步执行指令并查看堆栈,则可以确认这一点。这是我看到的:

堆

观察放置在堆栈上的值是FFB5。我找不到合适的语法(即使使用word修饰符)来强制 EMU8086 将其编码为push imm16. Apush imm16将能够将整个单词编码为可行的push 00b5

你可以做两件事。您可以在您的data段中放置 256 字节的虚拟数据,如下所示:

data segment
db 256 dup(?)
choice db ?
... rest of data

为什么这行得通?在虚拟数据之后定义的每个变量都将是一个无法用单个字节表示的偏移量。因此,EMU8086 被迫编码push offset msg1为字推。

更清洁的解决方案是使用LEA指令。这是load effective address指示。它接受一个内存操作数并计算地址(在这种情况下是相对于数据段的偏移量)。您可以使用以下代码替换所有使用的代码offset

lea ax, [msg1]
push ax

AX可以是任何通用 16 位寄存器。进入寄存器后,将 16 位寄存器压入堆栈。

有人可能对此有更好的解决方案,或者知道解决此问题的方法。如果是这样,请随时发表评论。


鉴于上述信息,您可能会问为什么在移动数据时它似乎有效?原因是您重新组织所有字符串的方式(将长字符串放在最后)导致所有变量以小于 < 128 的偏移量开始。因此,8 位立即偏移符号的PUSH扩展了 0 in放在堆栈上时的最高位。偏移量是正确的。一旦偏移量 >= 128(并且 < 256),符号位为 1,并且放置在堆栈符号上的值的高 8 位将为 1 而不是 0。


您的程序中还有其他错误,我专注于与您收到的错误直接相关的问题。

于 2016-04-30T22:09:01.177 回答
0

我查看了您的代码并专注于以下指令序列:

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。

这是多余的。

之后,您将该字符与“+”进行比较,然后...

  • 如果那个 char 等于 '+',你会得到下一个 char ,call getNums然后继续cont:.
  • 如果该字符不等于“+”,则将其与“x”(退出字符)进行比较。如果不是'x',你会掉到cont:

这里没有错误。

因此,您的问题menu1andmenu2可能源于字符串中包含的一些转义字符,例如%, /, \. 例如,%是某些汇编程序中可能产生问题的宏字符。

于 2016-04-30T16:20:08.770 回答
-1

简单的解决方案是您的字符串应始终以 '$' 结尾,将 DUP(?) 更改为 DUP('$') 并且所有其他字符串以 ,'$' 结尾

于 2020-12-18T14:46:00.843 回答