ASSEMBLY - 输出一个包含 32 位寄存器和 16 位寄存器的数组

ASSEMBLY - output an array with 32 bit register vs 16 bit

我正在做一些作业来打印出一个数组,因为它正在对数组中的一些整数进行排序。我的代码工作正常,但决定尝试在我的代码中使用 EAX 而不是 AL 并 运行 出错。我不明白为什么会这样。可以在这里使用 EAX 吗?

    ; This program sorts an array of signed integers, using 
    ; the Bubble sort algorithm. It invokes a procedure to 
    ; print the elements of the array before, the bubble sort, 
    ; once during each iteration of the loop, and once at the end.

    INCLUDE Irvine32.inc
    .data
    myArray   BYTE       5, 1, 4, 2, 8
    ;myArray    DWORD       5, 1, 4, 2, 8
    currentArray BYTE   'This is the value of array: ' ,0
    startArray BYTE 'Starting array. ' ,0
    finalArray BYTE 'Final array. ' ,0
    space BYTE ' ',0                                                ; BYTE

    .code
    main PROC

        MOV EAX,0                                       ; clearing registers, moving 0 into each, and initialize
        MOV EBX,0                                       ; clearing registers, moving 0 into each, and initialize
        MOV ECX,0                                       ; clearing registers, moving 0 into each, and initialize
        MOV EDX,0                                       ; clearing registers, moving 0 into each, and initialize
        PUSH EDX                        ; preserves the original edx register value for future writeString call
        MOV EDX, OFFSET startArray  ; load EDX with address of variable
        CALL writeString                ; print string
        POP EDX                         ; return edx to previous stack
        MOV ECX, lengthOf myArray           ; load ECX with # of elements of array
        DEC ECX                               ; decrement count by 1


    L1: 
        PUSH ECX                             ; save outer loop count
        MOV ESI, OFFSET myArray          ; point to first value

    L2: 
        MOV AL,[ESI]              ; get array value
        CMP [ESI+1], AL               ; compare a pair of values
        JGE L3                        ; if [esi] <= [edi], don't exch
        XCHG AL, [ESI+1]             ; exchange the pair
        MOV [ESI], AL
        CALL printArray             ; call printArray function
        CALL crlf
    L3: 
        INC ESI                       ; increment esi to the next value
        LOOP L2                   ; inner loop
        POP ECX                   ; retrieve outer loop count
        LOOP L1                   ; else repeat outer loop
        PUSH EDX                        ; preserves the original edx register value for future writeString call
        MOV EDX, OFFSET finalArray  ; load EDX with address of variable
        CALL writeString                ; print string
        POP EDX                         ; return edx to previous stack
        CALL printArray

    L4 : ret 



    exit
    main ENDP


    printArray PROC uses ESI ECX

    ;myArray loop
        MOV  ESI, OFFSET myArray                                ; address of myArray
        MOV  ECX, LENGTHOF myArray                              ; loop counter (5 values within array)          
        PUSH EDX                        ; preserves the original edx register value for future writeString call
        MOV EDX, OFFSET currentArray    ; load EDX with address of variable
        CALL writeString                ; print string
        POP EDX                         ; return edx to previous stack

    L5 :
        MOV  AL, [ESI]                                              ; add an integer into eax from array
        CALL writeInt   
        PUSH EDX                        ; preserves the original edx register value for future writeString call
        MOV EDX, OFFSET space
        CALL writeString
        POP EDX                         ; restores the original edx register value  
        ADD  ESI, TYPE myArray                                  ; point to next integer     
        LOOP L5                                             ; repeat until ECX = 0 
        CALL crlf

    RET 

        printArray ENDP

        END main
        END printArray

    ; output:
    ;Starting array. This is the value of array: +1 +5 +4 +2 +8

    ;This is the value of array: +1 +4 +5 +2 +8

    ;This is the value of array: +1 +4 +2 +5 +8

    ;This is the value of array: +1 +2 +4 +5 +8

    ;Final array. This is the value of array: +1 +2 +4 +5 +8

如您所见,输出对数组进行了从小到大的排序。我试图看看我是否可以将 AL 移到 EAX 中,但这给了我一堆错误。是否有解决此问题的方法,以便我可以使用 32 位寄存器并获得相同的输出?

如果您仍想进行 8 位存储,则需要使用 8 位寄存器。 (AL 是一个 8 位寄存器。不知道你为什么在标题中提到 16)。

x86 的负载越来越大(movzxmovsx),但是寄存器操作数的整数存储总是采用与内存操作数宽度相同的寄存器。即EAX低字节的存储方式是mov [esi], al.

printArray中,你应该使用movzx eax, byte ptr [esi]到zero-extend进入EAX。 (或者 movsx 到 sign-extend,如果你想将你的数字视为 int8_t 而不是 uint8_t。)这避免了需要将 EAX 的高 24 位清零。


顺便说一句,你的代码有很多不必要的指令。例如

    MOV EAX,0                                       ; clearing registers, moving 0 into each, and initialize

完全没有意义。如果您的第一次使用是 write-only,那么您在第一次使用前不需要 "init" 或 "declare" 注册。你用 EDX 做的事情很有趣:

    MOV EDX,0                                       ; clearing registers, moving 0 into each, and initialize

    PUSH EDX                        ; preserves the original edx register value for future writeString call
    MOV EDX, OFFSET startArray  ; load EDX with address of variable
    CALL writeString                ; print string
    POP EDX                         ; return edx to previous stack

"Caller-saved" 寄存器只有在您确实想要 旧值时才需要保存。我更喜欢术语 "call-preserved" 和 "call-clobbered"。如果 writeString 破坏了它的输入寄存器,那么 EDX 在函数 returns 之后会保存一个未知值,但这没关系。无论如何你都不需要这个值。 (其实我觉得Irvine32函数顶多是破坏EAX。)

在这种情况下,前面的指令只将寄存器(inefficiently)置零。整个块可能是:

    MOV EDX, OFFSET startArray  ; load EDX with address of variable
    CALL writeString            ; print string
    xor  edx,edx                ; edx = 0

实际上你也应该省略 xor-归零,因为你不需要将它归零。您没有将它用作循环中的计数器或任何其他用途,所有其他用途都是 write-only.


还要注意带内存的 XCHG 有一个隐含的 lock 前缀,所以它会原子地执行 read-modify-write (使其 慢很多 而不是单独的 mov 指令来加载和存储)。

您可以使用 movzx eax, word ptr [esi] 加载一对字节并使用分支来决定是否 rol ax, 8 交换它们。但是 store-forwarding 从字节存储转发到字加载的停顿也不是很好。

无论如何,这已经偏离了标题问题的主题,这不是 codereview.SE。

使用 EAX 绝对是可能的,事实上你已经是了。你问 "I was trying to see if I could move AL into EAX, but that gave me a bunch of errors." 想想那是什么意思。 EAX是扩展的AX寄存器,AL是AX的下分区。看看这张图:image of EAX register .如您所见,使用 MOVZX 指令将 AL 移入 EAX 可能只是将 AL 中的值放入 EAX 并从右到左填充零。您会将 AL 移动到 AL,并将 EAX 的其余部分设置为 0。您实际上可以将所有内容都移动到 EAX 并且 运行 程序完全相同并且没有区别,因为它使用的是相同的部分内存。

另外,你为什么要如此频繁地推送和弹出 EAX?从 运行 时间堆栈中 push/pop 东西的唯一原因是稍后恢复它们,但你永远不会这样做,所以你可以让当时 EAX 中的任何东西都消失。