ASM-8086/88


A simple introduction


July 18, 2024 - Tommy Dräger


Introduction

"If one is to understand the great mystery, one most study all its aspects!" were the words of darth sidious right before revealing himself to his new apprentince darth vader in star wars. We could use the same quote when it comes to assembly! These days, many claim to be programmers with their Vue, React, and Angular framework experience. But to truly grasp the essence of programming, diving into assembly is essential.

The argument that spoke for asm in the past was always speed and performance, as the code needed to be compact and as native as possible. With modern compilers and ultra fast hardware that narative seems to slowly fade out. However, there are real compelling reasons to master assembly.

Assembly is crucial for reverse engineering, analysis and research. With the rise of tools like Ghidra, being able to break apart software gives you a significant edge over other engineers. Also, assembly skills make you far superior in understanding the inner workings of technology in its full possibilities. And Interestingly, AI really struggles with assembly for some reason, which means human expertise in this area is even more valuable.

If you found this article the chance is high that you probably searched for ASM8086/88, and you already know the basics. In the beginning I planned to make this article as beginner friendly as possible, but I gave up after the first paragraph already exceeded 2 Pages.

These both Pdfs and a little bit of concentration are all you need in order to dive right in:

https://www.phatcode.net/res/226/files/pcasm-book.pdf https://www.phatcode.net/res/237/files/intelcodetable.pdf

Setup the Environment

I prepared a repository for you to follow along. This repository contains MASM, LINK and AFDEBUG!

git clone [email protected]:MilesTails01/i8086_boilercode.git

after downloading you need to unpack DOSBOX.zip and TOOLS.zip

cd \bin
tar -xf TOOLS.zip
tar -xf DOSBOX.zip

Usage

build.bat   // Compile, Link and Start the source
debug.bat   // Starts AFDEBUG and loads the compiled exe
start.bat   // Start the program without rebuilding it

Boilercode

;   #############################
;   #           MAIN.ASM        #
;   #############################

;   #############################
;   #           STACK           #
;   #############################
STACK SEGMENT PARA STACK
    db 64 dup(0)
STACK ENDS

;   #############################
;   #           DATA            #
;   #############################
DATA SEGMENT PARA 'DATA'
    string db 'Hello World','$'
    include \src\utils.inc
DATA ENDS

;   #############################
;   #           CODE            #
;   #############################
CODE SEGMENT PARA 'CODE'
    ASSUME cs:CODE, ds:DATA, ss:STACK

startup:
    mov ax, DATA
    mov ds, ax
    mov ax, STACK
    mov ss, ax

    lea dx, string
    call PRINT

    mov ah, 4Ch
    int 21h

CODE ENDS
END startup

The Project consists of 3 parts

  • STACK SEGMENT PARA STACK (I initialized it with 64 Bytes)
  • DATA SEGMENT PARA 'DATA' (This is where all your variables as well as includes are going)
  • CODE SEGMENT PARA 'CODE' (your startup point)

Include

PRINT PROC FAR

    mov ah, 09h
    int 21h
    ret

PRINT ENDP

People mostly claim that asm is super messy and chaotic when it comes to file structure cause one have to write spaghetti code, which is not true! You can create a project structure like you would with Java, C etc. by creating *.inc files. Only make sure that the defined procedures are using FAR since they are not on the same code segment.

PRINT PROC FAR

    mov ah, 09h
    int 21h
    ret

PRINT ENDP

This function is using the DOS interupt 21h in order to print. Make sure that DS:DX is pointing to the string you want print:

                    ; ds is already pointing at the correct segment
lea dx, string      ; loads the relative offset of "string" variable into dx
call PRINT          ; variable at DS:DX will get printed

https://www.i8086.de/dos-int-21h/dos-int-21h.html https://www.i8086.de/dos-int-21h/funktion-09.html

Initialization

CODE SEGMENT PARA 'CODE'
    ASSUME cs:CODE, ds:DATA, ss:STACK

startup:
    mov ax, DATA
    mov ds, ax
    mov ax, STACK
    mov ss, ax

    lea dx, string
    call PRINT

    mov ah, 4Ch
    int 21h

CODE ENDS

So let's break down what is happening here:

ASSUME cs:CODE, ds:DATA, ss:STACK

The ASSUME directive tells the assembler how to interpret segment names when generating code. It does not perform any runtime action. Essentially, it helps the assembler understand which segments to use for instructions that involve segment registers.

This tells the assembler that:

  • cs (Code Segment register) will use the CODE segment.
  • ds (Data Segment register) will use the DATA segment.
  • ss (Stack Segment register) will use the STACK segment.

However, this does not set the values in the segment registers. It only provides the assembler with information about how to generate the correct code.

mov ax, DATA    ; Load the address of the DATA segment into the ax register.
mov ds, ax      ; Move the address from ax to the ds (Data Segment register).
mov ax, STACK   ; Load the address of the STACK segment into the ax register.
mov ss, ax      ; Move the address from ax to the ss (Stack Segment register).

The reason you don't need to set the cs (Code Segment) register with mov instructions is that the cs register is automatically set by the CPU when your program starts. From here your program is ready to be extended with your own procedures, variables, etc.

simply type build.bat, which will start DOSBOX which will then mount the project folder, compile the main.asm, link it and start it. You should a screen like this (a simple Hello World):

Let me show show you how the AFDEBUG is working: type debug.bat. You should see a window like that:

Use F7, F8, F9 and F10 to switch between the Windows! The Top window is showing your Registers like AX, BX, CX etc. and which value they are currently holding. On the right Side you the Flags like ZF (Zero Flag), CF (Carry Flag) and so on.

In the center right and bottom left you have Hexcode View of your RAM at DS:DI (1A07:0000). On the bottom right you find the very same RAM Section but in ASCII representation. I want you to navigate to the bottom left window by pressing F10 and then F8. Now use the arrow until you find our "string" variable that should contain the word "Hello World".

In order to step trough the program press the F1 Key. Watch how the values of the registers will change as you step trough the program. If you need further help switch to the CMD window and Press F4 for help (you can switch the help pages with PgUp and PgDown Keys).

Conclusion

In this article we setup our foundation for the upcoming project. In the next article we will try to use the VGA mode, fill the screen with a rainbow and make sure that no flickering will occur during the render process.