Alien-planet Landing-module

Alien-planet Landing-module

For the first module I have a single and simple task in mind: just land on the surface of the planet and send a message back on earth just to say that the landing manoeuvre was positively accomplished. Just for this simple task I need preparation and some design consideration. The first thing is to write down the program in a way that it isn't a program rather a text form of a flowchart. Well, it sounds crazy, but that's the way my brain works. Here are the simple steps I plan to do.

AP-LAND - step 1
AP-LAND: Alien Planet Landing Module #################################################################################### # # This software is intended to be loaded in memory at fixed location 0x07C00 # it displays a string on the video, it waits for a keyboard key press # and reboots the PC. It uses only standard BIOS interrupts. # #################################################################################### ################################################################## # displays characters on the screen ################################################################## ####################################### # waits for key press and then reboot ####################################### ######################## # data string is here ######################## "Module landed on alien planet..." "Press any key to reboot..."

This basic idea needs to be adjusted to take into account how the CPU works in order to produce the desired effect. First of all, I want to display two lines so, after the first line, I have to tell the CPU that it has to move one line down (use the special ASCII code 0x0A for a line feed or /Lf for short) and move the cursor back to origin on the left side (use the special ASCII code 0x0D for carriage returns or /Cr for short). Then I decide to put a 0-byte character (/00) at the end of the string as a marker to identify the actual end of the string. I plan to use a "check-and-do-loop" logic to print the characters on the screen. I could have used a "for-loop" logic as well, in which case I used the length of the string so I didn't need to mark the point where it ends. Another thing I have to consider is that I need to point the CPU at the memory location where the string begins. After all these considerations I realized that I have to adapt my software project a little bit in the following way:

AP-LAND - step 2
AP-LAND: Alien Planet Landing Module #################################################################################### # # This software is intended to be loaded in memory at fixed location 0x07C00 # it displays a string on the video, it waits for a keyboard keypress # and reboots the PC. It uses only standard BIOS interrupts. # #################################################################################### ############################################# # point DS:[SI] to the start of the string ############################################# ################################################################## # displays characters on the screen until "00" byte is found ################################################################## ####################################### # wait for key press and then reboot ####################################### ######################## # data string is here ######################## start_of_string: "Module landed on alien planet..." /Lf /Cr "Press any key to reboot..." /00

Now, I feel that the idea is clear in my mind so I start coding it (AP-LAND final). I use the INTEL syntax. However, this code cannot be assembled with any assembler since I don't have one. So this is rather a more detailed project than a program even if it looks like a code. The only thing I have is DEBUG and I will use it to do the coding later on. For the moment let me comment with you the code just to be sure that I am doing the right steps (remember I am still in the learning phase). First of all, we need to point the CPU to the beginning of the string so we have to load DS and SI register correctly. We cannot load DS immediately (i.e. with an immediate value such as DS=0x0000) since there is no CPU instruction for it, instead we have to use a workaround: we load AX first and then DS with the value in AX. Then we enter the "check-and-do-loop" logic to print the characters on the screen. What we do is to load a byte from the location pointed by DS:[SI] into AL (AL = DS:[SI]) check it, exit the loop if equal to 0x00 or continue otherwise. To do that I use the LODSB instruction which is a tiny tiny piece of software hard-wired in the CPU and corresponds to MOV AL, DS:[SI] followed by INC SI. We have to check if the byte in AL is equal to 0x00 in which case we have to exit the loop. To do that, we subtract the value of 0x00 to AL and we look at the result. If two numbers are equal the subtraction result of these numbers is always zero. Anytime the result of an operation is zero the Zero Flag is set (ZF = 1, or ZR in DEBUG). What the CPU actually does, is to subtract from AL the value of zero (since this is the check value) and affects the flags by this operation (CMP AL, 001). After that, we look at the zero flag and we decide to jump depending on its value (JE). If AL is not equal to 0x00 then we keep going on and we interpret the byte in AL as a valid ASCII code to put on the screen by invoking the BIOS function INT 10H. As soon as the character is on the screen we repeat the loop again and again until we reach the end of string marked with 0x00. Once the program terminates the display of the string, it has to wait for a keypress. To do so we use the BIOS function INT 16H. The CPU waits for a keypress and goes back to our software as soon as this happens. It returns in AX the key pressed by the user. As soon as INT 16H returns, the CPU executes a BIOS function INT 19H which reloads the boot sector in memory. Now it may seem that we are in a loop, but you have to remember that the real boot sector is the GRUB2 and not our "Alien-Planet landing-module" so the effect of reloading the boot sector leads back to the GRUB2 start menu (Set up the test rig - AA) from which we can select what to boot next.

