汇编语言中的选择排序

发布于 2024-09-29 10:55:30 字数 2821 浏览 6 评论 0原文

这是我的代码..我必须对数组执行选择排序。这是家庭作业。 Irvine32.inc 设置了我的内存模型。对我做错的事情的任何建议都会有帮助。我现在已经把整个事情重做了几次了。

INCLUDE Irvine32.inc
.data
myArray DWORD 10, 12, 3, 5
.code
main PROC
    call Clrscr
    MOV EDI, OFFSET myArray
    MOV ECX, LENGTHOF myArray
    CALL PRINT_ARRAY


    MOV EDI, OFFSET myArray
    MOV ECX, LENGTHOF myArray
    CALL SORT_ARRAY

    CALL CRLF
    MOV EDI, OFFSET myArray
    MOV ECX, LENGTHOF myArray
    CALL PRINT_ARRAY

    exit
main ENDP
;-----------------------------------------------------------------------------
PRINT_ARRAY PROC
; requires edi to be pointing to an array
; requires ecx to be the length of the array
;-----------------------------------------------------------------------------
ARRAYLOOP: MOV EAX, [EDI]
           CALL WRITEINT
           CALL CRLF
           ADD EDI, TYPE myArray
           LOOP ARRAYLOOP
           ret
PRINT_ARRAY ENDP

;-----------------------------------------------------------------------------
SORT_ARRAY PROC
; requires edi to be pointing to an array
; requires ecx to be the length of the array
; 
; ebp points to the minimum value
; esp points to edi + 1 (4 in our case) 
;-----------------------------------------------------------------------------
PUSHAD                          ; push all our registers.. dont want to modify 



OUTER_LOOP: MOV EBX, ECX        ; ebx = inner looper counter
            DEC EBX             ; dont want to compare same offset
                                ; we want it one less in the ebx count  

            MOV EBP, EDI        ; assign our min value array OFFSET
            MOV ESP, EDI        ; our value of j which we will inc      
            ADD ESP, TYPE[EDI]  ; j = i + 1

INNER_LOOP: MOV EAX, [EBP]      ; save our min VALUE to a register

            ; ESP (j)  < EAX (min) ?
            CMP [ESP], EAX  

            ; no, its greater, skip this value
            JGE SKIP_ARRAY_VALUE

            ; yes, array value is less than min
            ; save a new min value
            MOV EBP, ESP

            SKIP_ARRAY_VALUE:

            ADD ESP, TYPE[EDI]
            ; decrement our counter
            DEC EBX

            ; if ebx didnt set the zero flag, keep looping
            JNZ INNER_LOOP

            ; move our min value into the position of edi, and move edi 
            ; to the position of the min value

            MOV EDX, [EDI]                  ; edx = numbers[i]
            MOV EAX, [EBP]                  ; eax = numbers[min] 
            MOV [EDI], EAX                  ; numbers[i] = numbers[min]
            MOV [EBP], EDX                  ; numbers[min] = numbers[i]

            INC EDI
            LOOP OUTER_LOOP

POPAD                           ; pop all registers

RET
SORT_ARRAY ENDP
END main

该程序首先打印出未排序的数组。然后它挂起一点然后崩溃,没有错误或任何东西。

here is my code.. I have to perform a selection sort on an array. It is homework. The Irvine32.inc sets up my memory model. Any suggestions to what I'm doing wrong would be helpful. I've redone the entire thing a few times now.

INCLUDE Irvine32.inc
.data
myArray DWORD 10, 12, 3, 5
.code
main PROC
    call Clrscr
    MOV EDI, OFFSET myArray
    MOV ECX, LENGTHOF myArray
    CALL PRINT_ARRAY


    MOV EDI, OFFSET myArray
    MOV ECX, LENGTHOF myArray
    CALL SORT_ARRAY

    CALL CRLF
    MOV EDI, OFFSET myArray
    MOV ECX, LENGTHOF myArray
    CALL PRINT_ARRAY

    exit
