I defined a DWORD variable: arr DWORD 6 Dup(?) and tried to view this array with visual studio debugger.
I did try arr, 6 but it doesn't seem to work while debugging assembly code.
I also tried arr, arr + 1, arr + 2 separately but the result is terribly wrong.
How to view the entire array?
ReadDec_inline proc Uses eax ebx ecx esi edi, n:DWORD, pArr:DWORD
Local OldEsp:DWORD
; Type in n numbers
mov eax,10
mov ebx,n
mul ebx
add eax,n
sub eax,1; add (n-1) space characters
mov OldEsp,esp
sub esp,eax
mov esi,esp
invoke ReadString,esi,45,offset _dword
;Now we extract
mov esi,pArr
mov ecx,n
L1:
mov eax,0
W1:
.If(BYTE PTR[esp + eax] == " " || BYTE PTR[esp + eax] == 0)
mov BYTE PTR[esp+eax],0
jmp quitW1
.Else
inc eax
.EndIf
jmp W1
quitW1:
mov edi,esp
mov ebx,eax
invoke ReadDec, edi, 1
mov [esi],eax ; Fetch the number
add esi,4
add esp,ebx
inc esp ; Next number
loop L1
mov esp,OldEsp
ret
ReadDec_inline endp
proc Uses eax ebx ecx esi edi
Because of the mul ebx instruction, you should add EDX to the list. The result of this multiplication will be in EDX:EAX.
Or better, change
mov eax,10
mov ebx,n
mul ebx
add eax,n
sub eax,1; add (n-1) space characters
into
imul eax, n, 10 + 1
dec eax
invoke ReadString,esi,45,offset _dword
Shouldn't this hardcoded 45 be actually 65? Same as the calculated value?
Your L1 loop depends on the ECX register. I don't think that ECX is a call-preserved register, so probably invoke ReadDec, edi, 1 messes up the count.
mov esi, pArr
mov ecx, n
L1:
mov edi, esp
push ecx ; (1)
xor ebx, ebx
W1:
.If(BYTE PTR[edi + ebx] == " " || BYTE PTR[edi + ebx] == 0)
mov BYTE PTR[edi + ebx], 0
jmp quitW1
.Else
inc ebx
.EndIf
jmp W1
quitW1:
invoke ReadDec, edi, 1
mov [esi], eax ; Fetch the number
add esi, 4
pop ecx ; (1)
add esp, ebx
inc esp ; Next number
dec ecx
jnz L1
I defined a DWORD variable: arr DWORD 6 Dup(?) and tried to view this array with visual studio debugger.
Your invoke ReadDec_inline, 3, offset arr asks for just 3 array elements and you can see their values in the Memory 1 pane. I sincerely thought the issue here was not having specified the whole 6.
However, it fails in the Watch 1 pane because Visual Studio allows you to write expressions in the Watch windows. This means that arr, arr+1, arr+2, and arr+4 are expressions that first retrieve the value at arr (the first array element which has the value 13), and then have some constant added.
I don't use Visual Studio myself, but for MASM you could try:
arr[0] or just arr
arr[4]
arr[8]
or
[arr+0] or just arr
[arr+4]
[arr+8]
Related
I asked for help earlier and thought I was home free but I'm not. My logic was wrong and I've greatly altered it. This program is supposed to return the Max int in the array (which also happens to be the last element). After tracing it with GDB a few times, I see that I get to the 5th (2nd to last) element in the array, "20", when I hit a segmentation fault and the program halts. I set ECX to the array length and subtracted 1 from ECX to avoid this, but my logic is obviously wrong. Am I wrong in depending on the ECX counter to terminate the loop. Any ideas please?
***EDIT MAJOR EDITS TO CODE
SECTION .data ;data section
msg1: db "Here are the array elements:", 10, 0
msg1Len: equ $-msg1
msg2: db "Here is the sorted array:", 10, 0
msg2Len: equ $-msg2
arr: dd 12, 16, 6, 18, 10, 40, 30
arrLen: equ ($-arr)/4 ;number of elements = array length / 4
SECTION .bss
max resd 1 ;declare and reserve space for max
SECTION .text
global bsort
bsort:
push ebp ; save old ebp register
mov ebp, esp ; build a new stack
restart:
mov ebx, arr ; the base address argument is saved in ebx register
mov ecx, arrLen ; the size argument is saved in exc register
sub ecx, 1 ; Last member has no following element to compare with.
; So we need to reduce the counter by 1
top:
mov eax, [ebx] ;; access first array element. Move its value to eax
cmp eax, [ebx+4] ; compare the value of eax ([ebx]) with [ebx+4]
jle noswap ; if value at eax is less or equal to value of [ebx+4]
; no need to exchang values. Jump to noswap
xchg eax, [ebx+4] ; if value at eax > value [ebx+4], exchange
mov [ebx], eax ; store the new exchanged value at [ebx]
jmp restart ; reset the base address and counter. Start over
noswap:
add ebx, 4 ; move to the next array element
loop top ; loop back to the top if the register ecx > 0
leave
ret
global main
main:
push ebp
mov ebp, esp
mov ecx, msg1 ;print msg1
mov edx, msg1Len
call PString
;save array base address in ebx and save sizein in ecx
mov ebx, arr
mov ecx, arrLen; store num elements in ecx
;loop to print array
PrintArray:
mov eax, [ebx] ;move value [ebx] to eax
call PrintDec
call Println
add ebx, 4
loop PrintArray
;call bubblesort
call bsort
mov ecx, msg2
mov edx, msg2Len
call PString
;save arr base add in sbx and size in ecx
mov ebx, arr
mov ecx, arrLen
PrintSortedArray:
mov eax, [ebx]
call PrintDec
call Println
add ebx, 4
loop PrintSortedArray
;exit program and clean stack
mov esp, ebp
pop ebp
ret
PString:; save register values of the called function
pusha
mov eax,4 ; use 'write' system call = 4
mov ebx,1 ; file descriptor 1 = STDOUT
int 80h ; call the kernel
; restore the old register values of the called function
popa
ret
Println:
;will call PString func
;will change content of ecx and edx
;need to save registers used by the main program
section .data
nl db 10
section .text
pusha
mov ecx, nl
mov edx, 1
call PString
;return original register values
popa
ret
PrintDec:
;saves all registers so they return unmodified
;build the function to handle dword size
section .bss
decstr resb 10 ; 10 32-bit digits
ct1 resd 1 ;keep track of dec-string size
section .text
pusha; save registers
mov dword[ct1],0 ;initially assume 0
mov edi, decstr ; edi points to dec-string
add edi, 9 ; moved to the last element of string
xor edx, edx ; clear edx for 64-bit div
whileNotZero:
mov ebx, 10 ; get ready to divide by 10
div ebx ; divide by 10
add edx, '0' ; convert to ascii
mov byte[edi], dl ; put it in string
dec edi ; move to next char in str
inc dword[ct1] ; inc char counter
xor edx, edx ; clear edx
cmp eax, 0 ;is remainder 0?
jne whileNotZero ;if no, keep on looping
inc edi ; conversion finished, bring edi
mov ecx, edi ; back to start of string. make ecx
mov edx, [ct1] ; point to counterm edx gets # chars
mov eax, 4 ; print to stdout
mov ebx, 1
int 0x80 ; call kernel
popa ; restore registers
ret
I'm having trouble figuring out how to put a value I currently have in register EAX into an array.
The basic function of this program is to take the dates array, convert them to unique numbers, BubbleSort them, and convert them back.
I put the values I find with "datetonum" into EAX, and I want to store those values I find into my array named ra.
I can't seem to figure out how to do something that.
Seems like it should be fairly simple?
Thanks in advance!
include \masm32\include\masm32rt.inc
.data
dates BYTE "23-JUL-2010", 0, "23-JAN-2010", 0, "23-JUL-2009", 0, "31-JUL-2012", 0, "05-MAR-2010", 0
months BYTE "JAN",0,"FEB",0,"MAR",0,"APR",0,"MAY",0,"JUN",0,"JUL",0,"AUG",0,"SEP",0,"OCT",0,"NOV",0,"DEC",0
nDates DWORD 4
ra DWORD 1,0,0,0,0
.code
start:
lea EAX, dates ; create pointer to beginning of dates array
push EAX
call datetonum
;-------------------
;put value currently in EAX into array to be sorted
;-------------------
print "Array to be sorted contents: "
print chr$(13,10)
lea ECX, ra
mov [ECX + nDates], EAX
print ra
print chr$(13,10)
Here's the datetonum function
;param1 (date string) = address of 12 byte date
datetonum:
enter 4, 0 ; [EBP - 4] holds 4 byte temp variable
mov EBX, [EBP + 8] ; pointer to entire date string
lea ESI, [EBX] ; pointing to day part of date
lea EDI, [EBP - 4] ; pointing to address of local variable to store day string
mov ECX, 2
cld
rep movsb
mov EDX, 0
mov [EDI], EDX ; add null terminator
lea EDX, [EBP - 4]
mov EAX, sval(EDX) ; convert day string to int
push EAX ; push EAX to stack
; extract month from date
lea ESI, [EBX + 3] ; pointing to month part of date
lea EDI, [EBP - 4] ; pointing to address of local variable to store month string
mov ECX, 3
cld
rep movsb
mov EDX, 0
mov [EDI], EDX ; add null terminator
; debug print of month string
pushad
lea EDX, [EBP - 4]
print EDX
print chr$(9) ; print a tab character
print chr$(13,10)
popad
; find month number
sub ESI, ESI
lea EDX, [EBP - 4]
mov EAX, [EDX]
mov ECX, 12
search_top:
lea EDX, [months + ESI * 4]
mov EBX, [EDX]
inc ESI
cmp EAX, EBX
loopne search_top
mov EDX, ESI ; result is in ESI
pop EAX ; pop EAX off the stack
mov AH, DL ; copy the month int into AH
push EAX ; push EAX to stack
; convert year chars to 2 byte int
mov EBX, [EBP + 8]
lea ESI, [EBX + 7] ; pointing to year part of date
lea EDI, [EBP - 4] ; pointing to address of local variable to store year string
mov ECX, 4
cld
rep movsb
mov EDX, 0
mov [EDI], EDX ; add null terminator
lea EDX, [EBP - 4]
mov EDX, sval(EDX) ; convert year string to int
pop EAX ; pop EAX off the stack
mov EBX, EAX ; copy EAX (contains month in AH and day in AL) to EBX
mov EAX, EDX ; copy year to EAX
shl EAX, 16 ; shift the year over to high 16 bits in EAX
mov AX, BX ; copy the month and day into low 16 bits in EAX
;print EAX ; this crashes the proc
print str$(EAX)
print chr$(13,10)
leave
ret 4
exit
If your dates are stored in an array, and you want to use another array to store the dates converted to numbers, you will need at least one loop. You can use index registers (ESI, EDI) to point to both arrays, then use one register to read the value from one array, and the other register to move the value into the other array.
Let's imagine you already have next 3 procedures :
num2date : converts a num in EAX back into a date (the opposite of datetonum). This date is stored in a variable date that is declared like this : date BYTE "DD-MMM-AAAA".
bubble_sort : sorts the numbers in array ra.
transfer_date : takes the 11 bytes of variable date and move them into the position pointed by ESI (ESI is pointing somewhere inside array dates).
This could be the logic of your code :
start:
;▼ NEXT LOOP CONVERTS ALL DATES TO NUMBERS ▼
lea ESI, dates ;POINTER TO ARRAY "DATES".
lea EDI, ra ;POINTER TO ARRAY "RA".
mov ECX, 5 ;LENGTH OF ARRAY "DATES".
dates2numbers:
push ECX ESI EDI ;PRESERVE REGISTERS.
mov EAX, [ESI] ;CURRENT DATE.
push EAX
call datetonum ;CONVERT CURRENT DATE TO A NUMBER.
pop EAX ;GET THE NUMBER.
pop EDI ESI ECX ;RESTORE REGISTERS.
mov [EDI], EAX ;STORE NUMBER IN "RA".
add ESI, 12 ;NEXT ITEM IN ARRAY "DATES" (12 BYTES EACH).
add EDI, 4 ;NEXT ITEM IN ARRAY "RA" (4 BYTES EACH).
loop dates2numbers
call bubble_sort ;◄■■■ SORT ARRAY "RA".
;▼ NEXT LOOP CONVERTS ALL NUMBERS TO DATES ▼
lea ESI, dates ;POINTER TO ARRAY "DATES".
lea EDI, ra ;POINTER TO ARRAY "RA".
mov ECX, 5 ;LENGTH OF ARRAY "RA".
numbers2dates:
push ECX ESI EDI ;PRESERVE REGISTERS.
mov EAX, [EDI] ;CURRENT NUMBER.
call num2date ;CONVERTS EAX TO A DATE IN VARIABLE "DATE".
;VARIABLE "DATE" LOOKS LIKE THIS : DATE BYTE "DD-MMM-AAAA"
;"NUM2DATE" SHOULD PRESERVE ESI AND RESTORE IT
;BECAUSE "TRANSFER_DATE" WILL NEED IT.
call transfer_date ;TRANSFER CONTENT OF VARIABLE "DATE" INTO THE
;ARRAY "DATES" AT THE POSITION ESI IS POINTING TO.
pop EDI ESI ECX ;RESTORE REGISTERS.
add ESI, 12 ;NEXT ITEM IN ARRAY "DATES" (12 BYTES EACH).
add EDI, 4 ;NEXT ITEM IN ARRAY "RA" (4 BYTES EACH).
loop numbers2dates
I'm having trouble understanding how to traverse a 2-d array in x86 assembly language. I am missing a bit of understanding. This is what I have so far.
The issue is the lines with //offset and //moving through array
For the //offset line the error I am getting is "non constant expression in second operand"
and also
"ebx: illegal register in second operand"
For the next line I get the error
"edx: illegal register in second operand"
mov esi, dim
mul esi
mov eax, 0 // row index
mov ebx, 0 // column index
mov ecx, dim
StartFor:
cmp ecx, esi
jge EndFor
lea edi, image;
mov edx, [eax *dim + ebx] // offset
mov dl, byte ptr [edi + esi*edx] // moving through array
mov edx, edi
and edx, 0x80
cmp edx, 0x00
jne OverThreshold
mov edx, 0xFF
OverThreshold:
mov edx, 0x0
See x86 tag wiki, including the list of addressing modes.
You can scale an index register by a constant, but you can't multiply two registers in an addressing mode. You'll have to do that yourself (e.g. with imul edx, esi, if the number of columns isn't a compile time constant. If it's a power-of-2 constant, you can shift, or even use a scaled addressing mode like [reg + reg*8]).
re: edit: *dim should work if dim is defined with something like dim equ 8. If it's a memory location holding a value, then of course it's not going to work. The scale factor can be 1, 2, 4, or 8. (The machine code format has room for a 2-bit shift count, which is why the options are limited.)
I'd also recommend loading with movzx to zero-extend a byte into edx, instead of only writing dl (the low byte). Actually nvm, your code doesn't need that. In fact, you overwrite the value you loaded with edi. I assume that's a bug.
You can replace
imul edx, esi
mov dl, byte ptr [edi + edx] ; note the different addressing mode
mov edx, edi ; bug? throw away the value you just loaded
and edx, 0x80 ; AND already sets flags according to the result
cmp edx, 0x00 ; so this is redundant
jne OverThreshold
with
imul edx, esi
test 0x80, byte ptr [edi + edx] ; AND, discard the result and set flags.
jnz
Of course, instead of multiplying inside the inner loop, you can just add the columns in the outer loop. This is called Strength Reduction. So you do p+=1 along each row, and p+=cols to go from row to row. Or if you don't need to care about rows and columns, you can just iterate over the flat memory of the 2D array.
A 2-dimensional array is just an interpretation of a sequence of bytes. You'll have to choose in which order to store the items. For example, you might choose "row-major order".
I've written a demo where a buffer is filled with a sequence of numbers. Then the sequence is interpreted as single- and two-dimensional array.
tx86.s
%define ITEM_SIZE 4
extern printf
section .bss
cols: equ 3
rows: equ 4
buf: resd cols * rows
c: resd 1
r: resd 1
section .data
fmt: db "%-4d", 0 ; fmt string, '0'
fmt_nl: db 10, 0 ; "\n", '0'
section .text ; Code section.
global main
main:
push ebp
mov ebp, esp
; fill buf
mov ecx, cols * rows - 1
mov [buf + ITEM_SIZE], ecx
.fill_buf:
mov [buf + ecx * ITEM_SIZE], ecx
dec ecx
jnz .fill_buf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 1-dimensional array
; buf[c] = [buf + c * ITEM_SIZE]
xor ecx, ecx
mov [c], ecx
.lp1d:
mov ecx, [c]
push dword [buf + ecx * ITEM_SIZE]
push dword fmt
call printf
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols * rows
jl .lp1d
; print new line
push dword fmt_nl
call printf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 2-dimensional array
; buf[r][c] = [buf + (r * cols + c) * ITEM_SIZE]
xor ecx, ecx
mov [r], ecx
.lp1:
xor ecx, ecx
mov [c], ecx
.lp2:
; calculate address
mov eax, [r]
mov edx, cols
mul edx ; eax = r * cols
add eax, [c] ; eax = r * cols + c
; print buf[r][c]
push dword [buf + eax * ITEM_SIZE]
push dword fmt
call printf
; next column
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols
jl .lp2
; print new line
push dword fmt_nl
call printf
; next row
mov ecx, [r]
inc ecx
mov [r], ecx
cmp ecx, rows
jl .lp1
mov esp, ebp
pop ebp ; restore stack
xor eax, eax ; normal exit code
ret
Buidling(on Linux)
nasm -f elf32 -l tx86.lst tx86.s
gcc -Wall -g -O0 -m32 -o tx86 tx86.o
Running
./tx86
0 1 2 3 4 5 6 7 8 9 10 11
0 1 2
3 4 5
6 7 8
9 10 11
I am trying to read chars from input file, and place them in an array (except new line chars).
here is my code:
mov dword [counter], 0
mov edi, [size]
loop:
mov esi, state
cmp [counter], edi ; read all chars of the file
je end_loop
pushad
mov eax, 3
mov ebx, dword [file_desc]
mov ecx, read_char
mov edx, 1
int 0x80
popad
cmp byte [read_char], '1'
je put_char
cmp byte [read_char], ' '
je put_char
jmp loop
put_char:
mov edx, [read_char]
mov [esi + counter], edx
;; print number of char read from 0 to size-1
pushad
mov ecx, dword [counter]
push ecx
push printInt
call printf
add esp, 8
popad
;; print char read
pushad
push edx
push printChar
call printf
add esp, 8
popad
;; print value stored in state[counter]
pushad
push dword [esi + counter]
push printChar
call printf
add esp, 8
popad
mov eax, [counter]
inc eax
mov [counter], eax
jmp loop
end_loop:
the printing inside the loop works fine, as i get the char number, the char i have just read and the char in [esi + counter] (supposed to be state[counter]).
however, trying to print it after the reading loop, with this code:
mov dword [counter], 0
mov edi, [size]
printarray:
mov esi, state
cmp [counter], edi
je end
pushad
push dword [esi + counter]
push printChar
call printf
add esp, 8
popad
pushad
mov ecx, [counter]
inc ecx
mov [counter], ecx
popad
jmp printarray
end:
all I get is blanks (new char lines every line, from my printChar).
I don't understand my the values I read are not stored in the array.
There is no code between end loop and mov dword [counter], 0 just before the printarray loop.
mere are my data and bss:
section .data
newLine: DB "", 10, 0
printInt: DB "%d", 10, 0
printString: DB "%s", 10, 0
printChar: DB "%c", 10, 0
hello: DB "hello", 10, 0
section .bss
file_name resb 80
file_desc resd 1
WorldLength resd 1
WorldWidth resd 1
generations resd 1
print_freq resd 1
state resb 60*60
read_char resb 1
counter resd 1
size resd 1
Thank you for your help.
Well...
First of all, don't use 32-bit registers when operating on bytes. I'm sure that even if your code worked, some data would be overwritten.
I believe that your problem resides somewhere in statements similar to these
mov [esi + counter], edx
...
push dword [esi + counter]
They actually mean: "take the address of counter and add it to esi", which I think isn't what you want.
Onto this,
- reading the file character by character is terribly inefficient
- using counter variables instead of ecx is inefficient
- incrementing register rather than a memory location iself is inefficient too
I've tried to rewrite your code as much as I could, and I hope it was worth something.
mov eax, 3
mov ebx, dword [file_desc]
mov ecx, state
mov edx, [size]
int 0x80
; eax now contains the number of bytes read, so why not to use it?
mov ebx, eax
add ebx, state
mov byte [ebx], 0x0 ; this will be end-of-string, although it may not really be necessary
xor ecx, ecx ; this will be our counter now
_loop: ; loop is actually an instruction
cmp ecx, eax
je _end
inc ecx ; ecx++
mov dl, '\n'
cmp byte [state + ecx], dl ; is newline?
jne _loop ; nope? ok, try again
mov dl, ' ' ; yes?
mov byte [state + ecx], dl ; replace newline with space character
jmp _loop
_end:
; print the result
mov edx, eax ; the size - number of bytes read
mov eax, 4
mov ebx, dword [file_desc]
mov ecx, state
int 0x80
Problem solved.
I should have used this to put the char in the array:
put_char:
mov dl, [read_char]
mov [esi], dl
mov eax, [counter]
inc eax
mov [counter], eax
inc esi
jmp loop
I removed the printing, those were for debugging only.
Thanks :)
So we're currently studying Intel 8086 Insertion Sort Code that our professor showed us. He wanted us to figure out why the code skips the 0th element within the array and the 3rd element in the array from a code that he had taken from the web.
; void isort(int *a, int n)
; sorts the first n elements of a
;
; Parameters
; a - pointer to the array
; n - number of elements to sorts
%define a [ebp + 8]
%define n [ebp + 12]
isort:
enter 0, 0
pusha
mov ecx, 1
for:
mov ebx, ecx
imul ebx, 4
add ebx, a
mov ebx, [ebx]
mov edx, ecx
dec edx
while:
cmp edx, 0
jl while_quit
mov eax, edx
imul eax, 4
add eax, a
cmp ebx, [eax]
jge while_quit
mov esi, [eax]
mov dword [eax + 4], esi
dec edx
jmp while
while_quit:
mov [eax], ebx
inc ecx
cmp ecx, n
jl for
popa
leave
ret
And the sample array was {5, 8, 12, 2, 1, 7}. This is more for understanding the 8086 language since we just started a couple days ago, and I was wondering if anyone could explain how and what might be going wrong.
Consider what the code will do when ECX is 1:
The while loop will be entered with EBX=8 and EDX=0.
The jl while_quit will not be taken, since EDX is 0.
EBX is compared to [EAX]. That is; 8 is compared to a[0], which is 5, so the jge while_quit is taken.
mov [eax], ebx stores 8 at a[0], so your array now contains {8,8,12,2,1,7}. Clearly not what you want, since you've lost one of the original elements.
Apart from the code's logical flaw, those imul instructions are completely unnecessary since you can use scaled indices on the x86. So the sorting code can be simplified to this (I've verified that it sorts the array correctly):
mov ecx, 1
for_:
; esi = &a[holePos]
lea esi,[a + ecx*4]
; valueToInsert = a[holePos]
mov ebx, [esi]
while_:
; Did we reach the beginning of the array?
cmp esi, OFFSET a
jle while_quit
; valueToInsert < a[holePos-1] ?
cmp ebx, [esi-4]
jge while_quit
; a[holePos] = a[holePos-1]
mov eax, [esi-4]
mov [esi], eax
; esi = &a[holePos-1]
sub esi,4
jmp while_
while_quit:
; a[holePos] = valueToInsert
mov [esi], ebx
inc ecx
cmp ecx, 6
jl for_
I'm using MASM syntax, but you get the idea.