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文件是如何工作的——你必须在程序启动时自己加载数据段寄存器吗? 不不,它会自动完成 只是因为0xCD
是INT
的操作码,这让我怀疑你是不是从代码段中获取数据。
在列表中,我有如下[ 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, 2
div 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, 1
对AL
寄存器的非破坏性如何,因此您无需在写入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,2
和ZF
实际上是设置的到 1,这真的很奇怪,因为 AL
寄存器仍然在说 CD
@Завида:如果你需要 DOS 仿真,Dosbox 是一个更好的仿真器,如果你只需要 Bios 或裸机,Bochs 是一个更好的仿真器。 EMU8086 是古老而有缺陷的。以上是关于EMU 8086 在寄存器中存储错误的数组元素值的主要内容,如果未能解决你的问题,请参考以下文章