Looping backwards in Assembly x86 - arrays

When you are looping backwards in Assembly x86, what is currently happening in the memory (Can you try to be visual, thanks)? The following code is what I am currently wondering about:
INCLUDE Irvine32.inc
.data
arrayb byte 1,2,3,4,5,6 ;6-7 bytes
len dword lengthof arrayb
space byte " ",0
x dword 3
.code
main PROC
mov edx,offset space
mov eax,0 ; clear ecx of garbage
mov ecx, len
mov esi,offset arrayb ; start of the array's memory
add esi,len ;This causes the array value to start at 6
dec esi ; esi goes from esi+5,esi+4,...,esi
myloop2:
mov al,[esi]
call writedec
call writestring
dec esi
loop myloop2
call crlf
In particular, why did I have to add 1 to esi? When you add 1 to the high speed memory transfer register esi, it seems that it causes the array value to start at 6. Why is that?Thank you.

Related

How to write a loop that does something different in the last iteration?

The output for the given program should be 1--2--3--4--5--6--7--8.
However, mine keeps giving me 1--2--3--4--5--6--7-8--.I need to get rid of "--" after 8, but I couldn't figure out how to do it. Could someone help me, please?
INCLUDE Irvine32.inc
.data
arrayb byte 1,2,3,4,5,6,7,8
space byte "--",0
.code
main PROC
mov eax,0
mov edx,offset space
mov esi,offset arrayb
mov ecx, 8
printb:
mov al, [esi]
call writedec
call writestring
inc esi
mov eax, 8
loop printb
exit
main ENDP
end main
Your code (and loop specifically) currently does:
<init state>
counter = 8
printb:
output number
output "--"
loop to printb
<exit>
If you run through it in your head, it should be obvious why "--" is printed after last number.
There are many ways how to adjust that code, in real world code doing formatting I often either use some kind of join function which takes list and separator, and produces the formatted string, or if doing it manually, I would probably hardcode output of "1" ahead of loop, initialize the state to start loop as if starting from "2", and output "--" as first thing in the loop, i.e.:
<init state>
output number
advance state as if it was printed inside loop
counter = 8-1 ; one value is already out
printb:
output "--" ; the loop body starts with "--" output
output number
loop to printb
<exit>
I.e. in your code (with some modifications "improving" some things I didn't like :
...
mov edx,offset space
mov esi,offset arrayb
movzx eax, BYTE PTR [esi]
inc esi
call writedec
mov ecx, 8-1
printb:
call writestring
movzx eax, BYTE PTR [esi]
inc esi
call writedec
loop printb
exit
...
edit
The Peter Cordes idea from comments to make last item special case would maybe lead to better code in generic cases, where the amount of items is variable (with "8" fixed you know you can display first element and then 7 more will be looped).
Let's imagine such function in assembly, taking arguments in registers:
; esi = byte array to output
; ecx = amount of bytes
; will modify eax, ecx, edx, esi
OutputByteArray PROC
jecxz NoItemsToOutput ; if ecx=0
dec ecx
jz OutputLastItem ; if ecx=1, display only last item
; 1 < count, so "0 < ecx" here (ecx = count-1) => do the loop
mov edx,offset space
printb: ; output number and "--" "count-1" times
movzx eax, BYTE PTR [esi]
inc esi
call writedec
call writestring
loop printb
OutputLastItem:
movzx eax, BYTE PTR [esi]
call writedec
NoItemsToOutput:
ret
ENDP

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

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?
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.
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.

Assembly x86, looping and replace values in an array

Hey I need to make a program that loops through an array (1,2,3,4,5,6,7,8,9,10) and exchanges the element ‘i’ with element ‘i+5’ when ‘i' is under 4 When the program ends, the new array has the following values 6,7,8,9,10,1,2,3,4,5
and right now I have it looping through the array
.386
.model flat,stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword
WriteDec PROTO
Crlf PROTO
DumpRegs PROTO
.data
arrayB WORD 1,2,3,4,5,6,7,8,9,10
.code
main proc
mov eax,0
mov edi,OFFSET arrayB ; address of arrayB
mov ecx,LENGTHOF arrayB ; loop counter
mov ax,0 ; zero the accumulator
L1:
mov ax,[edi] ; mov current edi value from array into ax
xchg arrayB, ax ;change the current ax register with the value in arrayB
add edi,TYPE arrayB ; point to next integer
loop L1
call DumpRegs
call WriteDec
call crlf
invoke ExitProcess,0
main endp
end main
But Im having trouble actually a) telling when the loop is under 4, and b) replacing the values correctly. Any help would be greatly appreciated
Edit: I do know I can use "cmp" to compare the ecx register
Your task description is inconsistent; you say the array should be 6,7,8,9,10,1,2,3,4,5 at the end, and that calls for exchanging all elements with index under 5 (elements 0 to 4), not under 4.
The LOOP command (which is not a good idea, but that's beside the point) uses ECX as an implicit counter. If ECX is nonzero, it decrements and jumps to the label, if it's zero, it doesn't. So you're looping for exactly LENGTHOF arrayB times; that's wrong, you want to loop for half that.
Also, the XCHG command is wrong. The destination for the exchange is not at arrayB, it's at the current index + 5. You figure out how to fix that.
Also, once you exchange ax with the index+5'th element, you still need to write the retrieved value of the index+5'th element back into the index'th position.
You are using edi to point to the elements of the array and store them in ax, one solution will be to use esi to point to the element+5 and store it in bx, then move bx into edi and move ax into esi (by the way, the loop should repeat half-array-length times) :
.386
.model flat,stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword
WriteDec PROTO
Crlf PROTO
DumpRegs PROTO
.data
arrayB WORD 1,2,3,4,5,6,7,8,9,10
.code
main proc
;mov eax,0 ;◄■■ UNNECESSARY.
mov edi,OFFSET arrayB ; address of arrayB
mov ecx,5 ;◄■■ 10 ÷ 2 ("when ‘i' is under 4").
;mov ax,0 ;◄■■ UNNECESSARY.
L1:
mov ax,[edi] ; mov current edi value from array into ax
mov esi,edi
add esi,TYPE arrayB * 5 ;◄■■ ELEMENT "I+5".
mov bx,[esi] ;◄■■ BX = "I+5".
mov [edi],bx ;◄■■ EXCHANGE ONE REGISTER.
mov [esi],ax ;◄■■ EXCHANGE THE OTHER REGISTER.
add edi,TYPE arrayB ; point to next integer
loop L1
;----------------------------------------------
mov edi, OFFSET arrayB
mov ecx, LENGTHOF arrayB
DISPLAY_ARRAY:
xor eax, eax
mov ax, [edi]
call WriteDec
add edi,TYPE arrayB ; point to next integer
loop DISPLAY_ARRAY
;----------------------------------------------
call DumpRegs
call WriteDec
call crlf
invoke ExitProcess,0
main endp
end main
Edited the answer to add loop to display the array elements.

Arrays in MASM Assembly (very confused beginner)

I have a pretty basic question:
How do you populate arrays in assembly? In high level programming languages you can use a for-loop to set a value to each index, but I'm not sure of how to accomplish the same thing assembly. I know this is wrong, but this is what I have:
ExitProcess PROTO
.data
warray WORD 1,2,3,4
darray DWORD ?
.code
main PROC
mov edi, OFFSET warray
mov esi, OFFSET darray
mov ecx, LENGTHOF warray
L1:
mov ax, [edi] ;i want to move a number from warray to ax
movzx esi,ax ;i want to move that number into darray...
add edi, TYPE warray ;this points to the next number?
loop L1
call ExitProcess
main ENDP
END
Each time the loop runs, ax will be overwritten with the value of the array's index, right? Instead how do I populate darray with the array elements from warray? Any help would be very much appreciated...I'm pretty confused.
There are more than one way to populate an array and your code is almost working. One way is to use counter in the indirect address so you don't have to modify destination and source array pointers each loop:
ExitProcess PROTO
.data
warray WORD 1,2,3,4
darray DWORD 4 dup (?) ; 4 elements
.code
main PROC
mov edi, OFFSET warray
mov esi, OFFSET darray
xor ecx, ecx ; clear counter
L1:
mov ax, [edi + ecx * 2] ; get number from warray
movzx [esi + ecx * 4], ax ; move number to darray
inc ecx ; increment counter
cmp ecx, LENGTHOF warray
jne L1
call ExitProcess
main ENDP
END
Of course this code could be modified to fill the array backwards to possibly save couple of bytes like you probably meant to do in your original code. Here is another way that has more compact loop:
ExitProcess PROTO
.data
warray WORD 1,2,3,4
darray DWORD 4 dup (?) ; 4 elements
.code
main PROC
mov edi, OFFSET warray
mov esi, OFFSET darray
mov ecx, LENGTHOF warray - 1 ; start from end of array
L1:
mov ax, [edi + ecx * 2] ; get number from warray
movzx [esi + ecx * 4], ax ; move number to darray
loop L1
; get and set element zero separately because loop terminates on ecx = 0:
mov ax, [edi]
movzx [esi], ax
call ExitProcess
main ENDP
END
You should also note that when working with arrays of the same type you can do simple copy very efficiently using repeat prefix with instructions like MOVSD:
ExitProcess PROTO
.data
array1 DWORD 1,2,3,4
array2 DWORD 4 dup (?)
.code
main PROC
mov esi, OFFSET array1 ; source pointer in esi
mov edi, OFFSET array2 ; destination in edi
mov ecx, LENGTHOF array1 ; number of dwords to copy
cld ; clear direction flag so that pointers are increasing
rep movsd ; copy ecx dwords
call ExitProcess
main ENDP
END
You're probably not "supposed to know" this, but anyway, there were (way back when) an instruction and an instruction prefix that were made to do exactly this.
Take a look here at this Microsoft page: HERE (click on it)
On that page scroll down until you find this phrase...
"...These instructions are remnants of the x86's CISC heritage and in recent processors are actually slower than the equivalent instructions written out the long way...."
What you do is...
Put the size of the array in Ecx
Point Edi at the start of the arry
Use the appropriate string instruction to populate it
The syntax (Masm/Tasm/etc.) will probably look something like this...
Mov Ecx, The_Length_Of_The_Array ;Figure this out somehow
Lea Edi, The_Target_You_Want_To_Fill ;Define this somewhere
Now, if you want to copy from one place to another, do this...
Lea Esi, The_Source_You_Want_To_Copy ;Whatever, define it
Cld ;This is the direction flag, make it inc
Rep Movsb ;Movsb means move byte for byte
Now, if you want to stuff the same value in each byte in the arrray do this...
Mov AL, The_Value_You_Want_To_Stuff ;Define this to your liking
Cld ;This is the direction flag, make it inc
Rep Stosb ;Stosb means store AL into each byte
Again, these instructions are, for reasons others will elucidate, not cool anymore and if you use them you will get cooties or something.
There are also string instructions for comparison, "Scanning", "Loading", and so on. They were once quite useful (and still are, but the "modern" gang today won't admit it) particularly with the Rep prefix added to them.
If this helps, but you need more detail, feel free to ask.

Printing Out a String Stored in an Array of DWORDS

I'm writing a program in Assembly that will Bubble Sort an Array of Strings. A zero length string terminates the array. I approached this by declaring a DWORD array, where the string var., that is a byte size, shall be stored. My main problem is not the bubble sort itself, but that strings that were stored in the array wasn't outputting completely.
To hopefully make it clear, here is my code:
.586
.MODEL FLAT
INCLUDE io.h ; header file for input/output
space equ 0
cr equ 0dh
.STACK 4096
.DATA
myStrings byte "Delts",0
byte "Abs",0
byte "Biceps",0
byte 0
labelStrOut byte "Output is: ", 0
stringOut dword 11 dup (?)
stringNum dword 0
stringArray dword 20 dup (?)
.CODE
_MainProc PROC
mov edi, offset myStrings
mov esi, offset stringArray
popltLp:
cmp BYTE PTR [edi], 0
jz popltDone
mov ebx, [edi]
mov DWORD PTR [esi], ebx
add esi, 4
inc stringNum
xor ecx, ecx
not ecx
xor al, al
repne scasb
jmp popltLp
popltDone:
xor edx, edx
lea esi, stringArray
mov ebx, DWORD PTR [esi]
mov stringOut, ebx
output labelStrOut, stringOut
add esi, 4
mov ebx, DWORD PTR [esi]
mov stringOut, ebx
output labelStrOut, stringOut
add esi, 4
mov ebx, DWORD PTR [esi]
mov stringOut, ebx
output labelStrOut, stringOut
outptDone:
mov eax, 0 ; exit with return code 0
ret
_MainProc ENDP
END ; end of source code
As can be seen, no Bubble Sorting is being done yet...
The lines below 'popltDone' is just me messing around to see if the strings carried over to the array just fine. However, when printed out on the screen, only 4 characters were just showing up! The entire string line was just not being printed out, which is currently driving me crazy. Can someone please tell me what I am doing wrong?
Thanks to anybody taking the time reading this.
The problem is, you aren't using string pointers correctly. Specifically, here's the code I'm referring to:
mov ebx, [edi]
mov DWORD PTR [esi], ebx
If you were to translate this into English, it would be something like this:
Move the 4 byte value pointed to by edi into ebx.
Move the value in ebx into the memory address pointed to by esi.
This is perfectly legal and may actually be what you want in some cases, but I'm guess this isn't one of them. The reason you are only seeing the first 4 characters when you output your array of strings is because you copied the literal string into your array. A DWORD is 4 bytes so you get the first 4 characters. Here's what I would write:
mov DWORD PTR [esi], edi
Which translates into:
Move the pointer value edi into the memory address pointed to by esi.
Now you have not an array of strings, but an array of string pointers. If you were to write your code in C, decompile it, this is most likely what you would see. Rewrite your comparison and output functions to work with the pointer to a string instead of the literal characters in the string and you'll fix your problem.

Resources