Browse Source

lesson 5, functions and strings

Carlos Fenollosa 10 years ago
parent
commit
08b9e8be07

+ 122 - 0
05-bootsector-functions-strings/README.md

@@ -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.

+ 33 - 0
05-bootsector-functions-strings/boot_sect_main.asm

@@ -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

+ 23 - 0
05-bootsector-functions-strings/boot_sect_print.asm

@@ -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