0

My original assignment question is this:

Write a program that uses the variables below and MOV instructions to copy the value from bigEndian to littleEndian, reversing the order of the bytes. The number's 32 - bit value is understood to be 12345678 hexadecimal.

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

I think my code is right but I can't figure out why I am getting this error. Here's my code and error:

.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>------ Build started: Project: BigEndianLittleEndian, Configuration: Debug Win32 ------

1> Assembling BigEndiantoLittleEndian.asm...

1>BigEndiantoLittleEndian.asm(20): error A2008: syntax error

1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\BuildCustomizations\masm.targets(50,5): error MSB3721: The command "ml.exe /c /nologo /Zi /Fo"Debug\BigEndiantoLittleEndian.obj" /W3 /errorReport:prompt /TaBigEndiantoLittleEndian.asm" exited with code 1.

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

How can I fix this error?

Here is updated code:

.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

The error I get

1>------ Build started: Project: BigEndianLittleEndian, Configuration: Debug Win32 ------

1> Assembling 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" parameter of the "Link" task. The "NoEntryPoint" parameter is of type "System.Boolean".

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Crusader158
  • 21
  • 1
  • 1
  • 4
  • 1
    I don't see any code to reverse the bytes. My guess is your ASM error is coming from the first mov statement. – user3344003 Mar 01 '16 at 01:54
  • 1
    `mov eax, DWORD PTR bigEndian, eax = 87654321h` will be a issue. `mov` takes 2 operands. Not 3. Not sure what the `, eax = 87654321h` is all about. – Michael Petch Mar 01 '16 at 04:10
  • did you look at the line it throws the error? – phuclv Mar 01 '16 at 04:44
  • 3
    It looks like `eax = 87654321h` was supposed to be a comment (i.e. `; eax = 87654321h`). The comment is incorrect though, because `eax` would get the value `78563412h`. – Michael Mar 01 '16 at 06:47
  • That's where I get confused. I tried to declare my code, but I still would get the error. eax=87654321h was supposed to be a comment I'll fix that. At this point I am guessing because I am trying to follow along with the book, but there are no examples. I just know there is one piece that is missing. I am hoping you guys might be able to pick it out for me and explain why it is. I do believe it has something with declaring the value 12345678h or the reverse of what I have. Thanks for the help everyone. – Crusader158 Mar 02 '16 at 02:44
  • at the moment you store `ax` twice to the `littleEndian+2` location. Sorry I can't help you with error you show. – lvd Mar 04 '16 at 10:05

4 Answers4

5

You should actually swap bytes in eax before writing it back to memory:

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

By using exclusively mov instructions it could be like this:

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
lvd
  • 793
  • 3
  • 12
  • The question does say to use `MOV` instructions. I think that excludes `BSWAP` – Michael Petch Mar 02 '16 at 17:05
  • It doesn't say not to use any other instructions :) – lvd Mar 02 '16 at 17:33
  • You know I have seen similar code floating around the net, but it seems it may be beyond the current stage I am at, but I am always open to learning more for sure. I haven't used bswap yet but I will look into it. I do know that swapping it around is where I am lost. I think the code prior to the swap is what I am missing. I just can't figure it out. – Crusader158 Mar 02 '16 at 22:17
  • 1
    Your code above just writes to memory the same it's read from another place in memory, so no wonder it doesn't do any little<>big conversion. – lvd Mar 03 '16 at 16:28
  • Doing such conversion is essentially changing byte order in a way shown with byte `mov`s above. Then many ways to do arise: you can do it with specialized command (`bswap`), with some shifts and masks machinery in 32bit registers, using byte moves in loops indexing memory locations inside 32bit word etc. – lvd Mar 03 '16 at 16:31
  • Ok I get what your saying. I am going to opt out of using `bswap` for now, because I don't want to get gigged for using it. I did, however, read a little more and came up with some different code, but still get an error. I have updated my code above. – Crusader158 Mar 04 '16 at 01:18
  • 1
    Your mov-only code could use two word-loads into `ax` and `bx`. Or a dword load into eax, store ah and al, then `shr eax, 16`. Atom, and Broadwell/Skylake, support `movbe`, which does a load-and-bswap in a single instruction. `bswap` was added with 486, so it's available everywhere, unlike `movbe`. – Peter Cordes Mar 04 '16 at 02:03
  • 1
    @PeterCordes : Original question suggests the exercise is to do the endian conversion using just `MOV` instructions, so I'd say that excludes the `SHR`, `BSWAP` and `MOVBE` instructions. – Michael Petch Mar 04 '16 at 02:28
  • 1
    @MichaelPetch: yup, agreed. That's why I led with the suggestion to use word loads, and didn't start talking about `rol ax, 8` / `mov [littleEndian+2], ax` :P – Peter Cordes Mar 04 '16 at 02:32
  • On CPUs other than Intel P6-family, using byte loads + word stores would be good. AMD since K8 and Intel since SnB can do 2 loads per clock, but only one store per clock, so more loads than stores is a win except on CPUs with partial-register stalls (P II through Nehalem.) – Peter Cordes Oct 03 '17 at 04:28
