1

I'm was working on some homework to print out an array as it's sorting some integers from an array. I have the code working fine, but decided to try using EAX instead of AL in my code and ran into errors. I can't figure out why that is. Is it possible to use EAX here at all?

    ; 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

As you can see the output sorts the array just fine from least to greatest. I was trying to see if I could move AL into EAX, but that gave me a bunch of errors. Is there a work around for this so I can use a 32 bit register and get the same output?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Alex
  • 109
  • 2
  • 11
  • 2
    Show the code that you actually tried, along with the error. Make sure you cross-check in the instruction set reference to see if what you are trying to do is valid or not. PS: note that dwords are 4 bytes in size and you have some places where you hardcoded the size as `1` instead of using `TYPE` operator. – Jester Nov 06 '17 at 23:43

2 Answers2

1

If you still want to do an 8-bit store, you need to use an 8-bit register. (AL is an 8-bit register. IDK why you mention 16 in the title).

x86 has widening loads (movzx and movsx), but integer stores from a register operand always take a register the same width as the memory operand. i.e. the way to store the low byte of EAX is with mov [esi], al.

In printArray, you should use movzx eax, byte ptr [esi] to zero-extend into EAX. (Or movsx to sign-extend, if you want to treat your numbers as int8_t instead of uint8_t.) This avoids needing the upper 24 bits of EAX to be zeroed.


BTW, your code has a lot of unnecessary instructions. e.g.

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

totally pointless. You don't need to "init" or "declare" a register before using it for the first time, if your first usage is write-only. What you do with EDX is amusing:

    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" registers only have to be saved if you actually want the old value. I prefer the terms "call-preserved" and "call-clobbered". If writeString destroys its input register, then EDX holds an unknown value after the function returns, but that's fine. You didn't need the value anyway. (Actually I think Irvine32 functions at most destroy EAX.)

In this case, the previous instruction only zeroed the register (inefficiently). That whole block could be:

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

Actually you should omit the xor-zeroing too, because you don't need it to be zeroed. You're not using it as counter in a loop or anything, all the other uses are write-only.


Also note that XCHG with memory has an implicit lock prefix, so it does the read-modify-write atomically (making it much slower than separate mov instructions to load and store).

You could load a pair of bytes using movzx eax, word ptr [esi] and use a branch to decide whether to rol ax, 8 to swap them or not. But store-forwarding stalls from byte stores forwarding to word loads isn't great either.

Anyway, this is getting way off topic from the title question, and this isn't codereview.SE.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • I'm still learning this and I'm pretty new at it. Thank you for point out these mistakes and redundancies. I'll work on making it better. – Alex Nov 07 '17 at 04:33
1

Using EAX is definitely possible, in fact you already are. You asked "I was trying to see if I could move AL into EAX, but that gave me a bunch of errors." Think about what that means. EAX is the extended AX register, and AL is the lower partition of AX. Take a look at this diagram:image of EAX register . As you can see, moving AL into EAX using perhaps the MOVZX instruction would simply put the value in AL into EAX and fill zeroes in from right to left. You'd be moving AL into AL, and setting the rest of EAX to 0. You could actually move everything into EAX and run the program just the same and there'd be no difference because it's using the same part of memory.

Also, why are you pushing and popping EAX so much? The only reason to push/pop things from the runtime stack is to recover them later, but you never do that, so you can just let whatever is in EAX at the time just die.

  • That's not a great diagram. The `AX` label is outside the low 16 that it's labeling, while AL and AH are inside the register they're labeling. It could confuse someone into thinking AX is the high half of EAX, rather than the low half. For an ASCII-art diagram, see https://stackoverflow.com/questions/37243801/x86-calculating-ax-given-ah-and-al/37275984#37275984. And [the YASM manual has a nice diagram](https://www.tortall.net/projects/yasm/manual/html/manual.html#x86-registers). Those are linked in https://stackoverflow.com/tags/x86/info so you can find them again quickly. – Peter Cordes Nov 07 '17 at 02:31
  • And BTW, no, even with `movzx` the OP can't use AL in every instruction. `CMP [ESI+1], AL` needs to use `AL` to do a byte compare instead of a dword compare. All the instructions that *write* `AL` could instead write EAX with `movzx`, except for the `xchg` (which would be more efficient as 3 `mov` instructions) – Peter Cordes Nov 07 '17 at 02:33
  • Thank you so much for this. It's actually working now. – Alex Nov 07 '17 at 04:34
  • Thanks for the corrections. The diagram could be better and you can’t run the program as is with eax. I was trying to convey the relationship between the extended registers. – Asher Mancinelli Nov 08 '17 at 21:07