main ENDP
;-----------------------------------------------------------------------------
PRINT_ARRAY PROC
; requires edi to be pointing to an array
; requires ecx to be the length of the array
;-----------------------------------------------------------------------------
ARRAYLOOP: MOV EAX, [EDI]
           CALL WRITEINT
           CALL CRLF
           ADD EDI, TYPE myArray
           LOOP ARRAYLOOP
           ret
PRINT_ARRAY ENDP

;-----------------------------------------------------------------------------
SORT_ARRAY PROC
; requires edi to be pointing to an array
; requires ecx to be the length of the array
; 
; ebp points to the minimum value
; esp points to edi + 1 (4 in our case) 
;-----------------------------------------------------------------------------
PUSHAD                          ; push all our registers.. dont want to modify 



OUTER_LOOP: MOV EBX, ECX        ; ebx = inner looper counter
            DEC EBX             ; dont want to compare same offset
                                ; we want it one less in the ebx count  

            MOV EBP, EDI        ; assign our min value array OFFSET
            MOV ESP, EDI        ; our value of j which we will inc      
            ADD ESP, TYPE[EDI]  ; j = i + 1

INNER_LOOP: MOV EAX, [EBP]      ; save our min VALUE to a register

            ; ESP (j)  < EAX (min) ?
            CMP [ESP], EAX  

            ; no, its greater, skip this value
            JGE SKIP_ARRAY_VALUE

            ; yes, array value is less than min
            ; save a new min value
            MOV EBP, ESP

            SKIP_ARRAY_VALUE:

            ADD ESP, TYPE[EDI]
            ; decrement our counter
            DEC EBX

            ; if ebx didnt set the zero flag, keep looping
            JNZ INNER_LOOP

            ; move our min value into the position of edi, and move edi 
            ; to the position of the min value

            MOV EDX, [EDI]                  ; edx = numbers[i]
            MOV EAX, [EBP]                  ; eax = numbers[min] 
            MOV [EDI], EAX                  ; numbers[i] = numbers[min]
            MOV [EBP], EDX                  ; numbers[min] = numbers[i]

            INC EDI
            LOOP OUTER_LOOP

POPAD                           ; pop all registers

RET
SORT_ARRAY ENDP
END main

The program results in printing the array out first off, unsorted. Then it hangs for a little bit and crashes, no errors, or anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

乖乖 2024-10-06 10:55:30

您需要诊断您的崩溃。

  • 安装并设置 DrWatson,以便它捕获崩溃数据。
  • 再次运行 ML,并选择输出 pdb 文件
  • 再次触发崩溃 - 你 DrWatson 应该捕获它。

选择:
通过调试器运行您的程序。
从 VS 2008 开始,VS 内置了 MASM (ML),因此您甚至可以进行源代码调试。我记录了在 VS 2008 Express SP1 中激活 MASM - 免费 - (大概是以下版本)

现在我根本没有仔细研究你的算法,但你使用 ESP 的方式有点让我害怕:
当您在 SORT_ARRAY 中执行 POPAD 时,您真的确定 ESP 仍然指向您的 PUSHAD 基于堆栈的保存区域吗?...

我已经使用 ML 编程和维护了非常大的软件片段,我的建议是永远不要搞乱 ESP,并在大多数情况下让 MASM 处理 (E)BP(LOCAL 子句,下面的示例)。唯一的例外与繁重的系统编程有关,例如位数模式更改(进入/离开 prot 模式)和实现线程监视器(状态保存/恢复)。

其他一些:
不要再使用跳转,使用 .IF / .ELSE / .ENDIF、.REPEAT / .WHILE / .UNTIL 等。
不要为参数和局部变量的 EBP 操心,让 ML 伪操作来处理参数和局部变量寻址。
使用 MASM 管理的参数传递(通过 INVOKE 而不是 CALL)并使用 MASM 管理的局部变量(通过 LOCAL in-PROC 指令)。您甚至可以使用如下所示的语法在 LOCAL 中定义数组

