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 9B

hello world assembly

  • Please log in to reply
7 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 15 August 2010 - 03:47 PM

All right, here's part 9B of my tutorial series, Intro to Intel Assembly Language. This has clearly gone beyond the "intro" point, but I'm keeping the name so that followers don't say "WTF...it's gone!" Anyway, today we're going to advance a bit with our foray into the graphics world. Not a whole bunch of assembly here, mostly setting up Bochs and watching your creation.

Note: For some reason CodeCall hates me and shrinks every image I attach. Click on the thumbnail to see the original image.

Getting Started

1. Get Bochs
Bochs is a full x86-32 system simulator, complete with a BIOS, screen, disks, APM, and so on. First we need to get Bochs.

Windows: Download Bochs x86 PC emulator from SourceForge.net
Linux (Debian): At the command prompt, type sudo apt-get install bochs, and once that's done type sudo apt-get install bochs-x. This is the GUI libary that'll let you see the virtual screen.
Linux (Other): RPM Package
You may also be able to get it from a package installer or something. I only use Ubuntu, so I don't know how you'd do it in Red Hat or other distros.

2. Create a folder for this.
If I have to explain how to do this...you shouldn't be here. Trust me, we're going to end up with a number of files, and finding them all in a huge directory is not going to be fun.

3. Create a virtual disk.
Bochs provides a utility to create disks that you'll need to run to create your virtual hard drive. We're going to go with something really small because we don't need much right now; we can always make a bigger one later. Linux people need to chdir to their project directory in the command prompt, then type bximage. Windows people: Start > Programs > Bochs > Disk Image Creator Utility (or whatever it's called). You should get something like this:

[ATTACH]3290[/ATTACH]

Pick the .16 option for now.

If you remember our program from last time, we're going to need to put it onto our newly-created fake disk. Compile it (remember to use the -fbin option!) and blast it onto the image with the utility I wrote. (Source code is attached, compile it on your system.)

NOTE: The source code I posted for the print() function last time was defective. I've since fixed it, so you should go back and take a look at the changes. If you're too lazy to do that, I've attached it as well. Note to Windows people: Use something like ConTEXT Programmer's Editor (freeware) to view it. Line ending control characters are different in Linux and Windows, so Notepad will put everything on one line. More advanced text editors probably won't.

Almost done. We need to set up Bochs to find the disk image and use our program. Start it up by typing "bochs" on the terminal; Windows people have it on their Programs list. You'll get a main menu with seven options, like this:

[ATTACH]3291[/ATTACH]

We want #3, "Edit Options." We're going to need to save them later. Once you get into the editing menu, you're going to see a list of more options to mess with. We're not going to do anything fancy, so we're just going to choose "Disk Options," #10 on my version. From there go to Floppy Disk 0, and enter the file path to the disk image we created earlier. If you're in the same directory as the image, just type the name. (Told you, didn't I?)
Next it'll ask you for the disk size; put 160K, and when it asks if the disk is inserted or not, type "inserted" without quotes.

[ATTACH]3292[/ATTACH]

That's it for that. Now we need to save our options somewhere, so we don't have to keep changing them every time we want to test our program. Get back to the main menu, and choose "Save options to." Save the file in the same directory as you're in right now, otherwise Bochs will barf on you because it won't find the disk image.

Run Your Creation
First things first: Your code as it stands won't boot. That's right, it won't work. Why? To guard against corrupt disks, when the BIOS loads the first sector of the boot code it checks to see that the last two bytes of the sector are 55h AAh. We don't have that yet. What you need to do is stuff the remaining 512 bytes in your code with nulls (or whatever garbage you like) until you get to byte 510, and then write 55h AAh. We can do this quickly and safely with this:

times 510-($-$$) db 0
db 55h, 0aah
Good thing about this is that if we write more than 510 bytes of code, NASM will barf once it hits that and tell us that we wrote too much to fit. If we want to write more than that, we're going to have to load the rest of our code somewhere else and jump to it.

Once we've got that fixed, let's run it!
Linux users, navigate to your directory and type bochs -qf <your config file name here>. Windows people need to run Bochs and load the file from the main menu.

You should get a screen that looks like a normal computer booting up, followed by "Hello, CodeCall!" in rather hideous psychedelic colors, like this:

[ATTACH]3293[/ATTACH]

I just put in random attributes; you might want to color-coordinate a bit so you don't get unsightly ** like that. (By the way, the screen might flicker a bit. This is normal.)

Making It Go Faster
If you should decide to make a really long string, you'll notice that it takes a while for the BIOS to write all the characters to the screen. This'll be even more obvious when we get to graphics. No one likes this kind of lag. Fortunately, there's a way around it: writing directly to video memory.

What? We can write directly to memory?! Yes, we can. Remember what I said earlier? We're not running on top of an operating system...we are the operating system. As such, we can do whatever we want, which includes writing to whatever address we want. Ish. Right now we're still in 8086 mode, so we're restricted to addresses 0x00000-0xFFFFF. Once I show you how to break out of 8086 mode we'll be able to write to every address in existing memory.

How does writing to memory write to the screen? We take advantage of memory-mapping. The VGA video controller--the thing that's drawing on the screen--monitors a small range of our memory, and uses that as its input data for whatever it's doing. It'd be stupid to do everything with one byte, so we map just enough memory so that we can write to every part of the screen in the current video mode. For example:

