查找数组中的最小值(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
。
我想编写一个简单的程序来查找数组中的最小值。我正在使用 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
。