【问题标题】:Big Endian to Little Endian大端到小端
【发布时间】:2016-06-13 07:14:55
【问题描述】:

我原来的作业问题是这样的:

编写一个程序,使用下面的变量和 MOV 指令将值从 bigEndian 复制到 littleEndian,颠倒字节顺序。该数字的 32 位值被理解为 12345678 十六进制。

.data
bigEndian   BYTE 12h, 34h, 56h, 78h
littleEndian DWORD?

我认为我的代码是正确的,但我不知道为什么会出现此错误。这是我的代码和错误:

.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.data
 bigEndian   BYTE 12h, 34h, 56h, 78h
 littleEndian DWORD ?
 .code
 main PROC
 mov eax, DWORD PTR bigEndian; eax = 87654321h
 mov littleEndian, eax

 invoke ExitProcess, 0
 main ENDP
 END main

1>------构建开始:项目:BigEndianLittleEndian,配置:调试 Win32 ------

1> 组装 BigEndiantoLittleEndian.asm...

1>BigEndiantoLittleEndian.asm(20):错误 A2008:语法错误

1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\BuildCustomizations\masm.targets(50,5):错误 MSB3721:命令“ml.exe /c /nologo /Zi /Fo"Debug\BigEndiantoLittleEndian.obj" /W3 /errorReport:prompt /TaBigEndiantoLittleEndian.asm" 退出,代码为 1。

========== 构建:0 成功,1 失败,0 最新,0 跳过 ==========

我该如何解决这个错误?

这里是更新的代码:

.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.data
bigEndian   BYTE 12h, 34h, 56h, 78h
littleEndian DWORD ?


.code
main PROC

mov ah, byte ptr bigEndian+0
mov al, byte ptr bigEndian+1
mov word ptr littleEndian+2,ax;here I want to move my now full register into      the 32bit register eax. 
mov ah, byte ptr bigEndian+2
mov al, byte ptr bigEndian+3
mov word ptr littleEndian+2,ax here I want to move my now full register into    the 32bit register eax which results in the order being reversed.


invoke ExitProcess, 0
main ENDP
END main

我得到的错误

1>----- 构建开始:项目:BigEndianLittleEndian,配置:调试 Win32 ------

1> 组装 BigEndiantoLittleEndian.asm...

1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.CppCommon.targets(708,9): error MSB4030: "main" is an invalid value for the " “链接”任务的 NoEntryPoint”参数。 “NoEntryPoint”参数的类型为“System.Boolean”。

========== 构建:0 成功,1 失败,0 最新,0 跳过 ==========

【问题讨论】:

  • 我没有看到任何反转字节的代码。我的猜测是您的 ASM 错误来自第一个 mov 语句。
  • mov eax, DWORD PTR bigEndian, eax = 87654321h 将是一个问题。 mov 需要 2 个操作数。不是 3. 不确定, eax = 87654321h 是什么意思。
  • 你看到它抛出错误的那一行了吗?
  • 看起来eax = 87654321h 应该是评论(即; eax = 87654321h)。不过评论是不正确的,因为eax 会得到78563412h 的值。
  • 这就是我感到困惑的地方。我试图声明我的代码,但我仍然会收到错误消息。 eax=87654321h 应该是我会修复的评论。在这一点上,我猜测是因为我试图跟随这本书,但没有例子。我只知道少了一件。我希望你们能够为我挑选出来并解释为什么会这样。我确实相信它与声明值 12345678h 或我所拥有的相反。感谢大家的帮助。

标签: assembly x86 masm 32-bit


【解决方案1】:

您实际上应该在将 eax 中的字节写回内存之前进行交换:

mov   eax,DWORD PTR bigEndian
bswap eax
mov   littleEndian,eax

通过专门使用mov 指令,它可能是这样的:

mov al,DWORD PTR bigEndian+0
mov ah,DWORD PTR bigEndian+1
mov bl,DWORD PTR bigEndian+2
mov bh,DWORD PTR bigEndian+3

mov littleEndian+0,bh
mov littleEndian+1,bl
mov littleEndian+2,ah
mov littleEndian+3,al

