Related
%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
I am trying to write an iterative version of InsertionSort using MASM. After repeatedly getting unexpected errors, I tried running through the code line by line and watching in the debugger that it did what I expected. Sure enough, I the cmp instruction within my 'while loop' doesn't seem to be jumping every time it's supposed to:
; store tmp in edx
movzx edx, word ptr[ebx + esi * 2]; edx: tmp = A[i]
...
//blah blah
while_loop :
...
movzx eax, word ptr[ebx + 2 * edi]
cmp dx, ax
jge exit_while
For example, if I use the data given, after the first 'for loop' iteration, I get to the point where EDX=0000ABAB, and EAX=00003333. I reach the lines:
cmp dx, ax
jge exit_while
Since ABAB>3333, I'd expect it to jump to exit_while, yet it doesn't!
What is going on here??? I'm at a total loss.
.data
arr word 3333h, 1111h, 0ABABh, 1999h, 25Abh, 8649h, 0DEh, 99h
sizeArr dword lengthof arr
printmsg byte "The array is: [ ", 0
comma byte ", ", 0
endmsg byte " ]", 0
.code
main proc
call printArr
call crlf
push sizeArr
push offset arr
call insertionsort
call crlf
call printArr
call crlf
call exitprocess
main endp
insertionsort proc
push ebp
mov ebp, esp
_arr = 8
len = _arr + 4
mov ebx, [ebp + _arr]
mov ecx, [ebp + len]
dec ecx
mov esi, 1; store i in esi, with i=1 initially
; for (i = 1; i < SIZE; i++)
forloop:
; store tmp in edx
movzx edx, word ptr[ebx + esi * 2]; edx: tmp = A[i]
; store j in edi, where in initially j = i
mov edi, esi
;set j=i-1
dec edi
;while (j >= 0 && tmp<arr[j])
while_loop :
cmp edi, 0
jl exit_while
movzx eax, word ptr[ebx + 2 * edi]
; cmp dx, [ebx + 2 * edi]
cmp dx, ax
jge exit_while
; A[j] = A[j-1]
push word ptr [ebx+2*edi]
pop word ptr [ebx+2*edi+2]
; j = j - 1
dec edi
jmp while_loop
exit_while:
push dx
pop word ptr[ebx + 2*edi+2]
; mov[ebx + edi], dx; A[j] = tmp
inc esi; i = i + 1
loop forloop
finished:
mov esp, ebp
pop ebp
ret 8
insertionsort endp
printArr proc
push ebp
mov ebp, esp
mov ebx, offset sizeArr
mov ecx, [ebx]
mov esi, offset arr
mov edx, offset printmsg
call writestring
mov edx, offset comma
loop1 :
movzx eax, word ptr [esi]
call writeHex
call writestring
add esi, 2
loop loop1
mov edx, offset endmsg
call writestring
mov esp, ebp
pop ebp
ret
printArr endp
jge is the version for signed values - as such, a word with the value ABAB is negative - hence the comparison result you see.
Try jae (jump if above or equal) instead - the unsigned equivalent.
This is probably my final hurdle in learning x86 assembly language.
The following subroutine is giving me a segmentation fault:
;=================================================================
; RemCharCodeFromAToB - removes all chars between a and e from str
; arguments:
; str - string to be processed
; a - start
; e - end
; return value:
; n/a
;-------------------------------------------------------------------
RemCharCodeFromAToB:
; standard entry sequence
push ebp ; save the previous value of ebp for the benefi$
mov ebp, esp ; copy esp -> ebp so that ebp can be used as a $
; accessing arguments
; [ebp + 0] = old ebp stack frame
; [ebp + 4] = return address
mov edx, [ebp + 8] ; string address
while_loop_rcc:
mov cl, [edx] ; obtain the address of the 1st character of the string
cmp cl, 0 ; check the null value
je while_loop_exit_rcc ; exit if the null-character is reached
mov al, cl ; save cl
mov cl, [ebp + 16] ; end-char
push cx ; push end-char
mov cl, [ebp + 12] ; start-char
push cx ; push start-char
push ax; ; push ch
call IsBetweenAandB
add esp, 12
cmp eax, 0 ; if(ch is not between 'a' and 'e')
je inner_loop_exit_rcc
mov eax, edx ; copy the current address
inner_loop_rcc:
mov cl, [eax+1]
cmp cl, 0
je inner_loop_exit_rcc
mov [eax], cl
inc eax
jmp inner_loop_rcc
inner_loop_exit_rcc:
inc edx ; increment the address
jmp while_loop_rcc ; start the loop again
while_loop_exit_rcc:
; standard exit sequence
mov esp, ebp ; restore esp with ebp
pop ebp ; remove ebp from stack
ret ; return the value of temporary variable
;===================================================================
I am suspecting that there is something wrong with data conversions from 32-bit to 8-bit registers and vice-versa. My concept regarding this is not clear yet.
Or, is there something wrong in the following part
mov al, cl ; save cl
mov cl, [ebp + 16] ; end-char
push cx ; push end-char
mov cl, [ebp + 12] ; start-char
push cx ; push start-char
push ax; ; push ch
call IsBetweenAandB
add esp, 12
?
Full asm code is here.
C++ code is here.
Makefile is here.
cx and ax are 16-bit registers, so your push cx ; push cx; push ax are pushing 16-bit values on the stack, a total of 6 bytes. But IsBetweenAandB is apparently expecting 32-bit values, and you add 12 to esp at the end (instead of 6). So you probably wanted push ecx etc.
Also, you probably want to zero out eax and ecx before using them. As it stands, they probably contain garbage initially, and you only load useful data into the low 8 bits al and cl. Thus when IsBetweenAandB tries to compare the full 32-bit values, you are going to get false results. Or else you want to rewrite IsBetweenAandB to only compare the low bytes that you care about.
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 :)
I am trying to write an assembly program that calls a function in c that will replace certain characters in a string with a predefined character given that the currently character in the char array meets some qualification.
My c file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//display *((char *) $edi)
// These functions will be implemented in assembly:
//
int strrepl(char *str, int c, int (* isinsubset) (int c) ) ;
int isvowel (int c) {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
return 1 ;
if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
return 1 ;
return 0 ;
}
int main(){
char *str1;
int r;
// I ran my code through a debugger again, and it seems that when displaying
// the character stored in ecx is listed as "A" (correct) right before the call
// to "add ecx, 1" at which point ecx somehow resets to 0 when it should be "B"
str1 = strdup("ABC 123 779 Hello World") ;
r = strrepl(str1, '#', &isdigit) ;
printf("str1 = \"%s\"\n", str1) ;
printf("%d chararcters were replaced\n", r) ;
free(str1) ;
return 0;
}
And my .asm file:
; File: strrepl.asm
; Implements a C function with the prototype:
;
; int strrepl(char *str, int c, int (* isinsubset) (int c) ) ;
;
;
; Result: chars in string are replaced with the replacement character and string is returned.
SECTION .text
global strrepl
_strrepl: nop
strrepl:
push ebp ; set up stack frame
mov ebp, esp
push esi ; save registers
push ebx
xor eax, eax
mov ecx, [ebp + 8] ;load string (char array) into ecx
jecxz end ;jump if [ecx] is zero
mov esi, [ebp + 12] ;move the replacement character into esi
mov edx, [ebp + 16] ;move function pointer into edx
xor bl, bl ;bl will be our counter
firstLoop:
add bl, 1 ;inc bl would work too
add ecx, 1
mov eax, [ecx]
cmp eax, 0
jz end
push eax ; parameter for (*isinsubset)
;BREAK
call edx ; execute (*isinsubset)
add esp, 4 ; "pop off" the parameter
mov ebx, eax ; store return value
end:
pop ebx ; restore registers
pop esi
mov esp, ebp ; take down stack frame
pop ebp
ret
When running this through gdb and putting a breakpoint at ;BREAK, it segfaults after I take a step to the call command with the following error:
Program received signal SIGSEGV, Segmentation fault.
0x0081320f in isdigit () from /lib/libc.so.6
isdigit is part of the standard c library that i have included in my c file, so I am not sure what to make of this.
Edit: I have edited my firstLoop and included a secondLoop which should replace any digits with "#", however it seems to replace the entire array.
firstLoop:
xor eax, eax
mov edi, [ecx]
cmp edi, 0
jz end
mov edi, ecx ; save array
movzx eax, byte [ecx] ;load single byte into eax
mov ebp, edx ; save function pointer
push eax ; parameter for (*isinsubset)
call edx ; execute (*isinsubset)
;cmp eax, 0
;jne end
mov ecx, edi ; restore array
cmp eax, 0
jne secondLoop
mov edx, ebp ; restore function pointer
add esp, 4 ; "pop off" the parameter
mov ebx, eax ; store return value
add ecx, 1
jmp firstLoop
secondLoop:
mov [ecx], esi
mov edx, ebp
add esp, 4
mov ebx, eax
add ecx, 1
jmp firstLoop
Using gdb, when the code gets to secondloop, everything is correct. ecx is showing as "1" which is the first digit in the string that was passed in from the .c file. Esi is displaying as "#" as it should be. However, after I do mov [ecx], esi it seems to fall apart. ecx is displaying as "#" as it should at this point, but once I increment by 1 to get to the next character in the array, it is listed as "/000" with display. Every character after the 1 is replaced with "#" is listed as "/000" with display. Before I had the secondLoop trying to replace the characters with "#", I just had firstLoop looping with it self to see if it could make it through the entire array without crashing. It did, and after each increment ecx was displaying as the correct character. I am not sure why doing mov [ecx], esi would have set the rest of ecx to null.
In your firstLoop: you're loading characters from the string using:
mov eax, [ecx]
which is loading 4 bytes at a tie instead of a single byte. So the int that you're passing to isdigit() is likely to by far out of range for it to handle (it probably uses a simple table lookup).
You can load a single byte using the following Intel asm syntax:
movzx eax, byte ptr [ecx]
A few other things:
it will also have the effect that it probably wouldn't detect the end of the string properly since the null terminator might not be followed by three other zero bytes.
I'm not sure why you increment ecx before processing the first character in the string
the assembly code you posted doesn't appear to actually loop over the string
I've put some comments into your code:-
; this is OK: setting up the stack frame and saving important register
; on Win32, the registers that need saving are: esi, edi and ebx
; the rest can be used without needing to preserve them
push ebp
mov ebp, esp
push esi
push ebx
xor eax, eax
mov ecx, [ebp + 8]
; you said that this checked [ecx] for zero, but I think you've just written
; that wrong, this checks the value of ecx for zero, the [reg] form usually indicates
; the value at the address defined by reg
; so this is effectively doing a null pointer check (which is good)
jecxz end
mov esi, [ebp + 12]
mov edx, [ebp + 16]
xor bl, bl
firstLoop:
add bl, 1
; you increment ecx before loading the first character, this means
; that the function ignores the first character of the string
; and will therefore produce an incorrect result if the string
; starts with a character that needs replacing
add ecx, 1
; characters are 8 bit, not 32 bit (mentioned in comments elsewhere)
mov eax, [ecx]
cmp eax, 0
jz end
push eax
; possibly segfaults due to character out of range
; also, as mentioned elsewhere, the function you call here must conform to the
; the standard calling convention of the system (e.g, preserve esi, edi and ebx for
; Win32 systems), so eax, ecx and edx can change, so next time you call
; [edx] it might be referencing random memory
; either save edx on the stack (push before pushing parameters, pop after add esp)
; or just load edx with [ebp+16] here instead of at the start
call edx
add esp, 4
mov ebx, eax
; more functionality required here!
end:
; restore important values, etc
pop ebx
pop esi
mov esp, ebp
pop ebp
; the result of the function should be in eax, but that's not set up properly yet
ret
Comments on your inner loop:-
firstLoop:
xor eax, eax
; you're loading a 32 bit value and checking for zero,
; strings are terminated with a null character, an 8 bit value,
; not a 32 bit value, so you're reading past the end of the string
; so this is unlikely to correctly test the end of string
mov edi, [ecx]
cmp edi, 0
jz end
mov edi, ecx ; save array
movzx eax, byte [ecx] ;load single byte into eax
; you need to keep ebp! its value must be saved (at the end,
; you do a mov esp,ebp)
mov ebp, edx ; save function pointer
push eax ; parameter for (*isinsubset)
call edx ; execute (*isinsubset)
mov ecx, edi ; restore array
cmp eax, 0
jne secondLoop
mov edx, ebp ; restore function pointer
add esp, 4 ; "pop off" the parameter
mov ebx, eax ; store return value
add ecx, 1
jmp firstLoop
secondLoop:
; again, your accessing the string using a 32 bit value, not an 8 bit value
; so you're replacing the matched character and the three next characters
; with the new value
; the upper 24 bits are probably zero so the loop will terminate on the
; next character
; also, the function seems to be returning a count of characters replaced,
; but you're not recording the fact that characters have been replaced
mov [ecx], esi
mov edx, ebp
add esp, 4
mov ebx, eax
add ecx, 1
jmp firstLoop
You do seem to be having trouble with the way the memory works, you are getting confused between 8 bit and 32 bit memory access.