Lab #1: Class Tools and the 8086 Architecture
This lab is to be completed individually; you must work alone. This lab is an introduction to the 8086 architecture and the tools that will be used to complete the remaining labs this semester. In this lab, use the tools to compile, assemble, and execute a simple program written in C and assembly language. Then you will write a function in assembly language that is called from the C source code. You will learn about the tools, the 8086 instruction set, and the C function calling convention.
Start by familiarizing yourself with the topics of this lab by doing the following reading:
All of the above documents can also be found on the 425 Online Resources page for future
Once you feel comfortable with the build process, follow the instructions below to
build the program for this lab.
- Read thoroughly the document 8086 Assembly Language
- Read thoroughly the document NASM Syntax
- Review the document 8086 Instruction Set
- Read the section on near calls in the document C
Calling Convention (the Quick Reference section may also be useful). The term C calling
convention refers to how a C compiler generates functions in assembly language,
including how function calls are made, how function arguments are passed to functions, and how
values are returned. This document also discusses how to access local variables in assembly language.
- Read thoroughly the document Building Programs with the Tools
The tools for this class run on the department's Linux machines in
425 CB. (If you want to explore the possibility of running the tools
on another platform, the source code is available at
/ee2/ee425/src/dist. /ee2 is a drive mounted on linux machines in the
lab. You are on your own getting the code to compile and run on your
own machine.) Use one of the available machines to perform the
- Create a directory to contain all your work for this class. You must
maintain access privileges appropriately so that only you have access to the files and directories it contains. Once this is done, create a subdirectory to contain the files for this lab.
- Add /ee2/ee425/bin to your path. This is necessary to run the
compiler and assembler. See setting
your path for more details.
- Copy the class library file clib.s
and its C header file clib.h to your
lab directory. These files are needed for all programs you build to run in
- Copy the assembly file lab1asm.s and
the C file lab1.c to your lab directory.
- Use cpp to preprocess lab1.c, the C file for this lab, by changing to your lab
directory and typing "cpp lab1.c lab1.i".
This will produce a version of lab1.c named lab1.i
that is ready to be compiled.
- Compile lab1.i using
c86 by typing "c86 -g lab1.i lab1.s". This will produce
an assembly language file named
- Concatenate your files together by typing "cat clib.s lab1asm.s lab1.s >
lab1final.s". The file clib.s contains output functions
used by lab1.c and must be included as the first file when you concatenate
your assembly files together.
- Assemble the final assembly file using NASM to produce an 8086 executable named lab1.bin. To assemble the file,
type "nasm lab1final.s -o lab1.bin
-l lab1.lst". This also creates a listing file named lab1.lst.
- Start the instruction level emulator for the
8086, by typing "emu86". Notice that the simulator
uses two separate windows: one for the output of the program executing
on the simulator and another for interacting with the simulator
- Load the executable into Emu86 by typing "l lab1.bin"
at the "Emu86>" prompt.
- Execute the program by typing "e". The program's output should
appear in the output window. It should output the following text:
Result 1 is: 0
Result 2 is: 0
Result 3 is: 0
- Quit the simulator by entering "q".
You now know how to build programs to run on Emu86. In
future labs you will use a Makefile to make this process as simple as typing
"make". Now your task is to modify the file lab1asm.s to change its
functionality. To do this you will need to have a basic understanding of the
8086 instruction set as well as the C calling convention on the 8086.
The file lab1asm.s contains the assembly routine "AsmFunction". This function is called a few times from the file lab1.c with different
function arguments based on the prototype:
int AsmFunction(int a, char b, char c, int d, int e);
Your assignment is to edit lab1asm.s and modify the assembly routine
AsmFunction so that it returns the result of the calculation
where a, b, c, d, and e are the arguments passed to the function and gvar
is a global variable declared in lab1.c. You may look at the source code for lab1.c but you are not allowed to modify it in any way.
After lab1asm.s has been modified to include your
assembly function contents, you must again go through the steps of
concatenation and assembly by entering "cat clib.s lab1asm.s lab1.s
> lab1final.s" followed by "nasm lab1final.s -o
lab1.bin -l lab1.lst". Then run the simulator as explained
previously. When AsmFunction is correct, your program's output should look like this:
Result 1 is: 16
Result 2 is: 111
Result 3 is: -34
Please consider the TA lab schedule well in
advance. New for Fall 2019: after passing off to the TA, upload
your lab1asm.s file on Learning Suite. (We need to collect examples of
student work for our department's ABET review next fall, but TAs may also review
your submission to double check that all requirements were met.)
- You must complete all the reading described in the Reading section.
- Implement your AsmFunction() in 20
instructions or less. This does not include labels, assembly directives,
- Your function must use the push/pop instructions to save any registers it uses
and restore them to their original values before it returns (You may not use the pusha/popa
instructions). Note that this does not apply
to the register being used for the return value.
- When you have completed your function and the output of your program is correct,
demonstrate your program and show your assembly code to a TA.
Tips and Hints
- Whether or not it is necessary to save and restore registers you
use when writing in assembly depends on the registers in question
and the convention used by the compiler. For this class we will
assume the callee save convention (as opposed to caller
save), which means that you must save any registers you use at
the beginning of a function (using
push instructions) and restore them at the end (using
pop instructions), unless the register is being used to return a value.
This will ensure that you don't overwrite a register the compiler expects to be unchanged
after the function call.
- The global variable gvar can be accessed from
the assembly file lab1.s without any new declarations using the
- Make sure you take into account the fact that the b and c
arguments passed to AsmFunction are of type char (a signed
byte), not int like the other arguments. Byte values must be properly
converted to words before being used in arithmetic with other word sized
- Start your function by setting up your stack frame and saving any registers you intend
to use. At the end of the function, restore all registers that were saved and undo the stack frame.
Remember, however, that you should not save or restore any register being used for the return value.
- Make sure you understand how to access the function arguments on the stack. Refer
to the C Calling Convention page as needed.
- Don't forget to specify the size of memory operands where needed.
- You can use the 's' simulator command to "step" through or execute the
program one instruction at a time and you can use the 'r' command to examine the
8086 register contents. This will allow you to see exactly what your code is
doing and will help you track down errors in your function. Use
the 'o' command to step "over" the calls to printString(), print(),
printInt(), and printNewLine() since you do not need
to concern yourself with their contents. Refer to the Emu86 Simulator webpage
or the '?' command for more information on commands. You will learn more about these commands in the
- You may want to look at the file lab1.s or lab1.lst
to see the code that is executing when your run the program. This will make it more clear what
part of your program is being executed as you step
through the code.
- The function should take about 20 instructions, but can be written in as few as 15, assuming you
properly save and restore your registers. If you take the time to look at the capabilities of the different
instructions and understand how the function arguments can be accessed
on the stack then writing the function should not be too difficult.
Last updated 4 September 2019