Foo[6]: BYTE


使用两个 DWORD 参数(LinBufferBase 和 BufferSize)调用 CheckRAMPresent。
在进入和退出时,MASM 保存并恢复 EAX ECX EBX DI ES,因为我告诉它 PROC 使用它。
SMAPBuffer、RAMBank 和 RAMBankEnd 是本地(基于堆栈)变量(SMPOutput 是一个 STRUCT)。 MASM 操纵堆栈指针在进入/退出时分配/释放,并管理基于 BP 的地址模式 - 查看 PROC 中的代码如何寻址参数和本地变量。
最后,您有 .IF .ELSE .ENDIF 甚至 .REPEAT / .UNTIL 的示例
请注意,您可以使用条件标志

.IF CARRY?

或类似 HLL 的条件表达式:

(ES:[DI].RangeType == 1)

甚至更复杂:

((ECX >= EAX) && (ECX <= EBX)) || ((EDX >= EAX) && (EDX <= EBX))

它们会生成完全可预测的代码,因此这仍然是汇编语言。但这只是一种更具可读性/可维护性的程序集。
对于所有 HLL 伪操作,请查看生成的代码(有一个 ML 选项)。

解释 HLL 构造的整套 MASM 文档可以在 ZIPped 中那里找到。文档与HTML 格式。我想,你还可以找到 PDF 格式的文件(谷歌)。
程序员指南是迄今为止最有用的部分。 MASM 参考手册大部分已经过时,您宁愿使用英特尔开发人员指南。

CheckRAMPresent PROC NEAR STDCALL PUBLIC \
                 USES EAX ECX EBX DI ES,
                   LinBufferBase: DWORD,
                   BufferSize:    DWORD

               LOCAL SMAPBuffer: SMAPOutput,
                   RAMBank:      DWORD,
                   RAMBankEnd:   DWORD


 MOV AX,SS                   ; Get ES:DI => SMAP buffer,
 MOV ES,AX
 LEA DI, SMAPBuffer
 MOV ECX, SIZEOF SMAPBuffer  ;  ECX = SMAP buffer size.

 PUSHCONTEXT ASSUMES
 ASSUME DI:PTR SMAPOutput

 XOR EBX,EBX                 ; Set initial continuation pointer.
 MOV RAMBank, EBX            ; Zero the RAM bank tracker.
 MOV RAMBankEnd, EBX

   .REPEAT
   INVOKE GetSMAP
   .BREAK .IF CARRY?
     ; If type is Available, then process that range.
     .IF (ES:[DI].RangeType == 1) ; If Available RAM, check what we have.
     SAVE EBX, ECX
     MOV EAX, ES:[DI].LowBase    ; Get Bank start in EAX,
     MOV EBX, EAX
     ADD EBX, ES:[DI].LowLng     ;   and bank end in EBX.
     MOV ECX, LinBufferBase      ; Get buffer start in ECX
     MOV EDX,ECX
     ADD EDX, BufferSize         ;  and buffer end in EDX.

       ; If either the lower end or the upper end of the buffer
       ; intersects with the bank, take that bank (if this is the
       ; first) or try to coalesce it with the existing one (if we already
       ; have one).
       ; This translates as:
       ; If either the low address (ECX) or the high address (EDX) of the
       ; buffer is within the bank boundaries [EAX - EBX], then the buffer
       ; intersects with the bank.
       .IF   ((ECX >= EAX) && (ECX <= EBX)) \ ; If buffer intersects,
          || ((EDX >= EAX) && (EDX <= EBX))
         ; then if this is the first intersecting RAM bank, too, then
