模拟器使用 int21h/ah=09h 显示“2000 个字节后未找到错误字节 24h”

2023-12-07

我必须使用汇编来做一个简单的计算器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,但这并没有帮助。请帮我找出问题所在。


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

通常,这意味着您的代码或数据有错误,例如在固定字符串中你忘记了$最后,或者如果将字节复制到缓冲区中,那么您可能会覆盖或从未存储过'$'首先。

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


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

INT 21h,AH=09h - 地址:170B5 2000 字节后未找到字节 24h。 ; INT 21h/9h 的正确示例: mov dx, 偏移量消息 移动啊,9 21小时内 雷特 消息数据库“你好$”

我不是这方面的专家EMU8086任何想象力。我知道为什么你的抵消不起作用。我无法告诉你是否有适当的方法来解决这个问题,或者是否是一个EMU8086漏洞。对这个模拟器有更好背景的人会知道。

您已经创建了一个data带有一些变量的段。对我来说似乎没问题(但我可能错过了一些东西)。我决定加载EMU8086实际尝试这个代码。它组装没有错误。使用调试器我单步执行到push offset msg1位于程序开头附近的行。我从指令编码中立刻就知道发生了什么。这是我看到的解码指令:

Instruction decoding

它显示指令被编码为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)那么问题是什么?可以压入堆栈的最小值push在 16 位模式下是一个 16 位字。由于字节小于字,因此处理器会获取该字节并对其进行符号扩展以生成 16 位值。指令集参考实际上是这样说的:

如果源操作数是小于操作数大小的立即数,则将符号扩展值推送到堆栈上

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

Stack

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

你可以做两件事。您可以将 256 字节的虚拟数据放入您的data像这样的分段:

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 位寄存器压入堆栈。

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


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


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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

模拟器使用 int21h/ah=09h 显示“2000 个字节后未找到错误字节 24h” 的相关文章

  • x86-64 上这个语句有什么问题?

    该函数的目的是获取堆栈的起始地址 unsigned long find start void asm movq rsp eax 当我编译它时 出现错误 Error suffix or operands invalid for movq mo
  • 遍历内存编辑每个字节

    我正在编写汇编代码 提示用户输入一串小写字符 然后输出包含所有大写字符的相同字符串 我的想法是迭代从特定地址开始的字节 并从每个字节中减去 20H 将小写变为大写 直到到达具有特定值的字节 我对 Assembly 相当缺乏经验 所以我不确定
  • 预取双类成员需要转换为 char*?

    我有一个正在使用的课程 mm prefetch 预先请求包含 double 类型的类成员的缓存行 class MyClass double getDouble return dbl other members double dbl othe
  • 为什么 SSE 对齐读取 + 随机播放在某些 CPU 上比未对齐读取慢,而在其他 CPU 上则不然?

    在尝试优化有限差分代码所需的未对齐读取时 我更改了未对齐的负载 如下所示 m128 pm1 mm loadu ps H k 1 进入这个对齐的读取 随机播放代码 m128 p0 mm load ps H k m128 pm4 mm load
  • AVX512 掩码寄存器(k1...k7)的 GNU C 内联 asm 输入约束?

    AVX512 为其算术命令引入了 opmask 功能 一个简单的例子 上帝螺栓 org https godbolt org z P7xWD8 include
  • Clang 使用 -nostdlib 生成崩溃代码

    我正在尝试为可执行文件设置自己的运行时环境 但无法使用 clang v3 4 1ubuntu1 目标 x86 64 pc linux gnu 来生成没有段错误的可执行文件 我已将问题简化为以下内容 如果我有一个文件 crt1 c 除了满足
  • 即使我确实为变量设置了初始值,数据段也没有被初始化

    我已经编写了一个代码 该代码应该生成某种数字列表 但是即使我为它们分配了初始值 我的数据段变量也没有被初始化 This is how DS 0000 looks when I run it 这是我的代码 但数据段只保留垃圾值 MODEL s
  • 在 x86 程序集中将整数打印到控制台

    当我在 16 位汇编中添加两个值时 将结果打印到控制台的最佳方法是什么 目前我有这个代码 CODE START mov ax 1 put 1 into ax add ax 2 add 2 to ax current value mov ah
  • 将两个 32 位整数向量相乘,生成 32 位结果元素向量

    将每个 32 位条目乘以 2 的最佳方法是什么 mm256i互相注册 mm256 mul epu32不是我正在寻找的 因为它产生 64 位输出 我想要每个 32 位输入元素都有一个 32 位结果 而且 我确信两个 32 位值的乘法不会溢出
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • Visual Studio 2017 上的简单装配程序

    386 model flat c stack 100h printf PROTO arg1 Ptr Byte data msg1 byte Hello World 0Ah 0 code main proc INVOKE printf ADD
  • 在linux x86平台上学习ARM所需的工具[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有一个 x86 linux 机器 在阅读一些关于 ARM 的各种信息时 我很好奇 现在我想花一些时间学
  • 为什么x86分页没有特权环的概念?

    早在 1982 年 当 Intel 发布 80286 时 他们在分段方案中添加了 4 个特权级别 环 0 3 由全局描述符表 GDT 和局部描述符表 LDT 中的 2 位指定 在 80386 处理器中 Intel 添加了分页功能 但令人惊讶
  • 为什么 GCC 不将 a*a*a*a*a*a 优化为 (a*a*a)*(a*a*a)?

    我正在对科学应用程序进行一些数值优化 我注意到的一件事是 GCC 会优化调用pow a 2 通过将其编译成a a 但是调用pow a 6 没有优化 实际会调用库函数pow 这大大降低了性能 相比之下 英特尔 C 编译器 http en wi
  • 用于预乘 ARGB 的 SSE alpha 混合

    我正在尝试编写一个支持 SSE 的 alpha 合成器 这就是我想出的 首先 混合两个 4 像素向量的代码 alpha blend two 128 bit 16 byte SSE vectors containing 4 pre multi
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • 使用 NEON 优化 Cortex-A8 颜色转换

    我目前正在执行颜色转换例程 以便从 YUY2 转换为 NV12 我有一个相当快的函数 但没有我预期的那么快 主要是由于缓存未命中 void convert hd uint8 t orig uint8 t result uint32 t wi
  • 使用 MIPS 从 Big Endian 到 Little Endian 无需逻辑运算?

    我正在使用 MIPS QtSpim 将 32 位字从 Big Endian 转换为 Little Endian 我下面显示的内容已检查且正确 不过我想知道还有什么其他方法可以让我进行转换 我虽然只使用了旋转和移位 但如果没有逻辑运算 我就无
  • Linux内核页表更新

    在linux x86 中分页 每个进程都有它自己的页面目录 页表遍历从 CR3 指向的页目录开始 每个进程共享内核页目录内容 假设三个句子是正确的 假设某个进程进入内核 模式并更新他的内核页目录内容 地址映射 访问 权利等 问题 由于内核地
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作

随机推荐