Let's say video mode X has a resolution of 640x480 pixels and 16 colors per pixel. 16 colors can be represented by four bits, so we can use one byte to represent two pixels. We have 640*480 = 307200 pixels, divided by two pixels per byte --> exactly 150K of memory. A modern screen at 1024x768 with 32-bit color gets you: 1024 * 768 * 4 bytes per pixel = 3 MB of memory. Not too bad.

But back to writing to memory: Unfortunately, there isn't any one single base address to write to for graphics or text modes, but luckily there aren't too many. Three, to be exact: 0xA0000, 0xB0000, and 0xB8000. The segmented addresses we'll be using in 8086 mode are A000:0000, B000:0000, and B800:0000 respectively. Each VGA video mode--VGA, mind you--uses one of these addresses as its base address to write to. I say VGA because we're going to switch to SVGA and XGA later. VGA is an old controller, and standard versions can't do anything beyond 640x480. SVGA and XGA allow us much larger resolutions and color depths, up to 1024x768.

I'm digressing again...
The general rule of thumb is that VGA graphics modes use A000:0000, two-color text modes use B000:0000, and color text and low-color graphics modes use B800:0000. Here's a small table of some sample addresses:
MODE    BASE ADDRESS
00-06h  B800:0000
07h     B000:0000
...reserved...
0Dh-13h A000:0000
(Modes above 13h are kinda non-standard things that won't always work everywhere. I've listed the modes I know to be standard.)

Another great thing about writing to video memory: you don't have to worry about wraparound with rows and columns. Just keep writing to memory and it'll automatically go to the next line. Let's modify our print() function to use this direct-to-memory method. For now we're going to hard-code it to use mode 3 settings. If we look at the table, we see that video mode 3 uses B800:0000 as the base address. So:

print:
    push    bp                          ; create the usual stack frame
    mov     bp, sp
    
    ; save segment registers and other key ones. we have to save these by
    ; standard C convention.
    push    ds
    push    es
    push    si
    push    di
    
    mov     ax, 0b800h                  ; load video segment into ES
    mov     es, ax

    ; load data segment address into DS. the BIOS assumes everything is in one
    ; segment, so all segment registers start out the same; therefore, our
    ; data, code, and stack segments are all the same. we only set DS here
    ; because it may have changed before this function was called.
    mov     ax, cs                      
    mov     ds, ax
    
    mov     si, [bp + 4]        ; load string offset into SI
    xor     di, di              ; ES:DI = A000:0000 now
    
    .print_loop:
        mov     ax, [ds:si]     ; load both character and attribute at once
        cmp     al, 00h         ; check for null character
        je      .done           ; hit end of string, break out of loop
        
        ; not a null character, write character and attribute to video memory
        mov     [es:di], ax

        ; increment pointers
        inc     si
        inc     di
        jmp     .print_loop

    .done:
    ; restore registers
    pop     di
    pop     si
    pop     es
    pop     ds
    pop     bp
    ret     2                   ; pop off 2 bytes of arguments on return

You'll get something like this:

[ATTACH]3294[/ATTACH]

Er...why is "Hello World!" all the way up at the top? Because we wrote to the beginning of video memory, which corresponds to the top left corner of the screen. When we're writing to memory, we don't care where the cursor is because it doesn't matter. We can go wherever we want.

Alright, I think that's enough for today. Next time we're going to do something a bit more fun...

Attachments
- Disk-file burning source code
- Source code for printing to the console using the BIOS
- Source code for printing to the console using direct-to-memory writing. (DTM)

Resources
Terribly-formatted table of video modes
VGA on Wikipedia

Attached Thumbnails

  • 5_dma..png
  • 4_run..png
  • 2_start..png
  • 3_edit..png
  • 1_create..png

Attached Files

  • Attached File  vdiskburn..c   3.38KB   414 downloads
  • Attached File  .asm   3.04KB   409 downloads
  • Attached File  .asm   2.52KB   445 downloads

Edited by dargueta, 21 August 2010 - 03:41 PM.
Typo

  • 1

#2 Alexander

Alexander

    YOL9

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

Posted 15 August 2010 - 05:23 PM

I've actually worked with porting Bochs to a MIPS platform to run DLX/W95 on a mobile device, it's pretty darn useful. This totally makes me want to mess around with it again, thanks for this tutorial part!
  • 0

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


#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 15 August 2010 - 08:51 PM

No problem! I'm going to keep going with this series. Probably going to show you how to make libraries out of these so you can use C to do the heavy lifting.

Edited by dargueta, 15 August 2010 - 08:51 PM.
Grammar

  • 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 - 01:13 AM

I could do it with my own dd, but your diskburn.c program hangs directly after:
if( writeback )

    fseek(fout, start, SEEK_SET);
Any clues? input is my print.bin from updated asm and disk option is os.img that was generated from bximage.
  • 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:11 AM

Weird. Only way I can see that it'd hang is if the input image is empty. I can't do it right now, but add a check for zero before the loop and gag if it is. What's your output file, the disk?

Edit: Fixed the problem, new version uploaded.

Edited by dargueta, 16 August 2010 - 02:41 PM.

  • 0

#6 se7en

se7en

    CC Regular

  • Member
  • PipPipPip
  • 32 posts

Posted 01 October 2010 - 12:29 AM

please continue the series!
  • 0

#7 dargueta

dargueta

    I chown trolls.

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

Posted 01 October 2010 - 09:25 AM

I will, life is just kinda crazy right now. Might be a while before the next one.
  • 0

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


#8 se7en

se7en

    CC Regular

  • Member
  • PipPipPip
  • 32 posts

Posted 02 October 2010 - 09:08 PM

ok, when every you can, thanks anyway!
  • 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