segment .data
array: db 68,222,29,68,33,234,179,103,37,85
fmt: db ",%d",0
segment .text
extern printf
global asm_main
asm_main:
enter 0,0 ; setup routine
pusha
push dword 10
push dword array
call print_array
add esp,8
push dword 10
push dword array
swap_byte_first_last:
mov eax,[array]
mov edx,[array+9]
mov [array+9],eax
mov [array],edx
call print_array
; don't delete anything following this comment
popa
mov eax, 0 ; return back to C
leave
ret
segment .data
ListFormat db ",%u", 0
segment .text
global print_array
print_array:
enter 0,0
push esi
push ebx
xor esi, esi
mov ecx, [ebp+12]
mov ebx, [ebp+8]
xor edx,edx
mov dl,[ebx + esi]
mov eax,edx
call print_int
dec ecx
inc esi
print_loop:
xor edx,edx
mov dl,[ebx + esi]
push ecx
push edx
push dword ListFormat
call printf
add esp, 8
inc esi
pop ecx
loop print_loop
call print_nl
pop ebx
pop esi
leave
ret
%define A_ADDR [ebp+12]
%define B_ADDR [ebp+8]
%define A_VAL [eax]
%define B_VAL [ebx]
segment .text
global swap_byte
swap_byte:
enter 0,0
pusha
mov eax, A_ADDR
mov ebx, B_ADDR
mov dl,A_VAL
mov cl,B_VAL
mov [eax],cl
mov [ebx],dl
popa
leave
ret
This prints 85,44,37,100,33,234,179,103,37,68. The result should be 85,222,29,68,33,234,179,103,37,68.
I was able to swap first element (68) with the last element (85), but numbers 222,29,and 68 have changed to 44,37,and 100. What changes do I need to make with the codes in swap_byte_first_last? Other parts of the code were given.
Related
I have elements loaded in stack and I need to move them to array. My code looks like this:
%include "asm_io.inc"
segment .data
array db 100 dup(0)
length db 0
segment .text
global _asm_main
extern getchar
_asm_main:
enter 0, 0
pusha
call getchar
char_loop:
mov ebx,10
sub eax, '0'
mul ebx
mov ebx, eax
call getchar
sub eax, '0'
add eax, ebx
push eax
inc BYTE[length]
call getchar
cmp eax, 10
je fill_array
cmp eax, 13
je fill_array
cmp eax, 32
je skip_spaces
jmp char_loop
skip_spaces:
call getchar
cmp eax, 32
je skip_spaces
jmp char_loop
fill_array:
mov ecx, [length]
mov ebx, array
l1:
pop eax
mov [ebx], eax ; should be al instead of eax
inc ebx
call print_int
call print_nl
loop l1
print_array:
mov ecx, [length]
mov ebx, array
l2:
mov eax, [ebx] ; should be al instead of eax
call print_int
call print_nl
inc ebx
loop l2
_asm_end:
call print_nl
popa
mov eax, 0
leave
ret
print_int in asm_io.asm is
print_int:
enter 0,0
pusha
pushf
push eax
push dword int_format
call _printf
pop ecx
pop ecx
popf
popa
leave
ret
Where int_format is int_format db "%i",0
Length and values in stack are correct, I had them printed but when I try to print array only last value is correct. Other values are random numbers. I tried combinations of registers of different sizes but it did not work. I think that error has to do something with size of registers or size of array.
Answer here:
As #xiver77 said in comments I was writing into array 4 bytes instead 1 byte. One element in array has 1 byte and I tried to write 4 bytes. That creates overflow of bites and change other elements in array. Instead mov [ebx], eax should be mov [ebx], al and mov eax, [ebx] for printing should be mov al [ebx].
%include "asm_io.inc"
;
; initialized data is put in the .data segment
;
segment .data
array: dd 180,32,455,499,388,480,239,346,257,84
fmt: dd ",%d",0
; uninitialized data is put in the .bss segment
;
segment .bss
resd 10
;
; code is put in the .text segment
;
segment .text
extern printf
global asm_main
asm_main:
enter 0,0 ; setup routine
pusha
; The following is just example of how to print an array
push dword 10
push dword array
call print_array
add esp,8 ; clean up stack
; don't delete anything following this comment
popa
mov eax, 0 ; return back to C
leave
ret
segment .data
ListFormat db ",%u", 0
segment .text
global print_array
print_array:
enter 0,0
push esi
push ebx
xor esi, esi ; esi = 0
mov ecx, [ebp+12] ; ecx = n
mov ebx, [ebp+8]
xor edx, edx
mov dl, [ebx + esi] ; ebx = address of array
mov eax,edx
call print_int
dec ecx
inc esi
print_loop:
xor edx,edx
mov dl,[ebx + esi]
push ecx ; printf might change ecx!
push edx ; push array value
push dword ListFormat
call printf
add esp, 8 ; remove parameters (leave ecx!)
inc esi
pop ecx
loop print_loop
call print_nl
pop ebx
pop esi
leave
ret
So this code prints out 180,0,0,0,32,0,0,0,199,1 when I want to print out 180,32,455,499,388,480,239,346,257,84. I think that it's because this is designed to print byte words. I'm trying to print in double words and I'm guessing something in the print_array needs to be changed. I tried mov dl, [ebx+esi*4] but it still doesn't print the array that I want to print. Or does something else needs to be changed to print array of double words?
You could leave it at changing the mov dl, [ebx+esi] instruction into mov edx, [ebx+esi*4], but that would be just half the fun!
1 Why not try to make a loop that can deal with the special case of the first value in the list that doesn't need the comma prefix? No more using print_int.
2 Also don't use the LOOP instruction. It's slow! The pair cmp jb (that can macro-fuse) is much better.
3 And replacing the prolog enter 0,0 and epilog leave codes by simply addressing the parameters via ESP relative addressing is simple enough.
4 Always consider the special cases! What if the array happens to be empty?
print_array:
push ebx
push esi
mov ebx, [esp+12] ; Begin array
mov esi, [esp+16] ; n
test esi, esi
jz done
lea esi, [ebx+esi*4] ; End array
mov edx, ListFormat+1 ; "%u"
more: mov eax, [ebx] ; Array dword value
push eax
push edx ; "%u" first time, ",%u" others
call printf
add esp, 8
add ebx, 4 ; To next dword in the array
mov edx, ListFormat ; ",%u"
cmp ebx, esi ; Current address < Last address ?
jb more ; Yes
call print_nl
done: pop esi
pop ebx
ret
Under the right conditions, keeping ESP fixed inside this loop can be worth doing. See Peter Cordes' comments below this answer.
Next is a version of this code that keeps ESP fixed inside the loop:
print_array:
push ebx
push esi
mov ebx, [esp+12] ; Begin array
mov esi, [esp+16] ; n
test esi, esi
jz done
sub esp, 8 ; Space for the printf args
lea esi, [ebx+esi*4] ; End array
mov edx, ListFormat+1 ; "%u"
more: mov eax, [ebx] ; Array dword value
mov [esp+4], eax
mov [esp], edx ; "%u" first time, ",%u" others
call printf
add ebx, 4 ; To next dword in the array
mov edx, ListFormat ; ",%u"
cmp ebx, esi ; Current address < Last address ?
jb more ; Yes
call print_nl
add esp, 8
done: pop esi
pop ebx
ret
My task here is to add a code that sorts the array with insertion sort.
'printf' function prints a string
printArray prints the array
For some reason the array doesn't get sorted, and i cant find the reason why.
Help will be appreciated.
main:
push MSG ; print welcome message
call printf
add esp,4 ; clean the stack
call printArray ;print the unsorted array
;;;;;;;;;;add code here;;;;;;;;;;
mov eax,1
loop1:
mov ebx, array
add ebx, eax
loop2:
mov esi, ebx
dec esi
mov esi, [esi] ;esi holds the value before what ebx points to
cmp [ebx], esi
ja endLoop2
mov edx, esi
mov esi, ebx
dec esi
mov ecx, [ebx]
mov [esi], ecx
mov [ebx], edx
dec ebx
cmp ebx, array
ja loop2
endLoop2:
inc eax
cmp eax, 11
jbe loop1
;;;;;;;end of your code;;;;;;;;;;;;;;
call printArray
mov eax, 1 ;exit system call
int 0x80
If your array is full of 1 byte values, use movb instead of mov when loading and storing to memory.
I am trying to convert C to x86. I am using a struct...
struct person_record_struct
{
char last_name[128];
char first_name[128];
char year_of_birth[10];
int month_of_birth; // January => 1
int day_of_birth; // 1st Day of a Month => 1
char drivers_license_no[128];
};
typedef struct person_record_struct person_record;
I am having trouble getting my scanf to work. Here is the C..
result = scanf("%s\n%s\n%s\n%d\n%d\n%s\n", &records[counter].last_name[0],
&records[counter].first_name[0], &records[counter].year_of_birth[0],
&records[counter].month_of_birth, &records[counter].day_of_birth,
&records[counter].drivers_license_no[0]);
And my x86..
;counter # [ebp-4]
;records # [ebp-16]
; format_string_main_2 db '%s\n%s\n%s\n%d\n%d\n%s\n', 0
; read in info
; push drivers_license_no
mov ebx, [ebp-16] ;
mov eax, [ebp-4]
mov ecx, struct_size
mul ecx
add eax, ebx
lea eax, [eax+276]
push eax
; push day_of_birth
mov ebx, [ebp-16]
mov eax, [ebp-4]
mov ecx, struct_size
mul ecx
add eax, ebx
lea eax, [eax+272]
push eax
; push month_of_birth
mov ebx, [ebp-16]
mov eax, [ebp-4]
mov ecx, struct_size
mul ecx
add eax, ebx
lea eax, [eax+268]
push ax
; push year_of_birth
mov ebx, [ebp-16]
mov eax, [ebp-4]
mov ecx, struct_size
mul ecx
add eax, ebx
lea eax, [eax+256]
push eax
; push first_name
mov ebx, [ebp-16]
mov eax, [ebp-4]
mov ecx, struct_size
mul ecx
add eax, ebx
lea eax, [eax+128]
push eax
; push last_name
mov ebx, [ebp-16]
mov eax, [ebp-4]
mov ecx, struct_size
mul ecx
add eax, ebx
lea eax, [eax+0]
push eax
push format_string_main_2
call scanf
add esp, 28
mov [ebp-12], eax
I'm using a check to see if result is 6 and if it's not my program that prints an error and exits. It keeps having an error and I'm not sure what I am doing wrong. Any help would be much appreciated. Thank you.
This is my calloc call which appears to be correct...
; // allocate the buffer of all the records
; records = (person_record *)calloc(number_of_records, sizeof(person_record));
push struct_size
mov eax, [ebp-8]
push eax
call calloc
add esp, 8
mov [ebp-16], eax
Under month_of_birth you have push ax instead of push eax. This would push only the lower 16 bits of the address on the stack, virtually guaranteeing a crash in scanf. Fix that and it should be OK.
There are many weird/wrong things going on in your code. It will be easier to show a cleaner way. You have not mentioned the Assembler you are using, there are a few for x86 and each has its own syntax. Here is how you can do it using NASM:
extern printf, scanf, calloc, exit, free, puts
global main
struc person_record
.last_name resb 128
.first_name resb 128
.year_of_birth resb 10
.month_of_birth resd 1
.day_of_birth resd 1
.drivers_license_no resb 128
.size equ $ - person_record
endstruc
MAX_RECORDS equ 2
section .data
Space db 32, 0
input_format db "%s%s%s%d%d%s", 0
output_format db "%s %s %s %d %d %s", 0
section .text
main:
push person_record.size
push MAX_RECORDS
call calloc
add esp, 4 * 2
mov esi, eax
mov ebx, eax
mov edi, MAX_RECORDS - 1
.FillRecord:
lea eax, [ebx + person_record.drivers_license_no]
push eax
lea ecx, [ebx + person_record.day_of_birth]
push ecx
lea edx, [ebx + person_record.month_of_birth]
push edx
lea eax, [ebx + person_record.year_of_birth]
push eax
lea ecx, [ebx + person_record.first_name]
push ecx
lea edx, [ebx + person_record.last_name]
push edx
push input_format
call scanf
add esp, 4 * 7
push Space
call puts
add esp, 4 * 1
add ebx, person_record.size
dec edi
jns .FillRecord
mov ebx, esi
mov edi, MAX_RECORDS - 1
.ShowRecord:
lea eax, [ebx + person_record.drivers_license_no]
push eax
mov ecx, [ebx + person_record.day_of_birth]
push ecx
mov edx, [ebx + person_record.month_of_birth]
push edx
lea eax, [ebx + person_record.year_of_birth]
push eax
lea ecx, [ebx + person_record.first_name]
push ecx
lea edx, [ebx + person_record.last_name]
push edx
push output_format
call printf
add esp, 4 * 7
push Space
call puts
add esp, 4 * 1
add ebx, person_record.size
dec edi
jns .ShowRecord
push esi
call free
add esp, 4 * 1
push 0
call exit
And the input and output of 2 records:
I have the following assembly code:
; File: strrev.asm
; A subroutine called from C programs.
; Parameters: string A
; Result: String is reversed and returned.
SECTION .text
global strrev
_strrev: nop
strrev:
push ebp
mov ebp, esp
; registers ebx,esi, and edi must be saved if used
push ebx
push edi
xor esi, esi
xor eax, eax
mov ecx, [ebp+8] ; load the start of the array into ecx
jecxz end ; jump if [ecx] is zero
mov edi, ecx
reverseLoop:
cmp byte[edi], 0
je reverseLoop_1
inc edi
inc eax
jmp reverseLoop
reverseLoop_1:
mov esi, edi ;move end of array into esi
mov edi, ecx ;reset start of array to edi
reverseLoop_2:
mov al, [esi]
mov bl, [edi]
mov [esi], bl
mov [edi], al
inc edi
dec esi
dec eax
jnz reverseLoop_2
end:
pop edi ; restore registers
pop ebx
mov esp, ebp ; take down stack frame
pop ebp
ret
Which works fine until you start looping through reverseLoop_2. Using gdb, eax is listed as being 11, which it should be (this is the length of the string I passed in through a separate c program). This is show in the debugger as:
Breakpoint 2, reverseLoop_2 () at strrev.asm:40
40 mov al, [esi]
(gdb) display $eax
1: $eax = 11
However, if I step through the program to the next line, it resets to 0.
(gdb) next
41 mov bl, [edi]
1: $eax = 0
I need eax to be preserved since its the one keeping track of how many times reverseLoop_2 needs to loop. Why is it resetting to 0 after the call to mov?
If you're using eax as a loop counter, you shouldn't write to it inside the loop :
reverseLoop_2:
mov al, [esi]
Remember that al is the least significant byte of eax :
I think this should work.
mov eax, address of your string
push esi
push edi
mov edi, eax
mov esi, eax
; find end of string
sub ecx, ecx
not ecx
sub al, al
cld
repne scasb
; points to the byte after '0x00'
dec edi
dec edi
; main loop will swap the first with the last byte
; and increase/decrease the pointer until the cross each other
_loop:
cmp esi, edi ; if both pointers meet, we are done
jg _done
mov al, [edi]
mov bl, [esi]
mov [esi], al
mov [edi], bl
inc esi
dec edi
jmp _loop
_done:
pop edi
pop esi