Assembly 查找数组中的最小值(Intel8086)
我想写一个简单的程序来查找数组中的最小值。我使用的是英特尔8086体系结构(如果我是对的话?)。问题是,我对汇编语言完全陌生,我只是不知道我的代码哪里做错了,我被卡住了 在结束时(退出前),myAssembly 查找数组中的最小值(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
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