Hi,
Haghiri75 wrote:Greetings. This is part of my code :
Code: Select all
__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: Select all
;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: Select all
;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: Select all
;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: Select all
;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: Select all
;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: Select all
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