OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Mar 19, 2024 1:55 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: function in assembly
PostPosted: Wed May 11, 2016 11:42 am 
Offline
Member
Member

Joined: Fri Nov 16, 2012 4:16 am
Posts: 29
Location: Iran
Greetings. This is part of my code :

Code:
__entry_point:
   mov edi, 0xb8000
   mov esi, string
   mov ah, 0x0f
   .displaying:
      lodsb
      stosw
      or al, al
      jnz .displaying
      jmp short $


and I wanna make the print part a function, but It doesn't work. My way was pushing and popping ax, and also adding a "ret" to the printing part.


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 12:25 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5069
Are you calling this function from C?

If so, what calling conventions does the compiler expect?


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 12:40 pm 
Offline
Member
Member

Joined: Fri Nov 16, 2012 4:16 am
Posts: 29
Location: Iran
Octocontrabass wrote:
Are you calling this function from C?

If so, what calling conventions does the compiler expect?


No, it's part of assembly code, and I want to make it a function, in assembly.


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 12:48 pm 
Offline
Member
Member
User avatar

Joined: Wed May 21, 2008 4:33 am
Posts: 294
Location: Mars MTC +6:00
Make sure your memory is identity mapped and your data segments DS and ES are base 0 relative to your 0xb8000 mapped memory, lastly add a cld at the top of the function to make sure the string moves are in the right direction.

_________________
"God! Not Unix" - Richard Stallman

Website: venom Dev
OS project: venom OS
Hexadecimal Editor: hexed


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 1:34 pm 
Offline
Member
Member

Joined: Fri Nov 16, 2012 4:16 am
Posts: 29
Location: Iran
Code:

print_string:
    push ax
    mov ah, 0x0f
   .displaying:
      lodsb
      stosw
      or al, al
      pop ax
      jnz .displaying
      ret


Is this the right way?!


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 1:53 pm 
Offline
Member
Member
User avatar

Joined: Wed May 21, 2008 4:33 am
Posts: 294
Location: Mars MTC +6:00
Ok I think I didn't read the problem right the first time.

try this

Code:
print_string:
    push eax
    push edi
    push esi
    mov edi, 0xb8000
    mov esi, string
    mov ah, 0x0f
   .displaying:
      lodsb
      stosw
      or al, al
      jnz .displaying
   pop esi
   pop edi
   pop eax
   ret

_________________
"God! Not Unix" - Richard Stallman

Website: venom Dev
OS project: venom OS
Hexadecimal Editor: hexed


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 2:16 pm 
Offline
Member
Member

Joined: Fri Nov 16, 2012 4:16 am
Posts: 29
Location: Iran
Thanks. Now, I have another problem.

When I define strings :

Code:
string db 'foo', 10, 13, 0
string2 db 'bar', 10, 13, 0


It doesn't print string 2 in a new line. What can I do?


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 2:20 pm 
Offline
Member
Member

Joined: Fri Jan 30, 2015 4:57 pm
Posts: 215
Location: Germany
you have to move to the current character location to a new line manually.


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 2:21 pm 
Offline
Member
Member

Joined: Fri Nov 16, 2012 4:16 am
Posts: 29
Location: Iran
Techel wrote:
you have to move to the current character location to a new line manually.


Can you explain how?


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Wed May 11, 2016 2:29 pm 
Offline
Member
Member

Joined: Fri Jan 30, 2015 4:57 pm
Posts: 215
Location: Germany
Here I have some code I wrote some time ago, it uses 32 bit instructions, prints text pointed to by esi, moves to a newline and scrolls everything when neccessary. It also updates the vga hardware cursor.
'CurrentPosition' is a memory address which stores a 4 byte position of the current character position (initialized with 0 obviously)

Code:
;; esi: pointer to nullterminated string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

PrintSomeText:
   pusha
   mov edi, 0xB8000 ;;video memory
   mov eax, dword[CurrentPosition]
   shl eax, 1
   add edi, eax ;;2 bytes per character
   .loopi:
   lodsb ;;get character
   cmp al, 0 ;;test if null
   je .done
   stosb ;;write to video memory
   mov al, 0x07 ;;grey on black
   stosb
   jmp .loopi
   .done:
   sub edi, 0xB8000
   shr edi, 1
   add edi, 80-1 ;;go one line below
   mov eax, edi
   xor edx, edx
   mov ebx, 80
   div ebx ;;x-offset = cursor pos%80
   sub edi, edx ;;sub x-offset
   cmp edi, 80*50;;check if out of screen
   jnae .inscreen ;;else move screen up
      push edi
      mov esi, 0xB8000+80*2 ;;begin at second line
      mov edi, 0xB8000 ;;to first line
      mov ecx, 80*49 ;;entire screen minus one line
      rep movsw
      mov edi, 0xB8000+49*80*2 ;;clear last line
      mov cx, 80
      mov ax, 0x0720 ;;character (space) + color
      rep stosw
      pop edi
      sub edi, 80
   .inscreen:
   mov dword[CurrentPosition], edi ;;save current cursor position
   mov al, 0x0F ;;update hardware cursor
   mov dx, 0x3D4
   mov al, 0x0F
   out dx, al
   mov eax, edi
   inc dx
   out dx, al
   mov al, 0x0E
   dec dx
   out dx, al
   mov eax, edi
   xchg al, ah
   inc dx
   out dx, al
   popa
   ret


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Thu May 12, 2016 5:48 am 
Offline
Member
Member