select it.
         .IF (!RAMBank && !RAMBankEnd)
         MOV RAMBank, EAX    ; Remember bank.
         MOV RAMBankEnd, EBX
         .ELSE
         ; We already have a RAM bank.
           ; If this new one starts where the one we have ends,
           ; the end of the new one become the end of the merged blocks.
           ; Else if the end of the new block is the beginning of the one
           ; we have, then the new block is located just before the one we
           ; have and its start become the start of the merged blocks.
           ; Otherwise, the new bank is not contiguous with the previously
           ; computed one and there's nothing we can do (at least using this
           ; algorithm).
           .IF (EAX == RAMBankEnd)
           MOV RAMBankEnd, EBX
           .ELSEIF (EBX == RAMBank)
           MOV RAMBank, EAX
           .ENDIF
         .ENDIF
       .ENDIF
     RESTORE EBX, ECX
     .ENDIF

   .UNTIL (EBX == 0)         ; If SMAP returned EBX == 0, we just did the
                             ; last SMAP bank.

 MOV EAX, LinBufferBase      ; Get buffer start in EAX
 MOV ECX,EAX
 ADD ECX, BufferSize         ;  and buffer end in ECX.

   ; If our start and our end are both in the bank,
   ; we win. Otherwise, we loose.
   .IF (EAX >= RAMBank) && (ECX <= RAMBankEnd)
   CLC
   .ELSE
   STC
   .ENDIF

 RET
CheckRAMPresent ENDP

玩得开心! ;-)

You need to diagnose your crash.

  • Install and setup DrWatson so that it catches crash data.
  • run ML again with options to output a pdb file
  • trigger the crash again - you DrWatson should trap it.

Alternative:
Run your program through a debugger.
Since VS 2008, VS has MASM (ML) built-in, so you might even be able to get source debugging. I documented activating MASM in VS 2008 Express SP1 - free - (and presumably following versions) there.
Otherwise, use windbg (not as friendly).

Now I didn't go at all through your algorithm, but the way you use ESP kinda scares me:
Are you REALLY sure ESP still points to your PUSHAD stack-based save area when you execute the POPAD in SORT_ARRAY?...

I have programmed and maintained very large software pieces using ML, and my recommendation would be to never messup with ESP, and let MASM take care of (E)BP in most circumstances (LOCAL clause, example below). The only exceptions relate to heavy systems programming, like bitness mode changing (entering / leaving prot mode) and implementing a threading monitor (state saving / restoration).

A few others:
Don't use jumps anymore, use .IF / .ELSE / .ENDIF, .REPEAT / .WHILE / .UNTIL, etc. and the like.
Don't bother with EBP for parms and local vars, let ML pseudo-ops take care of parms and local variable addressing.
Use MASM-managed parameter passing (through INVOKE instead of CALL) and use MASM-managed local variables (through the LOCAL in-PROC directive). You can even define arrays in LOCAL with a syntax such as

Foo[6]: BYTE

In the sample that follows:
CheckRAMPresent is called with two DWORD Parms, LinBufferBase and BufferSize.
Upon entry and exit, MASM saves and restore EAX ECX EBX DI ES because I told it the PROC uses it.
SMAPBuffer, RAMBank and RAMBankEnd are local (stack based) variables (SMPOutput is a STRUCT). MASM manipulates the stack pointer to allocated / deallocate upon entry / exit, and manages the BP-based address mode - see how the code in the PROC addresses both the parms and the local vars.
Finally, you have examples of .IF .ELSE .ENDIF and even a .REPEAT / .UNTIL
Note that you can use condition flags

.IF CARRY?

or HLL-like condition expressions:

(ES:[DI].RangeType == 1)

or even the more complex:

((ECX >= EAX) && (ECX <= EBX)) || ((EDX >= EAX) && (EDX <= EBX))

