I am experiencing some difficulties with understanding of how I can calculate the offset to the row in two dimensional array. The offsets will be used by main later to access the rows for the assignments and quizzes.
Assuming that I have,
scores DWORD 80,80,100, ; midterms, 2 scores
20,20,20,20,20,20,20,100, ; assignments, 7 scores
10,10,10,10,10,10,100 ; quizzes, 6 scores (lowest score dropped)
where "100" is a sentinel value. I understand that the offset is how many bytes away is the row from the start of array.
mov ecx, sentinelVal
mov edi, OFFSET scores
mov eax, sentinelVal
OffsetLoop:
repne scasd ; walk through the array until the target value is found.
jnz endLoop ; if the sentinel value is not found jump from the loop
; If the sentinel value if found
; edi is pointing to the location after the sentinel value
; I am not sure what I should do with the address of the array and edi
; to figure out the offset. Any help would be appreciated. Thanks!
loop OffsetLoop
endLoop:
edited:
I figured out what was my problem. My approach to calculate the offset was right, but it was the loop that caused the problem. It's not possible to simply set ecx to any arbitrary large numbers because scasd also uses ecx as a counter. By setting ecx to a large number, the instruction goes beyond the array boundary which triggers the exception.
mov ecx, LENGTHOF scores
OffsetLoop:
cld
repne scasd
jnz endLoop
mov ebx, edi
sub ebx, OFFSET scores
push ebx
inc ecx
loop OffsetLoop
endLoop:
Assuming your descriptions are correct in that edi is left pointing one beyond the sentinel word, you can simply do:
sub edi, OFFSET scores
to get the byte offset from the beginning of the table.
I'd be a little worried about this though:
mov ecx, sentinelVal
The ecx register is supposed to be a length limit and, while 100 may be a decent value, you should have a different symbolic name for it such as lenLimit.
Related
I'm writing a program in masm assembly to count and return the number of times integers appear in an array. I currently have the following code that allows me to populate an array with random integers. What I am struggling with is how to implement a counter that will store each occurrence of an integer at an index in the array. for instance, if the random array was [3,4,3,3,4,5,7,8], I would want to my count array to hold [3, 2, 1, 1, 1], as there are (three 3's, two 4's, etc).
I have the bounds of the random numbers fixed at 3/8 so I know they will be within this range. My current thinking is to compare each number to 3-8 as it is added, and increment my count array respectively. My main lack of understanding is how I can increment specific indices of the array. This code is how I am producing an array of random integers, with an idea of how I can begin to count integer occurrence, but I don't know if I am going in the right direction. Any advice?
push ebp
mov ebp, esp
mov esi, [ebp + 16] ; # holds array to store count of integer occurances
mov edi, [ebp + 12] ; # holds array to be populated with random ints
mov ecx, [ebp + 8] ; value of request in ecx
MakeArray:
mov eax, UPPER ; upper boundary for random num in array
sub eax, LOWER ; lower boundary for random num in array
inc eax
call RandomRange
add eax, LOWER
cmp eax, 3 ; Where I start to compare the random numbers added
je inc_3 ; current thought is it cmp to each num 3-8
mov [edi], eax ; put random number in array
add edi, 4 ; holds address of current element, moves to next element
loop fillArrLoop
inc_3: ; if random num == 3
inc esi ; holds address of count_array, increments count_array[0] to 1?
mov [edi], eax ; put random number in array to be displayed
add edi, 4 ; holds address of current element, moves to next element
loop MakeArray
My current thinking is to compare each number to 3-8 as it is added
No, you're vastly overcomplicating this. You don't want to linear search for a j (index into the counts) such that arr[i] == j, just use j = arr[i].
The standard way to do a histogram is ++counts[ arr[i] ]. In your case, you know the possible values are 3..8, so you can map an array value to a count bucket with arr[i] - 3, so you'll operate on counts[0..5]. A memory-destination add instruction with a scaled-index addressing mode can do this in one x86 instruction, given the element value in a register.
If the possible values are not contiguous, you'd normally use a hash table to map values to count buckets. You can think about this simple case as allowing a trivial hash function.
Since you're generating the random numbers to fill arr[i] at the same time as histograming, you can combine those two tasks, and instead of subtracting 3 just don't add it yet.
; inputs: unsigned len, int *values, int *counts
; outputs: values[0..len-1] filled with random numbers, counts[] incremented
; clobbers: EAX, ECX, EDX (not the other registers)
fill_array_and_counts:
push ebp
mov ebp, esp
push esi ; Save/restore the caller's ESI.
;; Irvine32 functions like RandomRange are special and don't clobber EAX, ECX, or EDX except as return values,
;; so we can use EDX and ECX even in a loop that makes a function call.
mov edi, [ebp + 16] ; int *counts ; assumed already zeroed?
mov edx, [ebp + 12] ; int *values ; output pointers
mov ecx, [ebp + 8] ; size_t length
MakeArray: ; do{
mov eax, UPPER - LOWER + 1 ; size of random range, calculated at assemble time
call RandomRange ; eax = 0 .. eax-1
add dword ptr [edi + eax*4], 1 ; ++counts[ randval ]
add eax, LOWER ; map 0..n to LOWER..UPPER
mov [edx], eax ; *values = randval+3
add edx, 4 ; values++
dec ecx
jnz MakeArray ; }while(--ecx);
pop edi ; restore call-preserved regs
pop ebp ; including tearing down the stack frame
ret
If the caller doesn't zero the counts array for you, you should do that yourself, perhaps with rep stosd with EAX=0 as a memset of ECX dword elements, and then reload EDI and ECX from the stack args.
I'm assuming UPPER and LOWER are assemble time constants like UPPER = 8 or LOWER equ 3, since you used all-upper-case names for them, and they're not function args. If that's the case, then there's no need to do the math at runtime, just let the assembler calculate UPPER - LOWER + 1 for you.
I avoided the loop instruction because it's slow, and doesn't do anything you can't do with other simple instructions.
One standard performance trick for histograms with only a few buckets is to have multiple arrays of counts and unroll over them: Methods to vectorise histogram in SIMD?. This hides the latency of store/reload when the same counter needs to be incremented several times in a row. Your random values will generally avoid long runs of the same value, though, so worst-case performance is avoided.
There might be something to gain from AVX2 for large arrays since there are only 6 possible buckets: Micro Optimization of a 4-bucket histogram of a large array or list. (And you could generate random numbers in SIMD vectors with an AVX2 xorshift128+ PRNG if you wanted.)
If your range is fixed (3-8), you have a fixed-length array that can hold your counts:
(index0:Count of 3),(index1:Count of 4)..(index5:Count of 8s)
Once you have an element from the random array, you just take that element and put it through a switch:
cmp 3, [element]
jne compare4
mov ebx, [countsArrayAddress] ; Can replace [countsArrayAddress] with [ebp + 16]
add ebx, 0 ; First index, can comment out this line
mov ecx, [ebx]
add ecx, 1 ; Increment count
mov [ebx], ecx ; Count at the zeroth offset is now incremented
compare4:
cmp 4, [element]
jne compare5
mov ebx, [countsArrayAddress]
add ebx, 4 ; Second index (1*4)
mov ecx, [ebx]
add ecx, 1
mov [ebx], ecx
...
Is this what you mean? I come from using fasm syntax but it looks pretty similar. The above block is a bit unoptimized, but think this shows how to build the counts array. The array has a fix length, which must be allocated, either on the stack (sub rsp the correct amount) or on the heap, i.e with heapalloc/malloc calls. (Edited, see you're using 32-bit registers)
I'm solving online test, that our teacher has come up with.
The task is not too hard itself:
EAX contains a pointer to an unknown size array of shorts terminated with 0.
Write a code that finds maximum of all elements that !does not repeat!, which goes to BX register.
EAX and contents of the array must remain unchanged.
For example, the array:
15,900,900,8,96,108,108,96,0
will have 15 in BX.
If an element with properties listed above, return 0;
But the problem is in the instruction set we have to use:
the only commands we have are MOV, PUSH, POP, INC, DEC, NEG, LOOP, STD, CLD, REP* MOVS*, CMPS*, SCAS*, LODS* and STOS*!
No conditional jumps allowed, no functions.
With those instructions I'm completely lost.
I tried searching the end of the array using
cld
mov edi, eax
mov ecx, -1
mov al, 0
repne scasb
inc ecx
inc ecx
neg ecx
and I'm thinking of repeating loop rep cmps with decrementing array length I found in previous state. But can't come up with a solution that works.
I'm using Intel x86 assembly NASM variation, but any will do.
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.
My program prints the msg3 statement (PutStr msg3), but does not proceed to the
DIV CX
instruction in my program.
Is there something I'm doing incorrectly with that register?
Or should the instruction be
DIV [CX]
instead or do I not have the compare and jump conditions set correctly?
prime_loop:
sub AX,AX ;clears the reg to allow the next index of the array
sub CX,CX ;clears counter to decrement starting from number of the value for array
mov AX, [test_marks+ESI*4] ;copy value of array at index ESI into reg
mov CX, [test_marks+ESI*4] ;copy value of array at index ESI into reg for purposes of counting down
check_prime:
dec CX
nwln
PutStr msg3
div WORD CX ;divide value of EAX by ECX
cmp DX,0 ;IF the remainder is zero
je chck_divisor ;check to see divisor 'ECX'
sub AX,AX ;else clear quotient register EAX
sub DX,DX ;clear remainder register
mov AX,[test_marks+ESI*4] ;move the number of the current iteration back into EAX
jmp check_prime ;start again from loop
chck_divisor:
cmp CX,1
jne prime_loop ;if the divisor is not 1 then it is not a prime number so continue with iterations
PutInt AX ;else print the prime_num
PutStr
inc ESI
jmp prime_loop
done:
.EXIT
These are some points about your code:
If this is indeed 8086 assembly then instructions like mov AX, [test_marks+ESI*4] that use scaled indexed addressing simply don't exist!
The scale by 4 suggests that your array is filled with doublewords, yet you use just a word. This could be what you want, but it looks suspicious.
Let's hope no array element is 1 because if so, then the div cx instruction will trigger an exception (#DE). Because you don't test the CX register for becoming 0.
In the check_prime loop only the 1st iteration lacks the zeroing of DX in order to give a correct quotient.
The solution will depend on the targetted architecture 8086 or x86. Now your program is a mix of both!
It's possible that since DX is not zeroed before div, that you're getting overflow. I don't know how your environment handles overflow.
I have an array of two-byte integers, which I am populating with random 3 digit numbers, using ESI to walk through the array. I am getting an access violation when I attempt to access the 9th element of the array and store it in ESI.
ARRAYSIZE = 200
.data
list WORD ARRAYSIZE DUP(?)
fillArray PROC
push OFFSET list
mov esi, [esp] ;GET ADDRESS OF FIRST ELEMENT INTO ESI
mov ecx, request ;NUMBER OF ELEMENTS TO BE ADDED
ArrFill:
;calculate random 3-digit number, store in eax
dec ecx
mov [esi], eax ;THIS IS THE LINE THAT THROWS THE EXCEPTION
sub esi, 2
cmp ecx, 0
jnz ArrFill
Exception thrown: Access violation writing location 0x00405FFE (The value of ESI when thrown).
When I change the array to four-byte integers, I also get an access violation for trying to access the 5th element of the array at the same address.
push OFFSET list
mov esi, [esp] ;GET ADDRESS OF FIRST ELEMENT INTO ESI
Why not simply assign the offset to ESI with mov esi, OFFSET list
mov [esi], eax ;THIS IS THE LINE THAT THROWS THE EXCEPTION
Since the array contains words you can only write the contents of AX, not EAX! Use mov [esi], ax
sub esi, 2
To progress through the array you need to add to the pointer not subtract from it. Use add esi, 2
Hudspero
You are getting an access violation because you are trying to write past the array boundary.
Few things:
You don't seem to be initializing ecx. You should initialize this to the size of the array.
As you are cmp ecx,0 you should be decrementing ecx before the comparison
You should move the esiaddress adjustment after you ensure you won't write past the boundary. I would restructure the code to test ecx for zero and jump out if true, otherwise decrement esiand jump back to ArrFill