Assembly 在数组中查找第二大值的汇编程序

Assembly 在数组中查找第二大值的汇编程序,assembly,nasm,x86-16,Assembly,Nasm,X86 16,我编写了一个汇编程序,用于查找数组中的最大值,但现在我希望它能够查找数组中的第二大值。我将如何修改我的程序来实现这一点 这是我写的程序,它确实有效。程序打印数组中的所有值,然后查找数组的最大值。现在我想让它找到第二大值 %include "io.mac" .STACK 100H .DATA Numbers DW 3,4,5,2,6,0 msg0 db "Printing values in array",0 msg1 db "Max",0 msg2 db "M

我编写了一个汇编程序,用于查找数组中的最大值,但现在我希望它能够查找数组中的第二大值。我将如何修改我的程序来实现这一点

这是我写的程序,它确实有效。程序打印数组中的所有值,然后查找数组的最大值。现在我想让它找到第二大值

 %include "io.mac"
.STACK 100H 

.DATA
   Numbers DW 3,4,5,2,6,0
   msg0  db "Printing values in array",0
   msg1  db "Max",0
   msg2  db "Min",0

   .CODE
        .STARTUP
    PutStr msg0
    mov dx, Numbers 
    mov si, dx ;point to array
    printValues:
    cmp word [si], 0
    je procedure
    nwln
    PutInt [si]
    add si, 2
    jmp printValues

    procedure:
    push Numbers ;push Number to stack to pass parameter by stack
    call maxMeth
    nwln
    PutStr msg1
    nwln

    PutInt ax
    nwln




    complete:
.EXIT





maxMeth:
    enter 0,0 ;save old bp and set bp to sp
    mov si, [bp+4] ;point to array 
    mov ax, [si]   ; ax holds max
    add si,2 ; Increment si to next number

;Now entering loop
max:   
    cmp word [si],0   ; checks to see if the number is 0 and if it is, then we are done.
    je finish
    cmp ax, [si]        ; ax holds the max . So if ax is greater than si, then dont assign si to ax. 
    jge increment
    jmp newMax
newMax: 
    mov ax, [si] ; Otherwise we have a new a max

increment:   
    add si, 2   ;now increment si to check the next value and jump back to the main loop.
    jmp max

finish: ;clean up.
    leave ;pop bp
    ret   ;return
我编辑了我的程序来跟踪第二个最大值,我收到的第二个最大值的结果是3。我期望程序输出5。我不知道为什么我得到了错误的输出。这是我对程序的编辑

%include "io.mac"
.STACK 100H 

.DATA
   Numbers DW 3,4,5,2,6,0
   msg0  db "Printing values in array",0
   msg1  db "Max",0


   .CODE
        .STARTUP
    PutStr msg0
    mov dx, Numbers 
    mov si, dx ;point to array
    printValues:
    cmp word [si], 0
    je procedure
    nwln
    PutInt [si]
    add si, 2
    jmp printValues

    procedure:
    push Numbers ;push Number to stack to pass parameter by stack
    call maxMeth
    nwln
    PutStr msg1
    nwln

    PutInt ax
    nwln

   PutInt bx
    nwln


    complete:
.EXIT





maxMeth:
    enter 0,0 ;save old bp and set bp to sp
    mov si, [bp+4] ;point to array 
    mov ax, [si]   ; ax holds max
    mov bx, [si]   ; ax holds max
    add si,2 ; Increment si to next number

;Now entering loop
max:   
    cmp word [si],0   ; checks to see if the number is 0 and if it is, then we are done.
    je finish          ;equals 0, then finished
    cmp ax, [si]         
    jg testSecondMax   ;So if ax is greater than si, then dont assign si to ax and check second max
    jmp newMax         ;otherwise go to new max


newMax: 
    mov ax, [si] ; save new max
    jmp increment ; and increment

testSecondMax:
    cmp bx, [si]
    jl secondMax
    jg increment

secondMax:
    mov bx, [si]
    jmp increment

increment:  
    add si, 2   ;now increment si to check the next value and jump back to the main loop.
    jmp max 