AP-LAND - final
AP-LAND: Alien Planet Landing Module #################################################################################### # # This software is intended to be loaded in memory at fixed location 0x07C00 # it displays a string on the video, it waits for a keybord keypress # and reboots the PC. It uses only standard BIOS interrupts. # #################################################################################### ############################################# # point DS:[SI] to the start of the string ############################################# xor ax, ax # AX = 0x00 mov ds, ax # unfortunately "mov ds, imm16" doesn't exist mov si, start_of_string: # DS:[SI] = 0000:[start_of_string] is now pointed ################################################################## # displays characters on the screen until "00" byte is found ################################################################## do_until: lodsb # AL = DS:[SI]; SI = SI + 1 cmp al, 00 # affect flags by: AL - 00 je do_end: # jump if ZeroFlag is set (ZF = 1) mov ah, 0e # int10/AH = 0e : video teletype out # AL = character to write mov bx, 0007 # BH = page number # BL = foreground color (graphics mode only) int 10 # returns nothing jmp do_until do_end: ####################################### # wait for key press and then reboot ####################################### xor ax, ax # int 16/AH = 00 : keyboard get keystroke int 16 # returns: AH = BIOS scan code # AL = ASCII character int 19 # System-BIOS bootstrap loader ######################## # data string is here ######################## start_of_string: "Module landed on alien planet…" /Lf /Cr "Press any key to reboot..." /00

Ok so far so good with design, it is time to put hands on the PC and build the "Alien planet landing module". I start a new session of DEBUG. I check that the memory starting at SEG:01002 is all free (if not so I zeros it out with f 100 2ff 003) and I start coding. At this point, I don't know where my string is going to be so I set SI = 0x0000 keeping in mind that I have to go back here and fix it. Further down in the code I don't know how much ahead I have to jump with JE to get out of the loop, so I just jump to the next instruction keeping in mind that I have to go back here and fix it too. Jump to the next instruction is done pretty easily since at the moment I type I know that I am at memory location 0x10A and those conditional jumps are two bytes long (0x10A + 0x2 = 0x10C). At the end of the loop I have to jump backwards: this is easy since I can immediately read the memory location where I want to jump to (0x107). At the end of the first step, my landing module looks like it in Fig. A.

DEBUG step by step
Fig. A

Now that I know how big the code is I can start writing the string in memory using the command e (which stands for "edit" in DEBUG). Indeed I could start the string at 0x11B but I prefer to start at 0x130 just because it is easier with my eyes "to see it" inside the hexadecimal dump (Fig. B).

DEBUG step by step
Fig. B

Now that I know where the string begins I can fix the pointing used for SI. To do that I show the listing of the program with the command u (which stands for "unassembled" in DEBUG) and I correct the line (Fig. C).

DEBUG step by step
Fig. C

Next, I need to fix the jump at "line" 1371:010a. Note that I typed in JE and DEBUG returns JZ. The point is that the opcode for it is 0x74 and the CPU knows exactly what to do by that (Jump if Zero-Flag is set) but the human interpretation of this is double rising two different mnemonics for the same code: JE = jump in case equal is detected and JZ = jump if operation result is equal to zero. DEBUG cannot know which interpretation of the two I was using for the current code so when it finds the byte 0x74 it translates back all the time using the mnemonics: JZ. Now I know that the loop ends and exits at 0x115 so I fix it too.

DEBUG step by step
Fig. D

Then I look at it once again in the whole, I check the registry and I think that everything is ok to start testing it (Fig. E).

DEBUG step by step
Fig. E

For my test, I use the command p (proceed) rather than t (single-step trace) because of the interrupts i.e. as long as debug process any simple instructions p and t are equivalent, but as soon as it processes a CALL or an INT instruction the behaviour changes. The command p executes all in once the function or interrupt called; the command t steps inside the service routine instruction by instruction. After a few steps of execution, I realize that my testing facility is not "the deep-space". Out of metaphor, I have to correct the memory location used in DEBUG to match the one on the real loaded position in memory later on. So the instruction "DS = AX = 0x0000" that I use, is correct once the code is in memory, but needs to be adjusted as long as the code is running within DEBUG. To do it, I reset back the DS registry (Fig. F).