Joined: Wed Jun 17, 2015 9:40 am
Posts: 501
Location: Athens, Greece
Hi,


I think you need to do more research if you are serious about this. Writing 5 lines of code and then asking why doesn't it work isn't going to help in long-term.

Hint: Have you checked the Intel manuals? Have you read the Ralph Brown's Interrupt List? Have you at least searched for some references about the behaviour of some instructions you aren't sure?


Regards,
glauxosdever


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Thu May 12, 2016 11:40 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

Haghiri75 wrote:
Greetings. This is part of my code :

Code:
__entry_point:
   mov edi, 0xb8000
   mov esi, string
   mov ah, 0x0f
   .displaying:
      lodsb
      stosw
      or al, al
      jnz .displaying
      jmp short $


Haghiri75 wrote:
and I wanna make the print part a function, but It doesn't work. My way was pushing and popping ax, and also adding a "ret" to the printing part.


Note that assembly doesn't really have "functions" (as people are used to them in high level languages). It's better to think of them as something different ("routines"), that can return multiple values, and can have multiple entry points. There is also no calling convention, and you decide where each routine's entry point expects to find input parameters and leaves output parameters. People that don't understand these differences end up writing bad code (code restricted by limitations that exist for compiler generated code but don't exist in assembly).

To turn your code into a routine, you need to document its inputs, outputs and which registers it trashes; and mostly just put a "ret" at the end. For example, your original code might become:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
   mov edi, 0xb8000
   mov ah, 0x0f
.displaying:
   lodsb
   stosw
   or al, al
   jnz .displaying
   ret


Note that this has a bug - it prints the last "0x00" character. It's also more efficient to use "test al,al" (as it doesn't modify AL). To fix that:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
   mov edi, 0xb8000
   mov ah, 0x0f
   lodsb
.displaying:
   stosw
   lodsb
   test al, al
   jnz .displaying
   ret


Also note that it'd be trivial to add multiple entry points to make this more flexible:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
   mov edi, 0xb8000

;Print an ASCIIZ string with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
; edi  Address in display memory to put string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite:
   mov ah, 0x0f

;Print an ASCIIZ string
;______________________________________________________________________________
;
;Input
; ah   Attribute for string
; esi  Address of ASCIIZ string
; edi  Address in display memory to put string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString:
   lodsb
.displaying:
   stosw
   lodsb
   test al, al
   jnz .displaying
   ret


Of course these entry points naturally lead into each other. In some cases you can't do that and you need a "jmp" instruction. For example:

Code:
;Print an ASCIIZ string at coords (x, y)
;______________________________________________________________________________
;
;Input
; ah   Attribute for string
; ebx  Y position
; esi  Address of ASCIIZ string
; edi  X position
;
;Output
; None
;
;Trashed
; eax, ebx, esi, edi
;______________________________________________________________________________

printString_atCoords:
    shl ebx,5                ;ebx = y * 32 = y * 16 * 2
    lea edi,[0xB8000+edi*2]  ;edi = 0xB8000 + x * 2
    lea ebx,[ebx*5]          ;ebx = y * 16 * 2 * 5 = y * 80 * 2
    add edi,ebx              ;edi = 0xB8000 + x*2 + y * 80 * 2 = 0xB8000 + (y * 80 + x) * 2
    jmp printString


Note that there's 2 ways to think about this. You could think of it as multiple entry points for the same routine. Alternatively you could think of it as multiple separate routines that use tail call optimisation combined with the removal of pointless "jmp" instructions.

For the latter, imagine you have something like this:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
    mov edi, 0xb8000
    call printString_blackAndWhite
    ret


Because we're not using the stack for arguments; the "tail call optimisation" mostly just means replacing "call then ret" with "jmp", like this:

Code:
printString_blackAndWhite_top_left:
    mov edi, 0xb8000
;    call printString_blackAndWhite
;    ret
    jmp printString_blackAndWhite


If the target of a jump is the next instruction, then the "jmp" does nothing and can be removed too. That gives us the same code as before.

Of course it's easier to think of it as a routine with multiple entry points (and not need to optimise) than it is to think of it as multiple routines (and then do 2 extra optimisations).


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Fri May 13, 2016 2:05 am 
Offline
Member
Member
User avatar

Joined: Wed Oct 18, 2006 3:45 am
Posts: 9301
Location: On the balcony, where I can actually keep 1½m distance
Haghiri75 wrote:
Code:

print_string:
    push ax
    mov ah, 0x0f
   .displaying:
      lodsb
      stosw
      or al, al
      pop ax
      jnz .displaying
      ret


Is this the right way?!


How many POPs are being executed here?

_________________
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Sat May 14, 2016 11:36 am 
Offline
Member
Member

Joined: Fri Nov 16, 2012 4:16 am
Posts: 29
Location: Iran
Brendan wrote:
Hi,

Haghiri75 wrote:
Greetings. This is part of my code :

Code:
__entry_point:
   mov edi, 0xb8000
   mov esi, string
   mov ah, 0x0f
   .displaying:
      lodsb
      stosw
      or al, al
      jnz .displaying
      jmp short $


Haghiri75 wrote:
and I wanna make the print part a function, but It doesn't work. My way was pushing and popping ax, and also adding a "ret" to the printing part.


Note that assembly doesn't really have "functions" (as people are used to them in high level languages). It's better to think of them as something different ("routines"), that can return multiple values, and can have multiple entry points. There is also no calling convention, and you decide where each routine's entry point expects to find input parameters and leaves output parameters. People that don't understand these differences end up writing bad code (code restricted by limitations that exist for compiler generated code but don't exist in assembly).

To turn your code into a routine, you need to document its inputs, outputs and which registers it trashes; and mostly just put a "ret" at the end. For example, your original code might become:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
   mov edi, 0xb8000
   mov ah, 0x0f
.displaying:
   lodsb
   stosw
   or al, al
   jnz .displaying
   ret


Note that this has a bug - it prints the last "0x00" character. It's also more efficient to use "test al,al" (as it doesn't modify AL). To fix that:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
   mov edi, 0xb8000
   mov ah, 0x0f
   lodsb
.displaying:
   stosw
   lodsb
   test al, al
   jnz .displaying
   ret


Also note that it'd be trivial to add multiple entry points to make this more flexible:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
   mov edi, 0xb8000

;Print an ASCIIZ string with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
; edi  Address in display memory to put string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite:
   mov ah, 0x0f

;Print an ASCIIZ string
;______________________________________________________________________________
;
;Input
; ah   Attribute for string
; esi  Address of ASCIIZ string
; edi  Address in display memory to put string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString:
   lodsb
.displaying:
   stosw
   lodsb
   test al, al
   jnz .displaying
   ret


Of course these entry points naturally lead into each other. In some cases you can't do that and you need a "jmp" instruction. For example:

Code:
;Print an ASCIIZ string at coords (x, y)
;______________________________________________________________________________
;
;Input
; ah   Attribute for string
; ebx  Y position
; esi  Address of ASCIIZ string
; edi  X position
;
;Output
; None
;
;Trashed
; eax, ebx, esi, edi
;______________________________________________________________________________

printString_atCoords:
    shl ebx,5                ;ebx = y * 32 = y * 16 * 2
    lea edi,[0xB8000+edi*2]  ;edi = 0xB8000 + x * 2
    lea ebx,[ebx*5]          ;ebx = y * 16 * 2 * 5 = y * 80 * 2
    add edi,ebx              ;edi = 0xB8000 + x*2 + y * 80 * 2 = 0xB8000 + (y * 80 + x) * 2
    jmp printString


Note that there's 2 ways to think about this. You could think of it as multiple entry points for the same routine. Alternatively you could think of it as multiple separate routines that use tail call optimisation combined with the removal of pointless "jmp" instructions.

For the latter, imagine you have something like this:

Code:
;Print an ASCIIZ string at the top left of the screen, with "black and white" text
;______________________________________________________________________________
;
;Input
; esi  Address of ASCIIZ string
;
;Output
; None
;
;Trashed
; eax, esi, edi
;______________________________________________________________________________

printString_blackAndWhite_top_left:
    mov edi, 0xb8000
    call printString_blackAndWhite
    ret


Because we're not using the stack for arguments; the "tail call optimisation" mostly just means replacing "call then ret" with "jmp", like this:

Code:
printString_blackAndWhite_top_left:
    mov edi, 0xb8000
;    call printString_blackAndWhite
;    ret
    jmp printString_blackAndWhite


If the target of a jump is the next instruction, then the "jmp" does nothing and can be removed too. That gives us the same code as before.

Of course it's easier to think of it as a routine with multiple entry points (and not need to optimise) than it is to think of it as multiple routines (and then do 2 extra optimisations).


Cheers,

Brendan


Thank you very much! But I have another question, does "printString_atCoords" routine, changes cursor place? I tried it but it didn't work.


Top
 Profile  
 
 Post subject: Re: function in assembly
PostPosted: Sat May 14, 2016 12:54 pm 
Offline
Member
Member

Joined: Fri Jan 30, 2015 4:57 pm
Posts: 215
Location: Germany
Writing to video memory directly doesn't affect the hardware cursor. Have a look at the local osdev vga wiki, there's some example code lingering around, or at my code I posted.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group