Assembly 查找数组中的最小值(Intel8086)

Assembly 查找数组中的最小值(Intel8086),assembly,x86-16,tasm,Assembly,X86 16,Tasm,我想写一个简单的程序来查找数组中的最小值。我使用的是英特尔8086体系结构(如果我是对的话?)。问题是,我对汇编语言完全陌生,我只是不知道我的代码哪里做错了,我被卡住了 在结束时(退出前),myax寄存器不包含我的结果。 我担心它甚至没有把它放在那里(我用Turbo调试器工具检查寄存器的值) .modeltimy 数据段 arr_len eq 5 arr DB 07h、02h、03h、10h、12h 最小分贝255 数据端 代码段 组织100小时 假设CS:Code,DS:Data,SS:Cod

我想写一个简单的程序来查找数组中的最小值。我使用的是英特尔8086体系结构(如果我是对的话?)。问题是,我对汇编语言完全陌生,我只是不知道我的代码哪里做错了,我被卡住了

在结束时(退出前),my
ax
寄存器不包含我的结果。
我担心它甚至没有把它放在那里(我用Turbo调试器工具检查寄存器的值)

.modeltimy
数据段
arr_len eq 5
arr DB 07h、02h、03h、10h、12h
最小分贝255
数据端
代码段
组织100小时
假设CS:Code,DS:Data,SS:Code
开始:
mov ax、SEG数据
mov-ds,ax;将数据加载到ds reg
mov cx,arr_len;到计数器的arr_长度
mov bx,偏移arr;加载第一个元素。
搜索:
公司bx
cmp bl,[最小值];比较加载的元素和最小值
jb互换;如果bl
  • 微型模型不需要显式设置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


下面介绍了如何在累积时不将
的最小值保留在内存中,因为这太可怕了

此版本在
al
中累积
最小值
,从不将其存储到内存中。我还使用了字符串操作的乐趣。您将获得更小的代码大小,但可能比使用
mov
加载代码慢。使用
si
作为源指针是一种很好的惯例,可以帮助人们跟踪事物,即使不使用字符串操作。当然,不要因此而让代码变得更糟,但我建议您在有选择的时候使用它

我还避免了
循环
,因为。除非您要对代码大小进行快速的核心优化,否则不要使用
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
.modeltimy
数据段
arr DB 07h、02h、03h、10h、12h
arr_len eq$-arr;让汇编器计算大小以避免错误
数据端
代码段
组织100小时
假设CS:Code,DS:Data,SS:Code
未签名的\u min:
;; 根据Fifoernik的说法,微小模型不需要分段设置
; 假设arr_len>=2
mov-si,偏移量arr;src指针=&第一个元素
lea di,[si+arr_len];结束指针。
;; 移动di,arr_len+偏移arr;也可以工作,因为输入指针是静态的
;; 或者使用arr_len+OFFSET arr作为循环条件的立即操作数,如果您不关心将硬编码输入地址保留在循环本身之外的话。
cld;清除方向标志,使字符串INSN向上计数。如果ABI已保证此状态,则省略
lodsb;al=最小到目前为止=第一个元素,并将si前进到指向第二个元素
;; 输入时:AL=第一个元素(最小值)。SI=指向第二个元素的指针。DI=结束指针
搜索:
cmpsb;将[si]与当前最小值(al)和++si进行比较
在。没有新的
mov-al[si-1];有条件地跳过。您可以在支持686 INSN的CPU上使用'cmov'而不是分支
.没有新的
cmp-si,di;当si

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

即使当前元素不是最小值,您也总是交换。@user786653,我明白了,我将
循环
指令替换为
jb
的正下方。现在,只有当我发现较小的值时,它才应该交换,对吗?或者我仍然不明白…
cmpbl,[minimum]
比较BL中的值,而不是您想要的arr中的值。您从未从数组中加载任何值,这就是您无法获得所需结果的原因。将AL设置为255,将[BX]的值与AL进行比较,然后从那里开始。
mov ah,4Ch
int 21h
是退出程序的DOS中断。AL恰好是将返回给DOS的返回值。我只想删除你最后的评论
;;执行exit syscall,如果这是一个函数,则执行ret,因为您的代码已经执行了该操作
RET
也会起作用,因为COM程序在程序启动时已在堆栈上按下0x0000。PSP:0x0000包含将退出程序的
int20h
指令(CP/M兼容性)。如果这是一个DOS EXE程序,
ret
将无法工作(最好是int 21h/ah=4ch)。可以看出这是一个COM程序,因为
org 100h
,以及
.MODEL TINY
。在这种情况下,CS=DS=SS。前100个字节包含PSP(程序段前缀)。COM程序入口点总是从DOS加载COM程序的段开始的100小时。当然,如果您使用了
ret
,在这种情况下,您实际上无法使用该方法返回值,因此我建议使用
mov ah,4Ch
int 21h
,其中AL(返回值)=minimum@Michae
.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