Hello I am tinkering around with assembly level programming. I have the following code
mov al, \'H\'
call my_function
my_function:
mov ah,0x0e
The CPU does not see your labels, it goes from instruction to instruction.
Unless the current instruction is some kind of jump (call
and ret
are also some kind of jumps) - after the CPU is done with current instruction, it will go to the next one, following it.
When you do call my_function
, it will execute all instructions inside the function, then upon executing ret
it will return back on next instruction after the call
.
And the next instruction is the first instruction of the my_function
again, doing it second time... After hitting ret
second time it will actually get lost who-knows-where (the ret
will take value at top of the stack and use it as address of next instruction, so whatever was in stack at the time of second ret
happening, there's your code now running wild...)
The assembly source is not just group of instructions, but you are also positioning them in memory, and controlling the code flow, by placing one instruction after another. The CPU will execute them sequentially, line after line, just like you wrote them (except when you change the code flow by using some kind of jump, then you can jump over several lines of source).
So if you want the CPU to stop after your main
is "finished", and you are creating bootloader, i.e. there's nothing to return to (no OS, or something like that), you will create dead-end at end of main by infinite loop like:
dead_end_loop:
pause ; give CPU hint this is idling loop
; so it will save power by switching off some circuitry
jmp dead_end_loop
And that "end of main" is right after the call my_function
. "my_function" itself must be defined outside of "main", for example after this infinite loop stopper.
Maybe you missed what is jmp $
and what was it purpose in the source. The $
symbol in this case means for assembler "address of current instruction/line", so jmp $
can be translated to "jump to this same line", and that means it is an infinite loop, the CPU will never execute anything else except this jmp $
instruction (except when it was set to handle some interrupt signals, then any such external signal will cause CPU to switch execution to the particular interrupt handler code, as the programmer/OS did configured it before entering the infinite loop).
One more idea: you may want to check https://schweigi.github.io/assembler-simulator/ and "step" over the example few times to see how CPU doesn't see the source, but only the machine code bytes (visible on right side as "memory"), and how it goes from one instruction to next, how IP
is changing, etc...
As Jester notified in his comment, all the labels which are supposed to be functions must be below jmp $
statement so it doesn't execute an extra time.
P.S. jmp $
is instructing the system to jump to the current location which results in an infinite loop, not allowing to proceed further where the functions exist.