【讨论】:

  • 问题确实说要使用MOV 指令。我认为不包括BSWAP
  • 您上面的代码只是写入内存,就像从内存中的另一个地方读取的一样,所以难怪它没有做任何小的大转换。
  • 您的 mov-only 代码可以使用两个字加载到 axbx。或者将 dword 加载到 eax 中,存储 ah 和 al,然后 shr eax, 16。 Atom 和 Broadwell/Skylake 支持 movbe,它在单个指令中执行加载和 bswap。 bswap 添加了 486,所以它在任何地方都可用,不像 movbe
  • @PeterCordes :原始问题表明该练习是仅使用 MOV 指令进行字节序转换,所以我会说不包括 SHRBSWAPMOVBE 指令。
  • @MichaelPetch:是的,同意。这就是为什么我提出使用单词加载的建议,并且没有开始谈论 rol ax, 8 / mov [littleEndian+2], ax :P
【解决方案2】:

我刚刚在我的装配课程中完成了这个。这是我苦心想出来的。我几乎可以肯定有一种更好、更正确的方法可以做到这一点,但这是我能弄清楚的唯一方法。还没有上交成绩,所以我不知道它在这方面的表现如何。我怀疑会没事的。它在约束集内完成问题所要求的工作。

 ; Description:  Copy value from bigEndian to littleEndian, reversing order of bytes
 ; Assignment: A06B 4.10 Programming Exercise
 ; Date: 3/17/2016

INCLUDE Irvine32.inc

.data
bigEndian       BYTE            12h, 34h, 56h, 78h
littleEndian    DWORD           ?

.code
main            PROC
                mov     al, [bigEndian+3]       ;swap 78h and 12h
                mov     ah, [bigEndian]     
                mov     [bigEndian], al
                mov     [bigEndian+3], ah

                mov     al, [bigEndian+2]       ;swap 56h and 34h
                mov     ah, [bigEndian+1]
                mov     [bigEndian+1], al
                mov     [bigEndian+2], ah

                mov     eax, DWORD PTR bigEndian    ;move reverse ordered bytes to eax
                mov     littleEndian, eax       ;move reverse ordered byted to littleEndian
                invoke  ExitProcess, 0
main            ENDP
END main

【讨论】:

  • 您在bigEndian 中就地交换,破坏了它的原始值,然后将其处理到littleEndian,这很奇怪。就效率而言,对于 32 位和 64 位操作数,bswap r32bswap r64 可以做到这一点,而且速度很快。对于 16 位操作数,rol r16, 8xchg al, ah 更有效。对于 Atom 和 Haswell,movbe 作为加载或存储的一部分即时执行。
  • 如果没有 bswap,最佳方式可能是加载第一个 16b,用rol ax, 8 翻转它,用shl eax, 16 将它放在上 16 位,然后加载并翻转高位 16b资源。不过,这将导致部分寄存器停顿。或者加载 32b,rol ax,8/rol eax, 16/rol ax, 8 只加载一次,但依赖链更长。如果您不需要 32 位寄存器中的结果,那么两个单独的加载/rol ax,8/存储对可能是最好的。
  • @PeterCordes :最初的问题确实说他们要使用MOV 指令。
  • @MichaelPetch:哦,我错过了。然后加载 al/ah 并存储 ax。或者在 pre-SnB Intel CPU 上,加载 AX 并分别存储每个字节,因为在写入 al/ah 后读取 ax 会导致部分注册停止。
【解决方案3】:

我想出了解决问题的更好方法。它仍然坚持只使用MOV 指令的限制,但它不会破坏数组的原始值。

INCLUDE Irvine32.inc

.data
bigEndian       BYTE    12h, 34h, 56h, 78h
littleEndian    DWORD   ?

.code
main            PROC
    mov esi, OFFSET bigEndian
    mov eax, 0
    mov al, [esi+3]     ;Isolate 5678h
    mov ah, [esi+2]
    mov ebx, 0
    mov bl, [esi+1]     ;Isolate 1234h
    mov bh, [esi]

    mov esi, OFFSET littleEndian
    mov [esi], eax      ;Move 5678h into lower 16bits of littleEndian
    mov [esi+2], ebx    ;Move 1234h into higher 16bits of littleEndian      
    invoke  ExitProcess, 0
main        ENDP
END main

【讨论】:

  • mov [esi+2], ebx 是一个写入到littleEndian 末尾的双字存储。我想你想要mov [esi+2], bx。此外,mov eax, 0should be xor eax,eax 或应该完全省略,因为它所做的只是打破对旧值的依赖。这仍然不能避免 P3/Core2/Nehalem 上的部分寄存器合并停顿,以便在单独写入较小部分后读取更宽的寄存器,但这在其他 CPU 上很好。
【解决方案4】:

实际上我刚刚完成了完全相同的任务,我想我会在这里发布我的答案。有一种方法可以利用指针和 OFFSET 命令来交换字节。 LittleEndian 仅在将字节写入内存时应用。这意味着当您运行以下代码时,eax 将保存 bigEndian 的反转字节。

