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

EMU 8086 stores wrong array element value in register

.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 的错误消息说

divide error - overflow. to manually process this error, change address of INT 0 in interrupt vector table.

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

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

您用 8 加载 cx,因此您的循环将迭代 8 次。但是你在每次迭代中将 si 递增 2,所以在前 4 次迭代之后你已经超过了 nizA 数组的末尾。

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

顺便说一句,检查 al 是否奇数的一种更简单、更有效的方法是 test al, 1,它根据 al 的按位与设置标志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[=54= 之前不需要重新加载值]数组?
另请注意,您并不总是需要单独的循环计数器。这里我使用了源数组索引。