Those generate totally predictable code, so this is still assembly language. But it's just a much more readable / maintainable kind of assembly.
For all the HLL pseudo-ops, look at the generated code (there's an ML option for that).

The whole set of MASM documentation explaining the HLL constructs can be found there in ZIPped .doc & HTML formats. You can find it elsewere in PDF form, methinks (Google around).
The Programmer's Guide is by far the most useful part. The MASM reference manual is mostly obsolete, you'd rather use the Intel developer's guide instead.

CheckRAMPresent PROC NEAR STDCALL PUBLIC \
                 USES EAX ECX EBX DI ES,
                   LinBufferBase: DWORD,
                   BufferSize:    DWORD

               LOCAL SMAPBuffer: SMAPOutput,
                   RAMBank:      DWORD,
                   RAMBankEnd:   DWORD


 MOV AX,SS                   ; Get ES:DI => SMAP buffer,
 MOV ES,AX
 LEA DI, SMAPBuffer
 MOV ECX, SIZEOF SMAPBuffer  ;  ECX = SMAP buffer size.

 PUSHCONTEXT ASSUMES
 ASSUME DI:PTR SMAPOutput

 XOR EBX,EBX                 ; Set initial continuation pointer.
 MOV RAMBank, EBX            ; Zero the RAM bank tracker.
 MOV RAMBankEnd, EBX

   .REPEAT
   INVOKE GetSMAP
   .BREAK .IF CARRY?
     ; If type is Available, then process that range.
     .IF (ES:[DI].RangeType == 1) ; If Available RAM, check what we have.
     SAVE EBX, ECX
     MOV EAX, ES:[DI].LowBase    ; Get Bank start in EAX,
     MOV EBX, EAX
     ADD EBX, ES:[DI].LowLng     ;   and bank end in EBX.
     MOV ECX, LinBufferBase      ; Get buffer start in ECX
     MOV EDX,ECX
     ADD EDX, BufferSize         ;  and buffer end in EDX.

       ; If either the lower end or the upper end of the buffer
       ; intersects with the bank, take that bank (if this is the
       ; first) or try to coalesce it with the existing one (if we already
       ; have one).
       ; This translates as:
       ; If either the low address (ECX) or the high address (EDX) of the
       ; buffer is within the bank boundaries [EAX - EBX], then the buffer
       ; intersects with the bank.
       .IF   ((ECX >= EAX) && (ECX <= EBX)) \ ; If buffer intersects,
          || ((EDX >= EAX) && (EDX <= EBX))
         ; then if this is the first intersecting RAM bank, too, then
select it.
         .IF (!RAMBank && !RAMBankEnd)
         MOV RAMBank, EAX    ; Remember bank.
         MOV RAMBankEnd, EBX
         .ELSE
         ; We already have a RAM bank.
           ; If this new one starts where the one we have ends,
           ; the end of the new one become the end of the merged blocks.
           ; Else if the end of the new block is the beginning of the one
           ; we have, then the new block is located just before the one we
           ; have and its start become the start of the merged blocks.
           ; Otherwise, the new bank is not contiguous with the previously
           ; computed one and there's nothing we can do (at least using this
           ; algorithm).
           .IF (EAX == RAMBankEnd)
           MOV RAMBankEnd, EBX
           .ELSEIF (EBX == RAMBank)
           MOV RAMBank, EAX
           .ENDIF
         .ENDIF
       .ENDIF
     RESTORE EBX, ECX
     .ENDIF

   .UNTIL (EBX == 0)         ; If SMAP returned EBX == 0, we just did the
                             ; last SMAP bank.

 MOV EAX, LinBufferBase      ; Get buffer start in EAX
 MOV ECX,EAX
 ADD ECX, BufferSize         ;  and buffer end in ECX.

   ; If our start and our end are both in the bank,
   ; we win. Otherwise, we loose.
   .IF (EAX >= RAMBank) && (ECX <= RAMBankEnd)
   CLC
   .ELSE
   STC
   .ENDIF

 RET
CheckRAMPresent ENDP

Have fun! ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文