1

这是我的代码。我必须对数组执行选择排序。这是家庭作业。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

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

4

1 回答 1

5

您需要诊断您的崩溃。

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

替代方案:通过调试器运行您的程序。从 VS 2008 开始,VS 内置了 MASM (ML),因此您甚至可以进行源代码调试。我记录了在 VS 2008 Express SP1 - free - (可能是以下版本)中激活 MASM 。否则,使用 windbg(不那么友好)。

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

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

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

Foo[6]: BYTE

在下面的示例中:
CheckRAMPresent 使用两个 DWORD Parms、LinBufferBase 和 BufferSize 调用。
在进入和退出时,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 选项)。

可以在 ZIPped .doc 和 HTML 格式中找到解释 HLL 结构的整套 MASM文档。你可以以 PDF 格式找到它,methinks (Google around)。程序员指南是迄今为止最有用的部分。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

玩得开心!;-)

于 2010-10-27T08:01:17.420 回答