查找数组中的最小值(Intel 8086)

Finding the minimum in an array (Intel8086)

我想编写一个简单的程序来查找数组中的最小值。我正在使用 Intel 8086 架构(如果我是对的?)。问题是我对汇编语言完全陌生,好吧,我就是不知道我的代码做错了什么,我被卡住了。

最后(退出前)我的 ax 寄存器不包含我的结果。
恐怕它甚至没有把它放在那里(我用 Turbo Debugger 工具检查寄存器的值)。

.MODEL TINY

Data SEGMENT
                        arr_len     EQU 5
                        arr         DB 07h, 02h, 03h, 10h, 12h
                        minimum     DB 255
Data ENDS

Code SEGMENT
                        ORG 100h
                        ASSUME CS:Code, DS:Data, SS:Code
Start:
                        mov ax, SEG Data
                        mov ds, ax  ;load data to ds reg    
                        mov cx, arr_len ;arr_length to counter
                        mov bx, OFFSET arr ;load first elem.
Search: 
                        inc bx
                        cmp bl, [minimum] ;compare loaded elem with min
                        jb Swap ;if bl<minimum
Swap:
                        mov minimum, bl
                        loop Search         

                        mov al, minimum ;result here?
                        mov ax, 4C00h
                        int 21h

Code ENDS
END Start

任何人都可以给我建议这里有什么问题吗?提前致谢。

  • 微型模型不需要显式设置 DS。 (CS=DS=ES=SS)
  • 因为 BX 第一次指向你的数组,你不应该立即递增它!仅在循环之前执行 inc
  • 您并没有真正比较数组元素,而是比较了您迭代数组所依据的地址的低字节。
  • 如果直接将 4C00h 放入 AX 退出,那么将任何结果放入 AL 是没有意义的。不过,使用调试器查看 AL 是可行的。

这是一个可用的版本:

Search: 
    mov al, [bx]
    cmp al, [minimum] ;compare loaded elem with min
    jnb NoSwap        ;if al>=minimum
    mov [minimum], al ;New minimum
NoSwap:
    inc bx
    loop Search

正如 Fifoernik 的回答所解释的那样,无论是否采用,您的条件分支都会跳转到相同的位置,因为分支目标是下一个 insn。


以下是如何做到这一点,而无需在积累时将 minimum 保留在内存中,因为那太可怕了。

此版本在 al 中累积 minimum,从不将其存储到内存中。为了好玩,我还使用了字符串操作。您将获得更小的代码大小,但可能比使用 mov 加载的代码更慢。使用 si 作为源指针是一个很好的约定,可以帮助人们跟踪事物,即使不使用字符串操作也是如此。当然,不要仅仅为此而使代码变得更糟,但如果您有选择,我建议您这样做。

我也避免 loop 因为 it's slow and wasn't gaining us much of anything。除非您要针对代码大小而不是速度进行硬核优化,否则不要使用 loop.

.MODEL TINY
Data SEGMENT
    arr         DB 07h, 02h, 03h, 10h, 12h
    arr_len     EQU $-arr                   ; let the assembler calculate the size to avoid mistakes
Data ENDS

Code SEGMENT
    ORG 100h
    ASSUME CS:Code, DS:Data, SS:Code
unsigned_min:
    ;; segment setup not needed with tiny model, according to Fifoernik
    ; assume arr_len >= 2
    mov    si, OFFSET arr       ; src pointer = &first element
    lea    di, [si + arr_len]   ; end pointer.
    ;; mov di, arr_len + OFFSET arr ; also works, since input pointer is static
    ;; or use arr_len + OFFSET arr as an immediate operand for the loop condition, if you don't care about keeping the hard-coded input address out of the loop itself.

    cld                         ; clear the direction flag so string insns count upwards.  Omit if the ABI guarantees this state already
    lodsb                       ; al = min so far = first element, and advance si to point to the second element


;; on entry: AL = 1st element (min).  SI = pointer to 2nd element.  DI = end-pointer
Search:
    cmpsb                       ; compare [si] with current min (al), and ++si
    jae   .no_new_min

    mov    al, [si-1]           ; conditionally skipped.  You could use `cmov` instead of the branch on a CPU supporting 686 insns
.no_new_min
    cmp    si, di               ; loop while si < end
    jb    Search

    ; min is in AL

    mov ah, 4Ch            ; exit with AL as exit status
    int 21h

如果您不能假设 arr_len >= 2,则将 min 初始化为 255 或第一个元素,并进入循环并使其指向第一个元素,而不是第二个元素。或者在循环外使用额外的 cmp si,di /jb