EMU 8086 在寄存器中存储错误的数组元素值

Posted

技术标签:

【中文标题】EMU 8086 在寄存器中存储错误的数组元素值【英文标题】:EMU 8086 stores wrong array element value in register 【发布时间】:2021-09-12 16:00:47 【问题描述】:
.model small
.data
nizA db 1,2,3,4,5,6,7,8   
nizB db 8 dup(?)
len equ 8
.code   
main proc
mov si,0
mov di,0
mov cx,len

program: 
    mov al,nizA[si]      ;problem is here it always stores CDh in AL
    cbw                  ;convert AL to AX so i can divide
    mov bl,2             ;The number I want to divide so I test if its even or not
    div bl               ;Overflow message
    cmp ah,0
    je next:
        input:
            mov dl,nizA[si]
            mov nizB[di],dl
            inc di
next:
    add si,2
    loop program
endp
end

我这里有这段代码,用于我需要制作的决赛,非常简单。从给定数组的偶数索引中查找奇数。所以很自然,这意味着我将SI 增加 2,然后将该数组元素放入AL。但是无论数组中的数字是什么,在AL 中存储的始终是十六进制数字CD,然后,如果它按预期存储,我想做CBW 转换为AX 并成为能够与BL分开 我现在只有来自 EMU 8086 的错误消息说

除法错误 - 溢出。要手动处理此错误,请更改 中断向量表中INT 0的地址。

这肯定和CD号有关,我这里只有两个问题

    存储的是 CD 而不是 1 溢出(很可能是因为 CD 太高了,但我仍然将数字除以它们只会减少,所以这有点没有意义)

【问题讨论】:

我忘记了EXE文件是如何工作的——你必须在程序启动时自己加载数据段寄存器吗? 不不,它会自动完成 只是因为0xCDINT的操作码,这让我怀疑你是不是从代码段中获取数据。 在列表中,我有如下[ 5] 0000: 01 02 03 04 05 06 07 08 nizA db 1,2,3,4,5,6,7,8 [ 6] 0008: 00 00 00 00 00 00 00 00 nizB db 8 dup(?) 有点不清楚,首先是内存位置,其次是机器代码,第三是源代码所以我有点假设,它正在被阅读好?同样在符号表中,我的两个数组放在数据段中,我会查看 IP 是否指向正确的位置 【参考方案1】:

"正在存储 CD 而不是 1"

当 DOS .EXE 启动时,DS 段寄存器确实指向程序的 .data 部分! DS 段寄存器指向 ProgramSegmentPrefix aka PSP。这个 256 字节的区域是 DOS 保存有关正在运行的程序的一些重要数据的地方,并且您可以在偏移地址 128 处检索程序的命令行。

您在代码运行时发现了值 CDh,因为它恰好是 PSP 中的第一个字节。它是始终启动 PSP 的 int 20h 指令的操作码。

你需要写的内容如下:

.code   
main proc
mov ax, @data
mov ds, ax

“溢出(很可能是因为 CD 太高了,但我仍然将它们除以更少的数字,所以它有点没有意义)”

其实是有道理的。因为您使用CBW 将被除数扩展到AX,并且因为AL 中的值错误地为CDh,所以AX 中的新值变为FFCDh。当您的代码将其除以 2(使用 mov bl, 2div bl)时,商远大于可以存储在除法专用商寄存器 AL 中的商。这就是你得到“除法错误”的原因。

解决方案

验证这次部门是否正常工作的解决方案:
    mov  ax, @data
    mov  ds, ax
    mov  si, 0
    mov  di, 0
    mov  cx, len/2
program: 
    mov  al, nizA[si]
    mov  ah, 0           ; For UNSIGNED division don't use CBW
    mov  bl, 2
    div  bl
    cmp  ah, 0           ; Remainder
    je   next
    mov  dl, nizA[si]    ; Reload
    mov  nizB[di], dl
    inc  di
next:
    add  si, 2
    loop program
避免分裂的解决方案有利于TEST,并添加了一些额外的改进:
    mov  ax, @data
    mov  ds, ax
    xor  si, si        ; Better than 'mov si, 0` for zeroing a register
    xor  di, di        ; idem
program: 
    mov  al, nizA[si]
    test al, 1
    jz   IsEven
    mov  nizB[di], al  ; Only storing 'odd' values
    inc  di
IsEven:
    add  si, 2         ; Next 'even' index
    cmp  si, len
    jb   program

了解TEST AL, 1AL 寄存器的非破坏性如何,因此您无需在写入nizB 数组之前重新加载值? 另请注意,您并不总是需要单独的循环计数器。这里我使用了源数组索引。

【讨论】:

非常感谢你,这真的解释了一切,代码现在可以按要求工作。我不知道我应该加载数据,有时代码在不加载的情况下工作得很好,所以很有趣。例如,如果我只想为数组进行输入/输出而不划分和搜索数字,则来自数组mov dl,niz[si] mov ah,2h 的输入将打印出好的字符 @Завида 不访问内存的代码无需设置段寄存器即可。在您描述的输入和直接输出 niz 数组的情况下,这似乎工作正常(没有更改DS),因为这样代码将简单地覆盖重要的东西在 PSP 中。但是如果你覆盖这个 PSP 的足够多,你会看到程序崩溃(不返回操作系统)。【参考方案2】:

您使用8 加载cx,因此您的循环将迭代8 次。但是您在每次迭代时将 si 增加 2,因此在前 4 次迭代之后,您将超过 nizA 数组的末尾。

尝试将mov cx, len 替换为mov cx, len/2

顺便说一句,检查al 是否为奇数的一种更简单、更有效的方法是执行test al, 1,它根据al 与1 的按位与来设置标志。

【讨论】:

使用它之后,实际上没有任何标志发生变化,自从我开始程序以来所有标志都是 0 并且 IF 始终设置为 1 请显示更新后的代码。我猜不出你到底改变了什么。 @Завида:但这是有道理的,因为所有偶数数组元素实际上都是奇数(所以test al, 1 将导致零标志被清除)。 代码实际上是一样的,我只是把TEST AL,1放在MOV AL,nizA[si]下面,我让代码一步一步运行,我现在尝试到TEST AL,2ZF实际上是设置的到 1,这真的很奇怪,因为 AL 寄存器仍然在说 CD @Завида:如果你需要 DOS 仿真,Dosbox 是一个更好的仿真器,如果你只需要 Bios 或裸机,Bochs 是一个更好的仿真器。 EMU8086 是古老而有缺陷的。

以上是关于EMU 8086 在寄存器中存储错误的数组元素值的主要内容,如果未能解决你的问题,请参考以下文章

汇编LAHF指令学习 - 使用emu8086

使用emu8086学习汇编mov指令

emu8086的软件简介

Emu8086 - 无法打开文件

Emu8086三种格式的代码-(顺序,分支,循环)

使用emu8086学习汇编 int 21h 指令