From 08b9e8be07f36b14b3188617a3e087e0d1b60594 Mon Sep 17 00:00:00 2001 From: Carlos Fenollosa Date: Sun, 5 Oct 2014 12:54:46 +0200 Subject: [PATCH] lesson 5, functions and strings --- 05-bootsector-functions-strings/README.md | 122 ++++++++++++++++++ .../boot_sect_main.asm | 33 +++++ .../boot_sect_print.asm | 23 ++++ 3 files changed, 178 insertions(+) create mode 100644 05-bootsector-functions-strings/README.md create mode 100644 05-bootsector-functions-strings/boot_sect_main.asm create mode 100644 05-bootsector-functions-strings/boot_sect_print.asm diff --git a/05-bootsector-functions-strings/README.md b/05-bootsector-functions-strings/README.md new file mode 100644 index 0000000..7f36576 --- /dev/null +++ b/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. diff --git a/05-bootsector-functions-strings/boot_sect_main.asm b/05-bootsector-functions-strings/boot_sect_main.asm new file mode 100644 index 0000000..d6dafd8 --- /dev/null +++ b/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 diff --git a/05-bootsector-functions-strings/boot_sect_print.asm b/05-bootsector-functions-strings/boot_sect_print.asm new file mode 100644 index 0000000..2e25394 --- /dev/null +++ b/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