LittleEndian BigEndian

mov ebx,OFFSET bigEndian
mov eax, [ebx]

然而,这个赋值限制了我们只能使用 mov 运算符。就我而言,也不允许使用 OFFSET 运算符。从字面上看,我们只能使用 mov。鉴于此,您将需要手动交换字节,并且由于您无法将数据从内存移动到内存,因此我们不得不使用 8 位寄存器作为缓冲区来临时保存数据。

您可以通过使用偏移量 bigEndian+0 -> bigEndian+3 单独访问 bigEndian 的每个字节,但考虑到我们在 bigEndian 之后声明 littleEndian,内存是在 bigEndian 之后分配给 littleEndian 的。这意味着如果我们移动偏移量 bigEndian+4 -> bigEndian+7,我们可以使用小的 8 位增量直接访问 littleEndian。我利用了这一点并在我的代码中使用了它。

编辑:这是最终结果

.386
.model flat,stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword
INCLUDE Irvine32.inc
.data

    bigEndian BYTE 12h, 34h, 56h, 78h
    littleEndian DWORD ?

.code
main proc

    ; You can swap them by taking advantage of when littleEndian is applied to variables in memory. 
    ; Since we are limited to only mov operators, we cannot use this code. But it's nice to have for the future.
    ; mov ebx,OFFSET bigEndian
    ; mov eax, [ebx]

    ; Display bigEndian
    mov ebx, 1  ;set to write eax al 8 hex digits using WriteHexB
    mov al, bigEndian+0
    call WriteHexB
    mov al, bigEndian+1
    call WriteHexB
    mov al, bigEndian+2
    call WriteHexB
    mov al, bigEndian+3
    call WriteHexB
    call CrlF

    ; Since bigEndian was declared before littleEndian, bigEndian only goes as far as bigEndian+3
    ; However, since littleEndian was declared after, we can traverse littleEndian using bigEndian+4 -> bigEndian+7.
    ; Considering we cannot move data from memory to memory, we have to use an 8-bit register as a buffer.
    ; We will use al register.

    ; bigEndian = 12345678h, littleEndian = 00000000h

    mov al, bigEndian+0
    mov bigEndian+4, al

    ; bigEndian = 12345678h, littleEndian = 00000012h

    mov al, bigEndian+1
    mov bigEndian+5, al

    ; bigEndian = 12345678h, littleEndian = 00003412h

    mov al, bigEndian+2
    mov bigEndian+6, al

    ; bigEndian = 12345678h, littleEndian = 00563412h

    mov al, bigEndian+3
    mov bigEndian+7, al

    ; bigEndian = 12345678h, littleEndian = 78563412h



    ; Write littleEndian to eax to display on screen
    mov eax, littleEndian

    ; Display littleEndian
    call WriteHex
    call CrlF

    ; Display 'Press [enter] to continue...'
    call WaitMsg

    invoke ExitProcess,0
main endp
end main    

【讨论】:

  • 不,这不是 MOV 的工作方式,堆栈与此无关。您的mov littleEndian, eax 将大端数据存储到littleEndian,因为您在加载到eax 后没有做任何转换。 mov 加载/存储对不会洗牌数据,只是复制。您确实一次加载了一个字节(在打印之前),但是您没有存储就将其丢弃了。
  • 要将字节“推入”寄存器,您需要 movzx eax, byte ptr [bigendian] / shl eax,1 / mov al, [bigEndian+1] / ... 然后您将在寄存器中有本地数据,这样您就可以 @987654330 @
  • 那为什么会起作用呢?起初我不确定我是否正确(在黑暗中拍摄),但如果我将 bigEndian 作为一个整体打印,我会得到 12345678h,但在 mov ebx, OFFSET bigEndian 和 mov eax, [ebx] 之后它是相反的命令。我可以从字面上打印出 eax,它将是 78563412h。我可以解释它的唯一方法是,它就像一个堆栈,我只是弹出堆栈。
  • 另外,这个赋值禁止使用 PTR 命令,所以我们不能像你建议的那样使用 BYTE PTR [bigendian]。相信我,我调查过了。我们只被允许使用 mov 命令,没有别的。甚至不是你的 movzx。
  • @PeterCordes 我和我的教授谈过了,他向我解释了我展示的代码为什么以及如何工作。但是,不允许使用 OFFSET 运算符。我已经相应地改变了我的答案。
猜你喜欢
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
  • 2013-04-30
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
相关资源
最近更新 更多