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 的负载越来越大(movzx
和 movsx
),但是寄存器操作数的整数存储总是采用与内存操作数宽度相同的寄存器。即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 中的任何东西都消失。
我正在做一些作业来打印出一个数组,因为它正在对数组中的一些整数进行排序。我的代码工作正常,但决定尝试在我的代码中使用 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 的负载越来越大(movzx
和 movsx
),但是寄存器操作数的整数存储总是采用与内存操作数宽度相同的寄存器。即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 中的任何东西都消失。