lesson 5, functions and strings

pull/6/head
Carlos Fenollosa 10 years ago
parent f1dab708b1
commit 08b9e8be07

@ -0,0 +1,122 @@
*Concepts you may want to Google beforehand: control structures,
function calling, strings*
We are close to our definitive boot sector.
In lesson 6 we will start reading from the disk, which is the last step before
loading a kernel. But first, we will write some code with control structures,
function calling, and full strings usage. We really need to be comfortable with
those concepts before jumping to the disk and the kernel.
Strings
-------
Define strings like bytes, but terminate them with a null-byte (yes, like C)
to be able to determine their end.
```nasm
mystring:
db 'Hello, World', 0
```
Notice that text surrounded with quotes is converted to ASCII by the assembler,
while that lone zero will be passed as byte `0x00` (null byte)
Control structures
------------------
We have already used one: `jmp $` for the infinite loop.
Assembler jumps are defined by the *previous* instruction result. For example:
```nasm
cmp ax, 4 ; if ax = 4
je ax_is_four ; do something (by jumping to that label)
jmp else ; else, do another thing
jmp endif ; finally, resume the normal flow
ax_is_four:
.....
jmp endif
else:
.....
jmp endif ; not actually necessary but printed here for completeness
endif:
```
Think in your head in high level, then convert it to assembler in this fashion.
There are many `jmp` conditions: if equal, if less than, etc. They are pretty
intuitive but you can always Google them
Calling functions
-----------------
As you may suppose, calling a function is just a jump to a label.
The tricky part are the parameters. There are two approaches to parameters:
1. The programmer knows they share a specific register or memory address
2. Write a bit more code and make it generic
Approach 1 is easy. Let's just agree that we will use `al` for the parameters.
```nasm
mov al, 'X'
jmp print
endprint:
...
print:
mov ah, 0x0e ; tty code
int 0x10 ; I assume that 'al' already has the character
jmp endprint ; this label is also pre-agreed
```
You can see that this approach will quickly grow into spaghetti code. The current
`print` function will only return to `endprint`. What if some other function
wants to call it? We are killing code reusage.
The correct solution offers two improvements:
- We will store the return address so that it may vary
- We will save the current registers to allow subfunctions to modify them
without any side effects
To store the return address, the CPU will help us. Instead of using a couple of
`jmp` to call subroutines, use `call` and `ret`.
To save the register data, there is also a special command which uses the stack: `pusha`
and its brother `popa`, which pushes all registers to the stack automatically and
recovers them afterwards.
Including external files
------------------------
I assume you are a programmer and don't need to convince you why this is
a good idea.
The syntax is
```nasm
%include "file.asm"
```
Code!
-----
Let's jump to the code. File `boot_sect_print.asm` is the subroutine which will
get `%include`d in the main file. It uses a loop to print bytes on screen.
The main file `boot_sect_main.asm` loads a couple strings, calls `print` and hangs. If you understood
the previous sections, it's quite straightforward.
As a last goodie, we will learn how to print newlines. The familiar `'\n'` is
actually two bytes, the newline char `0x0A` and a carriage return `0x0D`. Please
experiment by removing the carriage return char and see its effect.

@ -0,0 +1,33 @@
[org 0x7c00] ; tell the assembler that our offset is bootsector code
; The main routine makes sure the parameters are ready and then calls the function
mov bx, HELLO
call print
; We will get fancy and print a newline
mov ah, 0x0e
mov al, 0x0A ; newline char
int 0x10
mov al, 0x0D ; carriage return char
int 0x10
; feel free to integrate this into "boot_sect_print" if you want to
mov bx, GOODBYE
call print
; that's it! we can hang now
jmp $
; remember to include subroutines below the hang
%include "boot_sect_print.asm"
; data
HELLO:
db 'Hello, World', 0
GOODBYE:
db 'Goodbye', 0
; padding and magic number
times 510-($-$$) db 0
dw 0xaa55

@ -0,0 +1,23 @@
print:
pusha
; keep this in mind:
; while (string[i] != 0) { print string[i]; i++ }
; the comparison for string end (null byte)
start:
mov al, [bx] ; 'bx' is the base address for the string
cmp al, 0
je done
; the part where we print with the BIOS help
mov ah, 0x0e
int 0x10 ; 'al' already contains the char
; increment pointer and do next loop
add bx, 1
jmp start
done:
popa
ret
Loading…
Cancel
Save