1

I just finished this in my Assembly course. Here is what I painstakingly came up with. I am almost certain that there is a better, and more correct way to do it, but this was the only way that I could figure it out. Haven't turned it in for a grade yet, so I don't know how well it well hold up in that aspect. I suspect it will be fine. It does what the problem requested, within the constraints set.

 ; 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
P. Fernandez
  • 187
  • 1
  • 6
  • You're swapping in-place in `bigEndian`, destroying it's original value, and then coping that to `littleEndian`, which is weird. As far as efficiency, for 32 and 64bit operands, `bswap r32` or `bswap r64` does it, and is fast. For 16bit operands, `rol r16, 8` does it more efficiently than `xchg al, ah`. For Atom, and Haswell, `movbe` does it on the fly as part of a load or store. – Peter Cordes Mar 25 '16 at 09:33
  • Without bswap, the optimal way might be something like load the first 16b, flip it with `rol ax, 8`, put it in the upper 16 with `shl eax, 16`, then load and flip the high 16b of the source. That will incur a partial-register stall, though. Or load 32b, `rol ax,8` / `rol eax, 16` / `rol ax, 8` does the same thing with only one load, but a longer dependency chain. If you don't need the result in a 32bit register, then two separate load / `rol ax,8` / store pairs are probably best. – Peter Cordes Mar 25 '16 at 09:37
  • 1
    @PeterCordes : The original question did say that they were to use `MOV` instructions. – Michael Petch Mar 25 '16 at 11:53
  • 1
    @MichaelPetch: Oh, I missed that. Then load al/ah and store ax. Or on a pre-SnB Intel CPU, load AX and store each byte separately, because reading ax after writing al/ah causes a partial-reg stall. – Peter Cordes Mar 25 '16 at 12:13
1

