NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: sledge on August 16, 2011, 01:34:20 AM
-
My intention with this code is, to count how many characters a null terminated string has, and the return value is stored at AX.
The problem is with line (mov bx, [ebp+4+ax])
Here's the whole code:
char_count:
push ebp
mov ebp, esp
mov ax, 0 ; character offset
loop_start:
mov bx, [ebp+4+ax] ; bx = arg1[ax]
cmp bx, 0 ; compare if arg1[ax] = null terminate
jz end_loop ; if previous compare true, jump to end of loop
inc ax ; if not, go back to loop start
jmp loop_start
end_loop:
pop ebp
mov esp, ebp
ret
Btw, I'm using dos version of NASM on a 64bit system.
-
My intention with this code is, to count how many characters a null terminated string has, and the return value is stored at AX.
The problem is with line (mov bx, [ebp+4+ax])
Very good. But do you know why? The problem is, you're getting a WORD value, not a BYTE. So, for example. When you have the string "Hello", you're reading both the "H" and the "e" into bx and testing for 0. Then the next go-around, you're putting the "l" and "l" into bx and testing for 0 again. Finally, you put the "o" and the null terminator into bx and test for 0, but it doesn't find zero because the "o" is present! So, instead, lets try this using bytes instead.
char_count:
push bp
mov bp, sp
mov ax, 0 ; character offset
loop_start:
mov bl, [bp+4+ax] ; bl = arg1[ax]
cmp bl, 0 ; compare if arg1[ax] = null terminate
jz end_loop ; if previous compare true, jump to end of loop
inc ax ; if not, go back to loop start
jmp loop_start
end_loop:
mov sp, bp
pop bp
ret
I've made three minor changes to this code.
1. Since this code is running in DOS, I changed over to 16-bit register names. Not sure if it really matters, but it just looks cleaner. Also, this is probably required since you are addressing using [bp+4+ax] which means you're procedure expects arguments come after the IP and BP present on the stack.
2. I made "bx" into a "bl" so that we are only reading a byte value.
3. You pop the BP register before you update the SP register, I put these in the right order. really don't need to do this because SP isn't modified, but whatever.
Now, if you don't mind, I'm going to ASSume something here. You probably aren't wanting to access arg1[ax], rather you're probably going to be sending a pointer to the procedure as arg1. There is a slight difference in this.
char_count:
push bp
mov bp, sp
mov si, [bp+4] ; get address from arg1
loop_start:
mov bl, [si] ; bl = *arg1
cmp bl, 0 ; compare if *arg1 is null terminated
jz end_loop ; if previous compare true, jump to end of loop
inc si ; arg1++
jmp loop_start ; if not, go back to loop start
end_loop:
mov sp, bp
pop bp
ret
There might be another small "problem" where the program will segfault, this will most likely be the case if you don't adhere to the CDECL calling convention which you seem to be using. But I won't ASSume that much and just hope that you remember to add sp, 2 in your callee.
Regards,
Bryant Keller
-
"[bp + 4 + ax]" isn't going to work, either - accursed 16-bit addressing modes! You'd have to use si or di here instead of ax - or use 32-bit instructions in 16-bit code. This would require 32-bit hardware, but unless you bought your computer in an antique store, this won't be a problem.
As Bryant points out, your string probably isn't on the stack anyway - only the address/offset is. Do it the way Bryant shows you... but this won't return the length in ax! Umm...
char_count:
push bp
mov bp, sp
mov si, [bp+4] ; get address from arg1
loop_start:
mov bl, [si] ; bl = *arg1
cmp bl, 0 ; compare if *arg1 is null terminated
jz end_loop ; if previous compare true, jump to end of loop
inc si ; arg1++
jmp loop_start ; if not, go back to loop start
end_loop:
mov ax, si
sub ax, [bp + 4]
mov sp, bp
pop bp
ret
That's still untested... I really must get dosbox working! :)
Best,
Frank
-
Great, just compiled the code and NASM reported no errors, so the subprogram must have worked, problem is i cant see the result returned in AX because I dont know how to print a non-ascii number. Looks like I'll have to look for a DOS debugger so I can see whats going on the registers. Btw, thanks Bryant and Frank!
-
"No errors" just means that what you did was "legal" - instructions exist such that Nasm can produce the code you asked for. Doesn't mean it'll do what you intend, or even avoid run-time errors! Being "off by one" is quite a common error.
For printing a number, some code I posted in another thread may help...
http://forum.nasm.us/index.php?topic=1209.0
Best,
Frank
-
That's still untested... I really must get dosbox working! :)
Heh, ditto! I installed it on my netbook, but just my luck the power cable crapped out on me so I won't be using it until I get another cable. It would be nice to be able to test all the DOS related questions that pop up here. lol
-
Another example:
; avch
[org 100h]
[section .text]
mov di, String
call CalcLength
mov di, EndChar-13
or al, 30h ; Cuidadín con poner más de 9 caracteres ;)
stosb
mov dx, String
mov ah, 9
int 21h
ret
CalcLength:
; Calculates the length of a zstring
; di: string offset
; ax: length
push cx
mov cx, 0FFh
mov al, 0
repnz scasb
mov ax, 0FEh
sub ax, cx
pop cx
ret
[section .data]
String db "Im a liar", 0
db "has characters$"
EndChar:
PD: Long live to dosbox! :)
-
A bit better example ::)
; avch
[org 100h]
[section .text]
mov di, String
call CalcLength
mov di, EndChar-14
mov bl, 10
div bl
or ax, 3030h
stosw
mov dx, String
mov ah, 9
int 21h
ret
CalcLength:
; Calculates the length of a zstring
; di: string offset
; ax: length
push cx
mov cx, 0FFh
mov al, 0
repnz scasb
mov ax, 0FEh
sub ax, cx
pop cx
ret
[section .data]
String db "I'm a liar", 0
db "has characters$"
EndChar:
greetings