DEBUG step by step
Fig. F

Now I can keep on testing my code, but before I want you to notice how DEBUG prompts us what is happening during each test step. It shows the values of all registry in the CPU and the next instruction pointed by CS:IP that is going to be executed. Once you press p, you have to look at the line immediatelly following the command to look at the effect of the command (I have used different colours to highlight it with Microsoft-Paint). Keeping on testing, I see that everything seems to run smoothly. I want you to observe that the use of command p produces the effect of executing the interrupt service routine all at once so you see immediately the letter "M" (maked orange in Fig. G).

DEBUG step by step
Fig. G

Another thing I want you to observe is the impact of the command LODSB that changes the value in AL and SI.

DEBUG step by step
Fig. H

Now I know that it is working inside the loop, so I can modify the SI pointer to go immediately at the end of the string to check if it gets out of the loop in the right way. To do this I use the command d to identify the end of the string in memory and I use the command r to set it into the register SI (Fig. I).

DEBUG step by step
Fig. I

As I restart testing I notice that CMP AL, 00 is affecting the zero flag changing from NZ (which means not zero so Zero-Flag = 0) to ZR (which means zero so Zero-Flag = 1) since AL is 0x00. Soon after I see that the JZ/JE instruction works since the flag is set and IP = 0x0115 gets its new value (Fig. J). So the code exits the loop successfully.

DEBUG step by step
Fig. J

At this point, I have to stop testing since I cannot test INT 16 and INT 19 here and I have to "believe" or "trust" myself that I didn't any mistake there and see the effects on space whether or not the module will successfully land on the alien planet surface. So it is now time to take the code away from the test center and prepare it for the space mission. First of all, I have to put the "boot signature" (Fig. K).

DEBUG step by step
Fig. K

Then I have to correct SI by the fact that the code will run on the alien planet starting at 0x7c00 rather the 0x100 (Fig. L).

DEBUG step by step
Fig. L

As a final step, I tell DEBUG which name to assign to the file and where to save it by using the command n, then I set CX = 200 (= 512 bytes in decimal) to set the size of the file that I want to write to disk and I use the command w, but then.... something is wrong! It writes down to disk almost 70 KBytes but why? After a quick check, I realized that BX was "dirty" keeping the value of the last instruction that was affecting it (MOV BX, 0007) so the command "write" was executing correctly writing down BX:CX bytes to disk. I correct quickly the value of BX and I use w again (there is no need to use n again unless you want to change the file name now) this time successfully as you can see in Fig. M.

DEBUG step by step
Fig. M

Ok, the module is ready, I just have to put it in the cargo bay of the space rocket (out of metaphor "cargo bay" = boot record of test partition and "space rocket" = GRUB2), so I use the Linux dd command for it (Fig. N).

how to use dd command
Fig. N

Now I reboot my PC, I select the “Boot TEST” partition as in Set up the test rig - Fig. AA and....

Module landet on alien planet
Fig. O

IT WORKED! HEY! I MADE IT! WOW....
It is a small step (almost ridiculous) in computer science but a huge step for me!

Did you made it too? let me know in the comment.



  1. Compare (CMP) is the same as a subtraction (SUB) but the result of the operation is not used: CMP just updates the flags. Here is the way I like to think at it: SUB AX, BX is AX = AX - BX and CMP AX, BX is Flags = AX - BX. We know from the post on the two's complement that the CPU subtracts with use of the full adder circuit, so this single circut makes the CPU capable of adding, subtracting and comparing numbers (amazing!). You can see the details of it in the book "But How Do It know" from page 117 to page 122. [click back]
  2. All segments in DEBUG are equal to each other when the program starts so that DS = ES = CS = SS and the first 256 bytes (=0x100) are reserved for DEBUG. [click back]
  3. The command means fill from DS:0100 to DS:02FF with 0x00. Since the boot sector is 512 bytes long (= 0x200) and we start our code at 256 bytes (0x100) from origin then 0x100 + 0x200 = 0x300 so our sector goes from 0x100 to 0x2FF both included. [click back]

<PREV.  -  ALL  -  NEXT>

Comments