I came up with what seems like a better way to do solve the problem. It still sticks to the restraint of only using MOV instructions, but it doesn't destroy the array's original value.

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
P. Fernandez
  • 187
  • 1
  • 6
  • `mov [esi+2], ebx` is a dword store that writes past the end of `littleEndian`. I think you want `mov [esi+2], bx`. Also, `mov eax, 0` [should be `xor eax,eax`](https://stackoverflow.com/questions/33666617/what-is-the-best-way-to-set-a-register-to-zero-in-x86-assembly-xor-mov-or-and), or should be omitted entirely since all it does is break the dependency on the old value. That still doesn't avoid partial-register merging stalls on P3/Core2/Nehalem for reading a wider register after writing the smaller parts separately, but this is good on other CPUs. – Peter Cordes Oct 03 '17 at 04:33
0

I actually just had this exact same assignment, thought i would post my answer here. There IS a way to swap the bytes around by taking advantage of pointers and the OFFSET command. LittleEndian is only applied when writing bytes to memory. Which means when you run the following code, eax will hold the reversed bytes of bigEndian.

LittleEndian <=> BigEndian

mov ebx,OFFSET bigEndian
mov eax, [ebx]

This assignment however, restricted us to only using the mov operator. In my case, the OFFSET operators are not allowed either. Literally all we could use was mov. In light of this, you will need to swap the bytes around manually, and since you cannot move data from memory to memory, we are forced to use an 8-bit register as a buffer to temporarily hold the data.

You can access each byte of bigEndian individually by using offsets bigEndian+0 -> bigEndian+3, but considering we declared littleEndian after bigEndian, memory was allocated for littleEndian just after bigEndian. Which means if we move offsets bigEndian+4 -> bigEndian+7 we can directly access littleEndian using small 8-bit increments. I took advantage of this and used it in my code.

EDIT: Here is the final result

.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    
Nick S
  • 9
  • 1
  • No, that's not how MOV works, and stacks have nothing to do with this. Your `mov littleEndian, eax` stores the big-endian data into `littleEndian`, because you didn't do anything to convert it after loading into `eax`. A `mov` load/store pair doesn't shuffle data, just copies. You did load it all one byte at a time (before printing it), but you threw it away without storing it. – Peter Cordes Oct 03 '17 at 04:42
  • To "push" bytes into a register, you'd `movzx eax, byte ptr [bigendian]` / `shl eax,1` / `mov al, [bigEndian+1]` / ... Then you'd have native data in the register so you could `mov [littleEndian], eax` – Peter Cordes Oct 03 '17 at 04:46
  • Then why does it work? I was unsure if I was correct at first (was a shot in the dark) but if I were to print bigEndian as a whole i would get 12345678h, but after mov ebx, OFFSET bigEndian and mov eax, [ebx] it's in a reversed order. I can literally print out eax and it would be 78563412h. The only way I could explain it, is that it acts like a stack and I was simply popping the stack. – Nick S Oct 03 '17 at 12:58
  • Also, this assignment forbid the use of the PTR command, so we couldn't use BYTE PTR [bigendian] like you are suggesting. Trust me, i looked into it. We were only allowed to use the mov commands, nothing else. Not even your movzx. – Nick S Oct 03 '17 at 13:02
  • @PeterCordes I spoke to my professor and he explained to me why and how the code i showed worked. However, the OFFSET operator is not allowed. I have changed my answer accordingly. – Nick S Oct 03 '17 at 15:48
  • You're still just copying without reversing the byte order. But now your comments explain why you think this works. You think that a DWORD value of `78563412h` is the correct answer. But the 32-bit DWORD value in `bigEndian` is `12345678h` when interpreted as big-endian, which is what you're supposed to be doing. By changing the offsets, you can easily change your program to traverse one in the opposite order to the other. – Peter Cordes Oct 03 '17 at 16:03
  • Also, `mov ebx,OFFSET bigEndian` / `mov eax, [ebx]` is *exactly* equivalent to `mov eax, dword ptr [bigEndian]` (as far as the value in `eax` is concerned). My point earlier about shifts was that you *can't* "push bytes into a register" with just MOV instructions. (Well, you can write AH and AL, then store AX). IDK why your professor wants to restrict you from using `BYTE PTR` and `OFFSET` syntax to write the bytes of `littleEndian` or get an address into a pointer. Accessing the bytes of `littleEndian` via offsets from `bigEndian` is a clever workaround for a silly restriction. – Peter Cordes Oct 03 '17 at 16:09
  • `mov bigEndian+7, al` assembles to exactly the same machine-code as `mov byte ptr [littleEndian+3], al`. The only reason you even need `byte ptr` is that MASM magically implies an operand size based on a `dd` or `db` after a label. In NASM, `mov [littleEndian], 1` wouldn't assemble, because the operand size would be ambiguous. In assembly, everything is just 1 or more bytes, and there's nothing special about reading/writing only part of the bytes of something, or copying multiple bytes at once with a wider load. – Peter Cordes Oct 03 '17 at 16:14
  • Honestly i'm not quite sure why my prof decided to force us to do it this way. Thank you for taking the time to explain things to me, I Natively know C language and this is my first time learning Assembly. There are some things that are similar, but i'm still trying to wrap my head around how it all works :P – Nick S Oct 05 '17 at 14:37
  • Knowing C and understanding pointers / memory is a huge help for understanding asm. (asm is like C where you can do any kind of pointer-casting you want, e.g. `(char*)&foo` to access the bytes of any object.) And BTW, you should probably fix your answer so it does a copy+byte-swap instead of just a byte-at-a-time copy. Let me know so I can remove my downvote. – Peter Cordes Oct 05 '17 at 18:19