finish: ;clean up.
    leave ;pop bp
    ret   ;return

我最终为此编写了代码,因为它让我想知道我实现循环的效率有多高。如果你想自己解决作业,不要看我的代码,只看英文的要点。使用调试器单步执行代码

以下是我如何更改您的代码:

NASM样式:在函数内使用本地标签,如.noswap:。将操作数缩进一致的列,使其看起来不参差不齐。用输入/返回值和调用注册它的约定注释函数

在newMax之前优化jmp next_指令:因为在执行即将失败的地方进行跳转是一种昂贵的无操作

除非可能为真正的8086进行优化,否则不要使用enter,它很慢

将正在检查的每个元素加载到寄存器中,而不是多次使用同一内存操作数。x86-16具有除BP/SP以外的6个整数寄存器;使用它们

将循环退出条件分支放在底部。如果需要,跳转到循环入口点

在两个寄存器中保留一个max和第二个max,就像在AX中保留max一样

当您发现一个元素大于第二个最大值时,请保留您拥有的3个数字中最高的2个。i、 e.在2个寄存器中维护2元素队列/排序列表

未经测试:

; word max2Meth(word *array);
; Input: implicit-length array (terminated by a 0 element),
;        pointed to by pointer passed on the stack.  (DS segment)
; returns in ax
; clobbers: cx, dx
global max2Meth
max2Meth:
    push  bp
    mov   bp, sp     ; make a stack frame.  (ENTER is slow, don't use it)
    push  si         ; SI is call-preserved in many calling conventions.  Omit this if you want to just clobber it.

    mov   si, [bp+4] ; pointer to array 

    mov   ax, [si]   ; assume that the list is non-empty
    mov   dx, ax     ; start with max = max2 instead of putting a conditional xchg outside the loop

    jmp   .loop_entry   ; enter at the bottom, at the conditional branch
;;; ax: 2nd max
;;; dx: max

.maxloop:              ; do {
    cmp cx, ax         ; check against 2nd-max, because the common case is less than both.
    jg  .updateMaxes   ; optimize for the common case: fall through on not-found

.loop_entry:
    add  si, 2
    mov  cx, [si]      ;   c = *p++;
    test cx, cx
    jnz .maxloop       ; } while (c != 0);

.finish:
   ; 2nd-max is already in AX, just clean up and return

    pop  si
    leave    ;pop bp would be faster because SP is already pointing to the right place
    ret

; This block is part of the function, even though it's placed after the ret
.updateMaxes:
    mov  ax, cx           ; Bump the old 2nd-max unconditionally
    cmp  ax, dx
    jle  .loop_entry      ; if 2nd_max <= max, return to the loop
    xchg ax, dx           ; otherwise swap
    jmp  .loop_entry
构造内部循环的另一种方法如下:

.maxloop:              ; do {
    add  si, 2
    mov  cx, [si]      ;   c = *p++;

    test cx, cx
    jz .finish         ; jz instead of jnz: exit the loop on the sentinel value

    cmp cx, ax         ; check against 2nd-max, because the common case is less than both.
    jng .maxloop

;; .updateMaxes:  ;; conditionally fall through into this part 
    mov  ax, cx           ; Bump the old 2nd-max unconditionally
    cmp  ax, dx
    jle  .maxloop         ; if 2nd_max <= max, return to the loop
    xchg ax, dx           ; otherwise swap
    jmp  .maxloop

.finish:
这可能更好,因为我们可以进入循环的顶部开始。我们用一个跳过条件更新的jz跳出循环,所以我们没有任何只存在于跳过代码的分支。i、 我们已经成功地有效地布置了我们的代码块

某些CPU唯一的缺点是test/jz和cmp/jg是背靠背的。当条件分支由多个指令分隔时,有些CPU的性能会更好。e、 除非你运气好,这两个分支中的一个不会在Sandybridge上宏融合。但是,他们会使用第一个循环


提醒:Stack Overflow user contributions是在需要属性的情况下授权的,因此如果您复制粘贴我的全部代码,确保你包括https://stackoverflow.com/questions/47466116/assembly-program-that-finds-second-largest-value-in-array/4746768247467682 在一篇评论中。

我最终为此编写了代码,因为它让我想知道我可以如何高效地实现循环。如果你想自己解决作业,不要看我的代码,只看英文的要点。使用调试器单步执行代码

以下是我如何更改您的代码:

NASM样式:在函数内使用本地标签,如.noswap:。将操作数缩进一致的列,使其看起来不参差不齐。用输入/返回值和调用注册它的约定注释函数

在newMax之前优化jmp next_指令:因为在执行即将失败的地方进行跳转是一种昂贵的无操作

除非可能为真正的8086进行优化,否则不要使用enter,它很慢

将正在检查的每个元素加载到寄存器中,而不是多次使用同一内存操作数。x86-16具有除BP/SP以外的6个整数寄存器;使用它们

将循环退出条件分支放在底部。如果需要,跳转到循环入口点

在两个寄存器中保留一个max和第二个max,就像在AX中保留max一样

当您发现一个元素大于第二个最大值时,请保留您拥有的3个数字中最高的2个。i、 e.在2个寄存器中维护2元素队列/排序列表

未经测试:

; word max2Meth(word *array);
; Input: implicit-length array (terminated by a 0 element),
;        pointed to by pointer passed on the stack.  (DS segment)
; returns in ax
; clobbers: cx, dx
global max2Meth
max2Meth:
    push  bp
    mov   bp, sp     ; make a stack frame.  (ENTER is slow, don't use it)
    push  si         ; SI is call-preserved in many calling conventions.  Omit this if you want to just clobber it.

    mov   si, [bp+4] ; pointer to array 

    mov   ax, [si]   ; assume that the list is non-empty
    mov   dx, ax     ; start with max = max2 instead of putting a conditional xchg outside the loop

    jmp   .loop_entry   ; enter at the bottom, at the conditional branch
;;; ax: 2nd max
;;; dx: max

.maxloop:              ; do {
    cmp cx, ax         ; check against 2nd-max, because the common case is less than both.
    jg  .updateMaxes   ; optimize for the common case: fall through on not-found

.loop_entry:
    add  si, 2
    mov  cx, [si]      ;   c = *p++;
    test cx, cx
    jnz .maxloop       ; } while (c != 0);

.finish:
   ; 2nd-max is already in AX, just clean up and return

    pop  si
    leave    ;pop bp would be faster because SP is already pointing to the right place
    ret

; This block is part of the function, even though it's placed after the ret
.updateMaxes:
    mov  ax, cx           ; Bump the old 2nd-max unconditionally
    cmp  ax, dx
    jle  .loop_entry      ; if 2nd_max <= max, return to the loop
    xchg ax, dx           ; otherwise swap
    jmp  .loop_entry
构造内部循环的另一种方法如下:

.maxloop:              ; do {
    add  si, 2
    mov  cx, [si]      ;   c = *p++;

    test cx, cx
    jz .finish         ; jz instead of jnz: exit the loop on the sentinel value

    cmp cx, ax         ; check against 2nd-max, because the common case is less than both.
    jng .maxloop

;; .updateMaxes:  ;; conditionally fall through into this part 
    mov  ax, cx           ; Bump the old 2nd-max unconditionally
    cmp  ax, dx
    jle  .maxloop         ; if 2nd_max <= max, return to the loop
    xchg ax, dx           ; otherwise swap
    jmp  .maxloop

.finish:
这可能更好,因为我们可以进入循环的顶部开始。我们用一个跳过条件更新的jz跳出循环,所以我们没有任何只存在于跳过代码的分支。i、 我们已经成功地有效地布置了我们的代码块

某些CPU唯一的缺点是test/jz和cmp/jg是背靠背的。当条件分支由多个指令分隔时,有些CPU的性能会更好。e、 除非你运气好,这两个分支中有一个赢了 t宏保险丝。但是,他们会使用第一个循环


提醒:Stack Overflow user contributions是在需要属性的情况下授权的,因此如果您复制粘贴我的全部代码,确保你包括https://stackoverflow.com/questions/47466116/assembly-program-that-finds-second-largest-value-in-array/4746768247467682 在注释中。

通常的方法是,在遍历数组时跟踪最大值和第二个最大值。与第二个最大值进行比较,如果较大,则插入一个有效的2项排序列表,您应将其保存在寄存器中。运行“查找最大值”两次:运行一次,将列表中的所有匹配项归零,然后再次运行。通常的方法是在遍历数组时跟踪最大值和第二个最大值。与第二个最大值进行比较,如果更大,则插入一个有效的2条目排序列表,您应该将其保存在寄存器中。运行两次最大值:运行一次,将列表中的所有匹配项归零,然后再次运行。当人们没有完整性支持他们的否决票时,这只是令人沮丧。我当然看不到任何与之竞争的答案可以被判断为更优…稍微展开一下,你就可以接近每周期检查2个值的极限,基于分支的方法每周期限制2个分支,在更新不频繁的情况下,这几乎是速度的两倍。除此之外,我只能想到适用于某些类型的发行版的方法:如果长期的第二大值通常至少是大多数发行版的典型值的两倍,那么uniform是一个值得注意的例外,您可以使用cx或[si+…]N个值,然后检查是否大于当前的第二大值。哎呀,我没有注意到这是一个基于sentinel的循环,也没有注意到它是有符号的。如果必须支持负值,则可以解决符号性问题,但实际上不太可能遇到负值,只需对值或累积值进行无符号的乐观检查即可。任何负值都会导致它陷入缓慢的路径,但在这里,您需要进行有符号比较,因此它是安全的。“哨兵”杀死了它。@BeeOnRope:是的,没签名的人说得对。不过,只有SIMD可以解决哨兵问题。我想,如果你有64位寄存器,但没有SIMD,比如在一些RISC CPU上,可能值得去做检测零字的SWAR Bithack。我过去用过一种方法来解决分布是什么???问题是在运行时动态选择不同的循环。基本上,您可以在汇编级别上自由地执行它,并且通过实现在不同循环之间选择的隐式状态机来实现C或C++中GOTO的更丑、更大程度。例如,你有你为正常情况编写的循环,在这种情况下,发现更大是不寻常的,但是当你陷入updateMaxes时,你不会跳回那个循环,而是说循环的一个展开的迭代,如果你再次发现……当人们没有完整性来支持他们的否决票时,这是令人沮丧的。我当然看不到任何与之竞争的答案可以被判断为更优…稍微展开一下,你就可以接近每周期检查2个值的极限,基于分支的方法每周期限制2个分支,在更新不频繁的情况下,这几乎是速度的两倍。除此之外,我只能想到适用于某些类型的发行版的方法:如果长期的第二大值通常至少是大多数发行版的典型值的两倍,那么uniform是一个值得注意的例外,您可以使用cx或[si+…]N个值,然后检查是否大于当前的第二大值。哎呀,我没有注意到这是一个基于sentinel的循环,也没有注意到它是有符号的。如果必须支持负值,则可以解决符号性问题,但实际上不太可能遇到负值,只需对值或累积值进行无符号的乐观检查即可。任何负值都会导致它陷入缓慢的路径,但在这里,您需要进行有符号比较,因此它是安全的。“哨兵”杀死了它。@BeeOnRope:是的,没签名的人说得对。不过,只有SIMD可以解决哨兵问题。我想,如果你有64位寄存器,但没有SIMD,比如在一些RISC CPU上,可能值得去做检测零字的SWAR Bithack。我过去用过一种方法来解决分布是什么???问题是在运行时动态选择不同的循环。基本上,您可以在汇编级别上自由地执行它,并且通过实现在不同循环之间选择的隐式状态机来实现C或C++中GOTO的更丑、更大程度。例如,您有您为正常情况编写的循环,在这种情况下,发现更大是不寻常的,但是当您陷入updateMaxes时,您不会跳回该循环,而是说循环的一次展开迭代,如果您再次发现。。。