Skip to content

Writing a Hello World program

Konstantin (Vyacheslav) Kompan edited this page Feb 23, 2018 · 6 revisions

Preparations

So, you've decided to write your amazing program using TGBL. It requires NASM, and it is also desirable to have any build automation tool (e.g. GNU Make), QEMU to run and test program and Bochs to debug program.

Assuming that you have these tools, you should create a source file for your program. Let's call it hello.asm. To use TGBL, you have to copy lib/ folder to your program's one.

I assume that you are familiar with x86 assembly language, NASM syntax and preprocessor, BIOS interrupts etc. TGBL uses real mode, so you should know about segmentation and addressing in this mode.

Now you are ready to create your first program being runned without any operating system.

Program structure

Including TGBL

At first, BIOS searches for a bootable device which has bootloader. Bootloader loads specified number of sectors to RAM and runs them. TGBL has its own bootloader and uses 2 sectors: 1 for bootloader and 1 for library. So, you have to specify the number of sectors your code will use. A Hello World will use only one sector, so you write

%define NUM_OF_USER_SECTORS 1

Now you must set TGBL modules you're going to use:

%define INCLUDE_COMMON
%define INCLUDE_TEXT
%define INCLUDE_KEYBOARD
%define INCLUDE_TIME

At the moment of writing this guide there are 6 user-purposed modules:

  • Common - common definitions, such as screen size, colors, shutdown function etc.
  • Graphics - routines for drawing lines and borders
  • Text - routines for adding, printing and converting strings
  • Time - timer realization & sleep macro
  • Keyboard - keyboard handling routines
  • Pictures - macros for drawing ASCII pictures created with APET

After that you include main TGBL module:

%include "tgbl_main.asm"

And then you start your program:

tgblm_start main

Note that TGBL functions have prefix tgbl_ and TGBL macros have prefix tgblm_. tgblm_start main includes bootloader and modules and transfers control to label main in your code.

Time to do something

Now let's write main label:

main:
    ; Initialization
    call tgbl_initVGA ; Initialize 80x25 text mode
    tgblm_hideCursor  ; Literally hides cursor
    call initKeys
    
    ; Print "Hello World!"
    call printText
    
    ; Main program loop
    .mainLoop:
        call tgbl_keyboardHandler ; Checks if any key has been pressed
        tgblm_sleep 10            ; Sleep a bit before next loop iteration
        jmp .mainLoop
    
    hlt

There are two user-defined functions: initKeys and printText. Really, it's a good practice not to overload main.

Firstly, we initialize screen by switching to 80x25 VGA text mode and hiding cursor. Then we call initKeys, where we will initiaizate keystrokes handler.

Secondly, we call `printText', which prints "Hello World!".

Then begins the main loop. Here we should check keystrokes by calling tgbl_keyboardHandler and do any other routines that should be done in runtime. Also we force our program to sleep a bit before next loop iteration by adding tgblm_sleep 10 before jmp .mainLoop. hlt finishes the main code.

Let's write keyboard handling:

initKeys:
    call tgbl_clearKeyHandlersTable        ; Clear keystrokes handlers table
    tgblm_initKey KEY_ESC, ESC_key_handler ; Bind ESC_key_handler function to Escape key
    ret

ESC_key_handler:
    call tgbl_shutdown ; Shutdown computer
    ret ; Not necessary due to shutdowning

Keystrokes handlers table memory area may contain garbage after startup, so we clear it before use. Then we assign ESC_key_handler function to Escape key. tgbl_keyboardHandler will call this function on pressing Escape. In ESC_key_handler we call only tgbl_shutdown, which literally shutdowns computer.

Now let's write the rest of our code:

printText:
    tgblm_printString sampleText, FG_LGRAY, scrHMid, scrWMid - sampleText_len / 2 ; Print string at the screen middle
    ret

tgblm_addConstString sampleText, "Hello world!" ; Allocate string

tgblm_endSector 1, main ; End sector

tgblm_addConstString allocates the text string under the given lable name and creates a name_len constant equal to string length. In tgblm_printString it is used to print string in the middle of screen using constants scrHMid and scrWMid.

Sector finishes with tgblm_endSector. This macro calculates how much bytes the number of sectors starting at the given label will contain and fills unused space with zeroes.

Blade Build runner

Now you can save, build and run code. You can use Makefile, e.g.

ASM = nasm -f bin -i "../lib/"
SRC = helloworld

%.bin : %.asm
	$(ASM) $< -o $@
	dd if=/dev/zero of=$(SRC).img count=1008 bs=512
	dd if=$@ of=$(SRC).img conv=notrunc

all: $(SRC).bin

run: all
	qemu-system-i386 -s -m 32 -drive format=raw,file=$(SRC).bin

debug-bochs: all
	bochs -qf bochsrc-debug

clean:
	rm -f *.bin *.img

If you have everything correct, then you will see this:

After that, you can study examples and write your own programs. Best luck!

Clone this wiki locally