Author Topic: Char Count Subprogram  (Read 10550 times)

Offline sledge

  • Jr. Member
  • *
  • Posts: 19
Char Count Subprogram
« 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:

Code: [Select]
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.
« Last Edit: August 16, 2011, 02:01:36 AM by sledge »

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Char Count Subprogram
« Reply #1 on: August 16, 2011, 03:47:00 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])

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.

Code: [Select]
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.

Code: [Select]
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

About Bryant Keller
bkeller@about.me

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Char Count Subprogram
« Reply #2 on: August 16, 2011, 04:44:57 AM »
"[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...

Code: [Select]
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

Offline sledge

  • Jr. Member
  • *
  • Posts: 19
Re: Char Count Subprogram
« Reply #3 on: August 16, 2011, 09:34:07 PM »
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!

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Char Count Subprogram
« Reply #4 on: August 16, 2011, 10:12:59 PM »
"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


Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Char Count Subprogram
« Reply #5 on: August 18, 2011, 12:43:25 AM »
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

About Bryant Keller
bkeller@about.me

Offline avcaballero

  • Full Member
  • **
  • Posts: 132
  • Country: es
    • Abre los Ojos al Ensamblador
Re: Char Count Subprogram
« Reply #6 on: August 18, 2011, 08:36:43 AM »
Another example:

Code: [Select]
; 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! :)

Offline avcaballero

  • Full Member
  • **
  • Posts: 132
  • Country: es
    • Abre los Ojos al Ensamblador
Re: Char Count Subprogram
« Reply #7 on: August 18, 2011, 10:32:59 AM »
A bit better example  ::)
Code: [Select]
; 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