Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Intro to Intel Assembly Language: Part 9A

hello world assembly

  • Please log in to reply
4 replies to this topic

#1 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4854 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 21 July 2010 - 12:19 AM

Hi! Welcome back to my latest installment of my series, Introduction to Intel Assembly Language.
In this issue we're going to start getting into some really fun stuff--graphics. Because this involves writing directly to memory, and most operating systems won't let you do that*, we're going to need to emulate our code on a virtual machine.
*For some stupid reason, Windows allows this for raw binaries in COM (but not EXE) format.

We need two things:
1) A virtual processor. I use Bochs IA-32 Simulator, which you can download for free from their site. If it's lagging too much, you can try downloading it from their mirror site.
Note that this emulates 32-bit only, which is fine because I'm not teaching 64-bit ASM here. (The emulator runs on 64-bit processors, but it doesn't simulate one.)

2) A virtual disk. You can feel free to put your programs on bootable media like a flash drive or CD drive, but remember that you can screw up your system. We'll use image files, and mount them on our file system as if they were real disks. This is a built-in behavior on Linux (and I think UNIX) systems. Windows can't do this without special software, which you can get here (also free).

Making a Hello World Program
We've already made a "Hello World" program before, but this is a different kind. We now have no operating system whatsoever. No system calls, no protected memory, no libraries...we're completely on our own. Sounds scary, but that means that we call all the shots, and can do whatever we want--read from location 0, flash the BIOS, erase the hard drive...

Fire up your favorite text editor, and let's get going! Down here all we can rely on is the BIOS to do some of the dirty work for us. First thing we need to do is set the video mode. Your graphics controller has a bunch of different modes. For now we're going to start with the easy-but-ghetto VGA family, and then later get into fun SVGA-type stuff that modern OSes use.

VGA Graphics
Within the VGA set of modes, we have a number of sub-modes, each supporting a specific resolution and a number of colors. Typically the higher the resolution, the lower the number of colors supported. Each mode is either specifically for text, or graphics. (You can print text on the graphics modes, but it looks kinda weird.)

Here are a few modes:
(Note: for text modes, the resolution is the number of columns and rows of displayable letters.)
[U]Mode#   Type    Width/Height/Colors[/U]
00h     Text    40x25x16
03h     Text    80x25x16
13h     Graph.  320x200x256
18h     Graph.  640x480x16
There are a lot more than these four, but I thought these were a good place to start. Mode 3 is the default video mode that the computer boots in. I would imagine that'd be assigned to zero, but whatever. Anyway, we can tell the BIOS to switch video modes by calling interrupt 0x10. (I covered interrupts earlier in this series.) Calling parameters:

