仿真器使用int21h / ah = 09h显示“2000字节后找不到错误字节24h”

时间:2016-04-30 15:11:41

标签: assembly x86 dos emu8086 x86-16

我必须使用 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 个答案:

答案 0 :(得分:3)

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

  

INT 21h,AH = 09h -   地址:170B5   2000字节后找不到字节24h。   ; INT 21h / 9h的正确示例:   mov dx,offset msg   mov啊,9   int 21h   RET   msg db“Hello $”

在任何想象中,我都不是 EMU8086 的专家。我知道为什么你的抵消不起作用。我无法告诉您是否有正确的解决方法,或者它是 EMU8086 错误。在这个模拟器上有更好背景的人会知道。

您已创建包含一些变量的data细分。对我来说似乎没问题(但我可能会遗漏一些东西)。我决定加载 EMU8086 来实际尝试此代码。它没有错误地组装。使用调试器,我单步进入程序开头附近的push offset msg1行。我知道正在进行的指令编码。这是我看到的解码指令:

Instruction decoding

它显示指令编码为push 0b5h,其中0b5h是偏移量。问题是它被编码为push imm8。左侧窗格中的两个突出显示的字节显示它是用这些字节编码的:

6A B5 

如果您查看instruction set reference,您会发现使用6A编码的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位的值。指令集引用实际上是这样说的:

  

如果源操作数是一个小于操作数大小的立即数,则在栈上按下符号扩展值

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

Stack

观察堆栈上的值是FFB5。我找不到合适的语法(甚至使用word修饰符)来强制EMU8086将其编码为push imm16push 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位寄存器压入堆栈。

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

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

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

答案 1 :(得分:0)

我查看了您的代码,并专注于以下一系列说明:

template/templateUrl

所以你的序列

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

基本上意味着,您将BX设置为&#39; choice&#39;的地址,然后将&#39; choice&#39;([BX])设置为AL并将其复制回AL。

这是多余的。

之后,您将该char与&#39; +&#39;进行比较。和...

  • 如果该字母等于&#39; +&#39;,则会使用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 获取下一个字符,然后继续call getNums
  • 如果该字符不等于&#39; +&#39;,则将其与&#39; x&#39;,exit-char进行比较。如果它不是&#39; x,则会落到cont:

此处没有错误。

因此,cont:menu1的问题可能源于您的字符串中包含的某些转义字符,例如menu2%/。例如,\是某些汇编程序中的MACRO字符,可能会产生问题。

答案 2 :(得分:0)

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

结尾

答案 3 :(得分:-1)

我找到了一个解决方案..我不知道为什么它取决于它但是......这里是:

data segment
    choice db ?
    snum1 db 4 dup(?)
    snum2 db 4 dup(?)
    num1 db ?
    num2 db ?
    res db ?
    sres db 4 dup(?)

    msg1 db "Enter first number$"
    msg2 db "Enter second number$"
    msg3 db "Press any key to procced$"
    msg4 db "The result is $"
    menu1 db "Chose a function to procced", 10, 13, "Add [+]", 10, 13, "Sub [-]", 10, 13, "Mul [*]", 36
    menu2 db "Div [/]", 10, 13, "Mod [%]", 10, 13, "Pow [^]", 10, 13, "Exit [x]", 36
ends

通过将字符串排序从最短到最长,问题解决了......我认为这是关于语言的。