AH: 0x00 (the subfunction #)
AL: Video mode to change to

So if we wanted to change to video mode 13h, we'd do:
mov    ax, 0013h
int    10h
Simple, right? Now let's get to the interesting part: printing our "Hello World" message. We can print out a boring old white-on-black message, or we could take advantage of the 16 colors and make it interesting. I vote interesting.

Character Attributes
We can change the foreground and background color of a character by changing its attributes. This is a one-byte number that contains the foreground color in the low 4 bits, and the background color in the high 4 bits. This is why text modes never have more than 16 colors in VGA. How do we know what colors those are? There are eight basic colors: black, red, green, yellow, blue, magenta, cyan, and white. Colors 0-7 correspond to dim shades, and 8-15 are brighter shades of the same colors. (Black turns to gray in the bright mode; white in dim mode is light gray, and pure white in bright mode.)
That's all well and good, but how do we change the character attributes? Again we use interrupt 0x10, but this time we use subfunction 9:

AH: 09h
AL: Character to display
BH: Page number (just put 0 for now)
BL: Attribute
CX: Number of times to repeat

So to display a bright green 'A' on a dark red background, we do:
mov    ax, 0941h
mov    bx, 001ah
mov    cx, 0001h
int    10h
By now you're probably pissing yourself with excitement. Hold on, we're almost there.

Putting It Together
To print our "Hello World" message, we'll have one string with a character followed by its attribute byte, terminated by a null character. In NASM syntax we'd have:
hello_message: db "H",19h,"e",9ah,"l" ...etc... , 00h, 00h
Our print function is going to accept a string, read a character into AL, then read the next byte (the attribute) into BL, display the character, and continue until it hits a null character. I strongly suggest you try writing it by yourself first, and then check back to see how I did it. We must do this in 16-bit code. The processor starts out in "real," i.e. virtual 8086 mode, which means it's 16-bit. Switching over to 32-bit mode is beyond the scope of this tutorial.

EDIT: The original code in the first version this tutorial didn't move the cursor over, and thus printed all the characters of the string in one location. It's fixed now.
print:
    push    bp
    mov     bp, sp
    sub     sp, 4               ; make room for two temp vars

    ; get the current cursor position
    mov     ah, 03h
    mov     bh, 00h
    int     10h
    
    ; save row and column
    mov     [bp - 4], dx

    ; load address of string into BX
    mov     bx, [bp + 4]

    ; store address of string into our local variable
    ; as well. we have to do this because the BIOS
    ; might overwrite the value, and we need to keep
    ; it across calls.
    mov     [bp - 2], bx

    .print_loop:
        mov     bx, [bp - 2]        ; load string pointer
        mov     al, [bx]            ; read character
        mov     bl, [bx + 1]        ; read attribute

        cmp     al, 00h             ; check for null
        je      .done               ; break if null

        ; prepare interrupt call to write character
        mov     ah, 09h             ; subfunction
        mov     bh, 00h             ; page 0 (ignore for now)
        mov     cx, 0001h           ; rep count
        int     10h                 ; call interrupt

        add     word [bp - 2], 2    ; increment pointer
        
        ; move cursor over
        mov     dx, [bp - 4]        ; load the last position we saved
        inc     dl                  ; increment column (DH=row, DL=col)
        mov     [bp - 4], dx        ; store the incremented result back
        mov     ah, 02h             ; int 10h, subfunction 2
        mov     bh, 00h             ; page 0 (ignore for now)
        int     10h                 ; call interrupt
        
        jmp     .print_loop         ; on to next character

    .done:
    add     sp, 4                   ; clean up local vars
    pop     bp
    ret     2                       ; pop off 2 bytes of arguments on return
Now that we've gotten that out of the way, let's build a few things around it to finish it up:
use16
cpu 8086
org 7c00h

; CODE
start:
    mov     dx, hello_message
    push    dx
    call    print

    ; deliberately hang the system
    cli
    hlt

<your print function here>

; DATA
    hello_message:  db "H", 29h, "e", 6fh, "l", 0e7h, "l", 10h,
                    db "o", 9ah, " ", 0a0h, "W", 87h, "o", 0d9h,
                    db "r", 0eah, "l", 01h, "d", 0deh, "!", 0adh,
                    db 00h, 00h
A few things of note:
- use16: This tells the compiler that our default addressing mode is 16-bit.
- cpu 8086: This tells the compiler that we can only use instructions valid for the 8086.
- org 7c00h: This tells the compiler that our code is going to be loaded at address 7c00h. This is where the BIOS always loads the boot code.

We have to compile it now, but we've got a problem: The output file has to be a raw binary, because we have no operating system to interpret an EXE or ELF format. How do we do this? We can tell nasm to compile to raw binary using the -f option, which specifies what the output format is. For this we pass in bin as the argument. When I compiled it, I used this:
nasm -Wall -Werror -fbin -O0 -o hello.bin hello.asm
As a general rule, always use the -Wall –Werror switches. If the compiler is warning you about something, it's warning you for a good reason.

Wow...this tutorial has gotten way too long. I'll continue with part B later, where I'll show you how to put your program onto a virtual disk, and run your first operating system-less ASM program.

Next In This Series
http://forum.codecal...ge-part-9b.html

Edited by dargueta, 18 November 2010 - 07:27 PM.

  • 1

#2 manux

manux

    CC Addict

  • Just Joined
  • PipPipPipPipPip
  • 211 posts

Posted 21 July 2010 - 11:18 AM

I'm eager for the next part :)
  • 0

#3 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4854 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 21 July 2010 - 06:03 PM

I'll try and finish/test it tonight, but with my luck I won't even get to my computer.

Edit: Ten days later...still haven't really worked on it...

Edited by dargueta, 01 August 2010 - 08:42 AM.

  • 0

sudo rm -rf / && echo $'Sanitize your inputs!'


#4 Alexander

Alexander

    YOL9

  • Moderator
  • 3963 posts
  • Location:Vancouver, Eh! Cleverness: 200
  • Programming Language:C, C++, PHP, Assembly

Posted 16 August 2010 - 12:33 AM

Oh, quick note, the forums mucked up the nasm command's dashes with en-dashes, normalizing them will let the command line work:
nasm -Wall -Werror -fbin -O0 -o hello.bin hello.asm

  • 0

All new problems require investigation, and so if errors are problems, try to learn as much as you can and report back.


#5 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4854 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 16 August 2010 - 08:06 AM

Odd...should be fixed now.
  • 0





Also tagged with one or more of these keywords: hello world, assembly

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download