Microprocessor Lab Manual

• • • • •

Where to start? Working with The Editor How to Compile The Code Working with The Emulator Assembly Language
o o o o o o o o o o o o o

Numbering Systems What is assembly language? Memory access Variables Interrupts Library of common functions - emu8086.inc Arithmetic and logic instructions Program flow control Procedures Stacks Macros Making your own operating system Controlling external devices (robot, stepper-motor...)

Where to start?
1. Click code examples and select Hello, world. A code example with many comments should open. All comments are green
and they take up about 90% of all text, so don't be scared by this tiny "Hello Word" code. The compiled executable is only about 100 bytes long, because it uses no interrupts and has only one loop for color highlighting the text. All other code is straight-forward and writes directly to video memory. executable to c:\emu8086\MyBuild. If the assembler succeeds in creating the file, the emulator will also automatically load it into memory.

2. To run this example in the emulator, click emulate (or press F5). The program then attmepts to assemble and save the

3. You can then click single step (or press F8) to step through the code one instruction at a time, observing changes in
registers and the emulator screen. You can also click step back (or press F6) to see what happens when reversing those changes.

4. There are many ways to print "Hello,World" in assembly language, and this certainly isn't the shortest way. If you click
examples and browse c:\emu8086\examples, you'll find HelloWorld.asm which assembles into only a 30-byte executable. Unlike the previous example which carries out each step by itself, this one is much smaller because it uses a built-in interrupt function of the operating system to write to the display.

The integrated 8086 assembler can generate console programs that can be executed on any computer that runs x86 machine code (Intel/AMD architecture) The architecture of the 8086 Intel microprocessor is called "Von Neumann architecture" after the mathematician who conceived of the design. NOTE: A CPU can interpret the contents of memory as either instructions or data; there's no difference in the individual bytes of memory, only the way in which they're arranged. Because of this, it's even possible for programs to re-write their own instructions, then execute the instructions they've changed.

Source Code Editor
Using the Mouse Editor supports the following mouse actions: Mouse Action L-Button click over text R-Button click L-Button down over selection, and drag Ctrl + L-Button down over selection, and drag L-Button click over left margin L-Button click over left margin, and drag Alt + L-Button down, and drag L-Button double click over text Spin IntelliMouse mouse wheel Single click IntelliMouse mouse wheel Double click IntelliMouse mouse wheel Click and drag splitter bar Double click splitter bar Result Changes the caret position Displays the right click menu Moves text Copies text Selects line Selects multiple lines Select columns of text Select word under cursor Scroll the window vertically Select the word under the cursor Select the line under the cursor Split the window into multiple views or adjust the current splitter position Split the window in half into multiple views or unsplit the window if already split

Editor Hot Keys:
Command Keystroke ======================================================================= Toggle Bookmark Control + F2 Next Bookmark F2 Prev Bookmark Shift + F2 Copy Cut Cut Line Cut Sentence Paste Undo Document Document Document Document Control + C, Control + Insert Control + X, Shift + Delete, Control + Alt + W Control + Y Control + Alt + K Control + V, Shift + Insert Control + Z, Alt + Backspace End Control + End End Extend Control + Shift + End Start Control + Home Start Extend Control + Shift + Home

Find Control + F, Alt + F3 Find Next F3 Find Next Word Control + F3 Find Prev Shift + F3 Find Prev Word Control + Shift + F3 Find and Replace Control + H, Control + Alt + F3 Go To Line Control + G Go To Match Brace Control + ] Select All Control + A Select Line Control + Alt + F8 Select Swap Anchor Control + Shift + X Insert New Line Above Control + Shift + N Indent Selection Outdent Selection Tabify Selection Untabify Selection Lowercase Selection Uppercase Selection Tab Shift + Tab Control + Shift + T Control + Shift + Space Control + L Control + U, Control + Shift + U

[a-c]). + (for one or more ot something). * (for zero or more of something). logical OR: subexpressions may be ORed together .g.Left Word Right Word Left Sentence Right Sentence Control + Left Control + Right Control + Alt + Left Control + Alt + Right Insert Control + Alt + T Toggle Overtype Display Whitespace Scroll Scroll Scroll Scroll Window Window Window Window Up Control + Down Down Control + Up Left Control + PageUp Right Control + PageDown Control + Delete Control + Backspace Delete Word To End Delete Word To Start Extend Extend Extend Extend Extend Extend Extend Extend Extend Extend Char Left Shift + Left Char Right Shift + Right Left Word Control + Shift + Left Right Word Control + Shift + Right to Line Start Shift + Home to Line End Shift + End Line Up Shift + Up Line Down Shift + Down Page Up Shift + PgUp Page Down Shift + Next Ctrl + Q Ctrl + W Comment Block Uncomment Block regular expression syntax rules for search and replace wildcards: ? (for any character). sets of characters: characters enclosed in square brackets will be treated as an option set.(e. character ranges may be specified with a .

with the | pipe symbol. escape characters: sequences such as: \t . . \\ represents the backslash.ocx" from program's folder into Windows\System or Windows\System32 replacing any existing version of that file (restart may be required before system allows to replace existing file). will be substituted for an equivalent single character.tab etc. If there are problems with the source editor you may need to manually copy "cmax20. rarenthesized subexpressions: a regular expression may be enclosed within parentheses and will be treated as a unit.

after successful compilation you can click emulate button to load the compiled file in emulator. the output file type directives: #make_com# #make_bin# #make_boot# #make_exe# you can insert these directives in the source code to specify the required output type for the file. . you will be asked for a place where to save the compiled file. and click compile button.Compiling the assembly code type your code inside the text area. only if compiler cannot determine the output type automatically and it when it cannot find any of these directives it may ask you for output type before creating the file.

segment and offset for memory area where this file will be loaded. stack segment should be defined in the program. current register values are used and "MY. but . such files are loaded with 100h prefix (256 bytes).com and . When loading "MY.BIN" file is loaded at current CS:IP.BINF" file. • #make_exe# .there is virtually no difference between how . registers are also set using information in that file (open this file in a text editor to edit or investigate). and code segments. This file type is selected automatically if org 100h directive is found in the code.BIN" file to emulator it will look for a "MY.the oldest and the simplest format of an executable file.BINF file is created automatically if assembler finds any of the following directives.a simple executable file.BINF" file. the execution starts from values in CS:IP. the entry point (where execution starts) is defined by a programmer.BIN" file to location specified in "MY. not limited by size and number of segments. Execution always starts from the first byte of the file. • #make_bin# . . however the directives are unique and will not work if .BINF" file. in case the emulator is not able to find "MY.bin are assembled because these files are raw binary files. bin file type is not unique to the emulator. Select Clean from the New menu if you plan to compile a COM file. description of output file types: • #make_com# .exe file has a special header in the beginning of the file that is used by the operating system to determine some properties of the executable file. stack. Compiler directive ORG 100h should be added before the code.more advanced format of an executable file. You can define the values of all registers. this file type is selected automatically if stack segment is found.bin file is executed outside of the emulator because their output is stored in a separate file independently from pure binary code. and load "MY. . supported by dos and windows command prompt. you may select exe template from the new menu in to create a simple exe program with pre-defined data. supported by DOS and Windows Command Prompt.

when not specified these values are set by default: LOAD_SEGMENT = 0100 LOAD_OFFSET = 0000 CS = ES = SS = DS = 0100 IP = 0000 if LOAD_SEGMENT and LOAD_OFFSET are not defined.. then CS and IP values are used and vice-versa.1233# ." directive can be used to write values to memory before program starts #MEM=nnnn.[bytestring]-nnnn:nnnn.these directives can be inserted into any part of the source code to preset registers or memory before starting the program's execution: #make_bin# #LOAD_SEGMENT=1234# #LOAD_OFFSET=0000# #AL=12# #AH=34# #BH=00# #BL=00# #CH=00# #CL=00# #DH=00# #DL=00# #DS=0000# #ES=0000# #SI=0000# #DI=0000# #BP=0000# #CS=1234# #IP=0000# #SS=0000# #SP=0000# #MEM=0100:FFFE.01ABCDEF0122-0200.00FF-0100:FF00.. "#mem=.F4# • all values must be in hexadecimal.[bytestring]# for example: #MEM=1000.

Comments are added just to make some order. DS . BH .for physical address. . DI . BP . SI . • • the format of a typical ". load to offset. BL . CS .BINF" file: 8000 0000 55 66 77 88 99 AA BB CC DDEE ABCD EF12 3456 7890 8000 0000 C123 D123 . DH .binf file it will use c:\emu8086\default. this also applies to any other files with extensions that are unfamiliar to the emulator. . DL . if none of the above directives directives are preset in source code. so line order is very important. when emulator loads .binf file is automatically overwritten on re-compile. load to segment. CL . for example: 0A. CH . 12 or 00. note: all values are in hex. spaces are allowed inside. or (nnnn:nnnn) for logical address. . SS .bin file without .separates the entries. when emulator loads a BINF file it does not care about comments it just looks for a values on specific lines.all values are in hex. IP . binf file is not created. • . ES . hexadecimal suffix/prefix is not required.binf instead. AH . for each byte there must be exactly 2 characters. SP • we can observe that first goes a number in hexadecimal form and then a comment. AL . NOTE: existing . nnnn .

• In case load to offset value is not zero (0000). you may write the same files to real floppy and boot your computer from it. error processing assembly language compiler (or assembler) reports about errors in a separate information window: . compiler directive org 7c00h should be added before the code.asm" and "microos_kernel. then select [virtual drive] -> [boot from floppy] menu to boot emulator from a virtual floppy. the only difference from #make_bin# is that loading segment is predefined to 0000:7c00h (this value is written to accompanied . however to make correct test in emulator you will need to add these directives: #cs=0# and #ip=7c00# .BIN file where ????h is the loading offset. in fact you can use #make_bin# without any lack of performance.bin file and load it in emulator (see "micro-os_loader. if you are curious.binf file).assembler writes these values into . • #make_boot# . when computer starts it loads first track of a floppy disk at the address 0000:7c00. ORG ????h should be added to the source of a . the size of a boot record file should be less then 512 bytes (limited by the size of a disk sector). you can use "writebin.binf file. then.asm" from c:\emu8086\examples\ micro-operating system does not have ms-dos/windows compatible boot sector. this should be done to allow compiler calculate correct addresses.asm" in c:\emu8086\examples for more information). this file type is unique to emu8086 emulator. execution always starts from the first byte of the file. You can write a boot sector of a virtual floppy (FLOPPY_0) via menu in emulator: [virtual drive] -> [write 512 bytes at 7c00 to boot sector] first you should compile a . so it's better to use an empty floppy disk. refer to tutorial 11 for more information.this type is a copy of the first track of a floppy disk (boot sector).

general purpose register should be used. for example MOV AX. 300 . . and thus maximum value for it is 255 (or 11111111b).MOV DS. 100 . and the minimum is -128. 100 MOV DS.is illegal instruction because AL register has only 8 bits. AX MOV AL.is illegal instruction because segment registers cannot be set directly.

so you may view it in any text editor (including emu8086 source editor). • • .symbol . (created only if an executable is a BIN file). *. 1 instructions). *. and set register values prior execution.~asm . • • *. *. no segment and no variable declarations.When saving an assembled file.this file has information that enables the emulator select lines of original source code while running the machine code. this is done mainly for the compatibility with original 8086 microprocessor (for example ROL AL. but everything is converted to pure machine code.this file contains the original source code that was used to make an executable file. 5 is assembled into five sequential ROL AL. It is a plain text file.this ASCII file contains information that is used by emulator to load BIN file at specified location. Compiler directives produce no binary code. compiler also saves 2 other files that are later used by the emulator to show original source code when you run the binary executable. Sometimes a single original instruction is assembled into several machine code instructions.binf . Very often the original code differs from the disabled code because there are no comments. and select corresponding lines. it contains information that enables to show the "variables" window.symbol table.debug .

Using the emulator If you want to load your code into the emulator. compile it and then load into the emulator: . If there are no files in "MyBuild" folder return to source editor. Select Show emulator from the Emulator menu. load any sample. Try loading files from "MyBuild" folder. just click "Emulate" button . select Examples from File menu. But you can also use emulator to load executables even if you don't have the original source code.

Double click on register text-boxes opens "Extended Viewer" window with value of that register converted to all possible forms.[Single Step] button executes instructions one by one stopping after each instruction. [Run] button executes instructions one by one with delay set by step delay between instructions. Less significant byte is at lower address: LOW BYTE is loaded from selected position and HIGH BYTE from next . You can modify the value of the register directly in this window. Double click on memory list item opens "Extended Viewer" with WORD value loaded from memory list at selected location.

3)! To delete a floppy drive you should close the emulator. Emulator starts counting attached floppy drives from starting from the first. 01 two floppy disks. To add more floppy drives select [Create new floppy drive] from [Virtual drive] menu. check out operating system tutorial. You can modify the value of the memory word directly in the "Extended Viewer" window. You can determine the number of attached floppy drives using INT 11h this function returns AX register with BIOS equipment list. delete the required file manually and restart the emulator. To write and read from floppy drive you can use INT 13h function. and FLOPPY_3 files. You can modify the values of registers on runtime by typing over the existing values. emulator can emulate tiny operating system.560 bytes). [Flags] button allows you to view and modify flags on runtime. Only 4 floppy drives are supported (0. see list of supported interrupts for more information. 11 four floppy disks.. 10 three floppy disks. . Each time you add a floppy drive emulator creates a FLOPPY_1. in case file FLOPPY_1 does not exist it stops the check and ignores FLOPPY_2 and FLOPPY_3 files. Created floppy disks are images of empty IBM/MS-DOS formatted disk images.memory address. By default there is a FLOPPY_0 file that is an image of a real floppy disk (the size of that file is exactly 1. Virtual drives Emulator supports up to 4 virtual floppy drives.474. Bits 7 and 6 define the number of floppy disk drives (minus 1): Bits 7-6 of AX: 00 single floppy disk. FLOPPY_2.

Numbering Systems Tutorial What is it? There are many ways to represent the same numeric value. but a V now meant five sticks. 2. In the decimal system there are 10 digits: 0. 6. Using sticks to count was a great idea for its time. the number 5 was first represented as: | | | | | (for five sticks). 8. 3. So. 5. Later on. for example: 754. Decimal System Most people today use decimal representation to count. | | still meant three sticks. 9 These digits can represent any value. 7. humans used sticks to count. the Romans began using different symbols for multiple numbers of sticks: | and an X was used to represent ten of them. and later learned how to draw pictures of sticks in the ground and eventually on paper. multiplied by the base (in this case it is 10 because there are 10 digits in decimal system) in power of digit position (counting from zero): Position of each digit is very important! for example if you place "7" to the end: 547 it will be another value: . Long ago. 1. And using symbols instead of real sticks was much better. 4. The value is formed by the sum of each digit.

1 And thus the base is 2. two bytes form a WORD. Each digit in a binary number is called a BIT. binary system uses 2 digits: 0. 4 bits form a NIBBLE. two words form a DOUBLE WORD (rarely used): There is a convention to add "b" in the end of a binary number. The binary number 10100101b equals to decimal value of 165: .Important note: any number in power of zero is 1. this way we can determine that 101b is a binary number with decimal value of 5. 8 bits form a BYTE. even zero in power of zero is 1: Binary System Computers are not as smart as humans are (or not yet). or 1 and 0. it's easy to make an electronic machine with two states: on and off. Computers use binary system.

5. D.. 9. The hexadecimal number 1234h is equal to decimal value of 4660: . It is very easy to convert numbers from binary system to hexadecimal system and vice-versa. We also add "0" (zero) in the beginning of hexadecimal numbers that begin with a letter (A. for example 0E120h. C.F). 8. 6. B.Hexadecimal System Hexadecimal System uses 16 digits: 0. A. 3. E. 4. this way we can determine that 5Fh is a hexadecimal number with decimal value of 95. 7. every nibble (4 bits) can be converted to a hexadecimal digit using this table: Decimal Binary Hexadecimal (base 10) (base 2) (base 16) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 0 1 2 3 4 5 6 7 8 9 A B C D E F There is a convention to add "h" in the end of a hexadecimal number. 1. F And thus the base is 16. 2. Hexadecimal numbers are compact and easy to read.

All remainders were below 10 in the above example. each time you should remember the result and keep the remainder. The remainders are then used to represent a value in that system. so we do not use any letters. Here is another more complex example: let's convert decimal number 43868 to hexadecimal form: . the divide process continues until the result is zero.Converting from Decimal System to Any Other In order to convert from decimal system. to any other system. it is required to divide the decimal value by the base of the desired system. Let's convert the value of 39 (base 10) to Hexadecimal System (base 16): As you see we got this hexadecimal number: 27h.

and then convert it to binary number using the above table: As you see we got this binary number: 1010101101011100b Signed Numbers There is no way to say for sure whether the hexadecimal byte 0FFh is positive or negative. or convert to hexadecimal number. we are using the above table to convert remainders over 9 to corresponding letters. so it we'll get: 256 . Using the same principle we can convert to binary form (using 2 as the divider).The result is 0AB5Ch. Using this complex way to represent negative numbers has some meaning.5".5" to "5" you should get zero.. the result gets over 255. in math when you add ".1".5 = 251. because of the overflow processor gets zero! . 8 bits can be used to create 256 combinations (including zero).127) will represent positive numbers and next 128 combinations (128.. In order to get ". This is what happens when processor adds two bytes 5 and 251.256) will represent negative numbers. it can represent both decimal value "255" and ". so we simply presume that first 128 combinations (0. we should subtract 5 from the number of combinations (256).

so this maybe used to determine the sign of a number... first 32768 combinations (0. all you need is a click on Math menu: . The same principle is used for words (16 bit values).When combinations 128.32767) are used to represent positive numbers.. There are some handy tools in emu8086 to convert numbers. and make calculations of any numerical expressions.65535) represent negative numbers. and next 32768 combinations (32767.256 are used the high bit is always 1. 16 bits create 65536 combinations.

When Signed is checked evaluator assumes that all values (except decimal and double words) should be treated as signed. Double words are always treated as signed values. and do the calculation with decimal values (if it's easier for you). For example you want to calculate: 0FFFFh * 10h + 0FFFFh (maximum memory location that can be accessed by 8086 CPU). Just type a value in any text-box. If you check Signed and Word you will get -17 (because it is evaluated as (-1) * 16 + (-1) . so 0FFFFFFFFh is converted to -1. To make calculation with unsigned values uncheck Signed so that the evaluation will be 65535 * 16 + 65535 and you should get 1114095. You can also use the base converter to convert non-decimal digits to signed decimal values.Base converter allows you to convert numbers from any system and to any system. Type an expression and press enter. Multi base calculator can be used to make calculations between numbers in different systems and convert numbers from one system to another. These operation are supported: . result will appear in chosen numbering system. and the value will be automatically converted to all other systems. You can work both with 8 bit and 16 bit values. You can work with values up to 32 bits.

subtract (and unary -). % modulus. & bitwise AND. / divide. << shift left. ^ bitwise XOR.F). * multiply. example: 0ABCDh Octal (base 8) numbers must have "o" suffix. example: 77o .. | bitwise OR.~ not (inverts all bits). >> shift right. + sum. and start with a zero when first digit is a letter (A. example: 00011011b Hexadecimal numbers must have "h" suffix. Binary numbers must have "b" suffix.

RAM is a place to where the programs are loaded in order to be executed. The CPU is the heart of the computer. most of computations occur inside the CPU. you need to get some knowledge about computer structure in order to understand anything. the simple computer model as i see it: the system bus (shown in yellow) connects the various components of a computer. Inside the CPU .What is assembly language? Assembly language is a low level programming language.

DX) are made of two separate 8 bit registers. it's something like: 0011000000111001b (in binary form). accessing data in a register usually takes no time.stack pointer. CX. but they are still an excellent place to store temporary data of calculations. since this way we can access much more memory than with a single register that is limited to 16 bit values. BX . this is never a good idea. because registers are located inside the cpu. DS . 4 general purpose registers (AX. they are much faster than memory.the data register (divided into DH / DL).general purpose registers 8086 CPU has 8 general purpose registers. DX .generally points at segment where variables are defined. when you modify any of the 8 bit registers 16 bit register is also updated. the size of the above registers is 16 bit.the accumulator register (divided into AH / AL).the base address register (divided into BH / BL). it's up to a coder to define its usage. despite the name of a register. it's the programmer who determines the usage for each general purpose register. the same is for other 3 registers. BX. you should try to keep variables in the registers. CPU makes a calculation of physical address by multiplying the segment register by 10h and adding general purpose register to it . SP . segment registers work together with general purpose register to access any memory value. DI .destination index register. register sets are very small and most registers have special purposes which limit their use as variables. accessing a memory location requires the use of a system bus. each register has its own name: • • • • • • • • AX . so it takes much longer.source index register. SI . we should set the DS = 1230h and SI = 0045h. BP . the segment registers have a very special purpose .pointing at accessible blocks of memory. and vice-versa. therefore. then AH=00110000b and AL=00111001b. the main purpose of a register is to keep a number (variable). "H" is for high and "L" is for low part.points at the segment containing the current program. or 12345 in decimal (human) form. For example if we would like to access memory at the physical address 12345h (hexadecimal).points at the segment containing the stack. for example if AX= 0011000000111001b.the count register (divided into CH / CL). although it is possible to store any data in the segment registers. ES .extra segment register. SS .base pointer. segment registers • • • • CS . This is good. CX . therefore.

by default BX. flags register is modified automatically by CPU after mathematical operations. the way you can access AX and other general registers. generally you cannot access these registers directly. Other general purpose registers cannot form an effective address! also. flags register .(1230h * 10h + 45h = 12345h): the address formed with 2 registers is called an effective address. IP register always works together with CS segment register and it points to currently executing instruction. and to determine conditions to transfer control to other parts of the program. but it is possible to change values of system registers using some tricks that you will learn a little bit later. . special purpose registers • • IP .the instruction pointer. although BX can form an effective address. SI and DI registers work with DS segment register. BH and BL cannot. BP and SP work with SS segment register. this allows to determine the type of the result.determines the current state of the microprocessor.

. -1. BP. for example. displacement can be a immediate value or offset of a variable. etc. generally the compiler takes care about difference between d8 and d16.).. and generates the required machine code.. there is an easy way to remember all those possible combinations using this chart: .) d16 .stays for 16 bit signed immediate displacement (for example: 300. these combinations are supported (addressing modes): [BX + SI] [BX + DI] [BP + SI] [BP + DI] [SI + d8] [DI + d8] [BP + d8] [BX + d8] [SI] [DI] d16 (variable offset only) [BX] [BX + SI + d16] [BX + DI + d16] [BP + SI + d16] [BP + DI + d16] [BX + SI + d8] [BX + DI + d8] [BP + SI + d8] [BP + DI + d8] [SI + d16] [DI + d16] [BP + d16] [BX + d16] d8 . let's assume that DS = 100. displacement is a signed value.Memory Access to access memory we can use these four registers: BX. BX = 30.. The following addressing mode: [BX + SI] + 25 is calculated by processor to this physical address: 100 * 16 + 30 + 70 + 25 = 1725. if there are several values. for these SS segment register is used. displacement can be inside or outside of the [ ] symbols. by default DS segment register is used for all modes except those with BP register. 5517h. or even both. etc. so it can be both positive or negative. -259. assembler evaluates all values and calculates a single immediate value. SI. assembler generates the same machine code for both ways.stays for 8 bit signed immediate displacement (for example: 22. 55h.. combining these registers inside [ ] symbols. DI. we can get different memory locations. SI = 70.

The physical address will be 1234h * 10h + 7890h = 19BD0h. and the value in purpose register (BX.for byte ptr w. byte access. here are an examples of a valid addressing modes: [BX+5] . [BX+SI] . SI and DI also don't go together. [DI+BX-4] the value in segment register (CS. these prefixes should be used: byte ptr . assembler supports shorter prefixes as well: b. however 10h = 16. . word ptr . it is multiplied by 16. so if zero is added to a hexadecimal value.for byte. SI. . . word access. . When DS contains value 1234h and SI contains the value 7890h it can be also recorded as 1234:7890.for word (two bytes). for example: byte ptr [BX] or word ptr [BX] . BP) is called an offset.for word ptr in certain cases the assembler can calculate the data type automatically. ES) is called a segment. if zero is added to a decimal number it is multiplied by 10. SS. as you see BX and BP never go together. DI. DS. for example: 7h = 7 70h = 112 in order to say the compiler about data type.you can form all valid combinations by taking only one item from each column or skipping the column by not taking anything from it.

[BX+SI+7]. immediate: 5.. DH. . etc.. AL.. 10001101b. SP. SP. memory MOV memory. CH. REG MOV REG. for segment registers only these types of MOV are supported: MOV SREG. which can be a byte or a word. DL. BP. memory: [BX]. memory MOV memory. the source operand can be an immediate value. REG SREG: DS. variable. SREG MOV REG. ES. BH. REG MOV memory. these types of operands are supported: MOV REG. BL. the destination register can be a general-purpose register. BX. -24. both operands must be the same size. immediate REG: AX. CX. DI. immediate MOV REG. DX.MOV instruction • • • • copies the second operand (source) to the first operand (destination). CH. the MOV instruction cannot be used to set the value of the CS and IP registers. BP. BL. [BX+SI+7]. BH. CL. variable. or memory location.. 3Fh. DI. etc. DX. DH. SI. memory: [BX]. BX. SS. SI. AH. CX. and only as second operand: CS. REG: AX. SREG MOV SREG. DL.. general-purpose register or memory location.. AL. AH. etc. CL.

anything after ". 'A' . go to the source editor and press Ctrl + V combination to paste. click before the text and drag it down until everything is selected.com program. click [Single Step] button and watch the register values. set BX to 15Eh. 0B800h . select the above text using mouse. 1101_1111b . MOV CL. the emulator window should open with this program loaded. copy contents of CX to memory at B800:015E RET .here is a short program that demonstrates the use of MOV instruction: ORG 100h . set AX to hexadecimal value of B800h. MOV [BX]. set CH to binary value." symbol is ignored by compiler. MOV DS. you can copy & paste the above program to the code editor. copy value of AX to DS. this directive required for a simple 1 segment ." is used for comments. 2. press Ctrl + C combination to copy. 15Eh . MOV BX. how to do copy & paste: 1. AX . as you may guess. 3. MOV AX. you should see something like that when program finishes: . MOV CH. ". and press [Compile and Emulate] button (or press F5 key on your keyboard). set CL to ASCII code of 'A'. returns to operating system. CX . it is 41h.

. so you may see that MOV is a very powerful instruction.actually the above program writes directly to video memory.

Our compiler supports two types of variables: BYTE and WORD. binary. var1 MOV BX. As you probably know from part 2 of this tutorial. though it should start with a letter. Let's see another example with MOV instruction: ORG 100h MOV AL. or decimal). value . For a programmer it is much easier to have some value be kept in a variable named "var1" then at the address 5A73:235B. var2 RET . name . and press F5 key to compile it and load in the emulator. DW . MOV instruction is used to copy values from source to destination. VAR1 DB 7 var2 DW 1234h Copy the above code to the source editor. or "?" symbol for variables that are not initialized. You should get something like: . especially when you have 10 or more variables.stays for Define Word. It's possible to declare unnamed variables by not specifying the name (this variable will have an address but no name).stays for Define Byte. stops the program. Syntax for a variable declaration: name DB value name DW value DB .can be any letter or digit combination.can be any numeric value in any supported numbering system (hexadecimal.Variables Variable is a memory location.

and full address is 0B56:0108.code segment). except that variables are replaced with actual memory locations. It is assumed that low byte is stored at lower address. second row is a hexadecimal value. and full address is 0B56:0109. You can even write the same program using DB directive only: . You can see that there are some other instructions after the RET instruction. The offset of var2 is 0109h. By default segment is loaded in DS register (when COM files is loaded the value of DS register is set to the same value as CS register . and last row is an ASCII character value. it just processes the values in memory and it understands them as valid 8086 instructions (we will learn them later). When compiler makes machine code. Compiler is not case sensitive. so "VAR1" and "var1" refer to the same variable.As you see this looks a lot like our example. this variable is a WORD so it occupies 2 BYTES. it automatically replaces all variable names with their offsets. The offset of VAR1 is 0108h. this happens because disassembler has no idea about where the data starts. third row is decimal value. so 34h is located before 12h. In memory list first row is an offset.

Arrays Arrays can be seen as chains of variables. Maybe we'll talk more about EXE files later. It tells compiler that the executable file will be loaded at the offset of 100h (256 bytes).ORG 100h DB 0A0h DB 08h DB 01h DB DB DB DB 8Bh 1Eh 09h 01h DB 0C3h DB 7 DB 34h DB 12h Copy the above code to the source editor. Directives are never converted to any real machine code. and press F5 key to compile and load it in the emulator. 0 . 65h.255). ORG 100h is a compiler directive (it tells compiler how to handle the source code). so compiler should calculate the correct address for all variables when it replaces the variable names with their offsets. this set is called machine code. This directive is very important when you work with variables. 6Ch. 6Ch. A text string is an example of a byte array. and generally use special segment for variables. processor understands the machine code and executes it. Though this is true for COM files only. the compiler just converts the program source to the set of bytes. each character is presented as an ASCII code value (0. such as command line parameters and etc. Here are some array definition examples: a DB 48h. 6Fh. 00h b DB 'Hello'. You should get the same disassembled code. and the same functionality! As you may guess. Why executable file is loaded at offset of 100h? Operating system keeps some data about the program in the first 256 bytes of the CS (code segment).. EXE files are loaded at offset of 0000.

or smaller then -128. This chart shows a part of the memory where these arrays are declared: You can access the value of any element in array using square brackets. DW cannot be used to declare strings. The syntax for DUP: number DUP ( value(s) ) number . 2. 2. 3 MOV AL.b is an exact copy of the a array. 9. BP. 1. 2) is an alternative way of declaring: d DB 1. a[SI] If you need to declare a large array you can use DUP operator.expression that DUP will duplicate. you can use DW instead of DB if it's required to keep values larger then 255. SI. Getting the Address of a Variable . 9 one more example: d DB 5 DUP(1.number of duplicate to make (any constant value). 2. 2 Of course. 1. 9. for example: MOV SI. a[3] You can also use any of the memory index registers BX. for example: MOV AL. 1. 9. when compiler sees a string inside quotes it automatically converts it to set of bytes. DI. for example: c DB 5 DUP(9) is an alternative way of declaring: c DB 9. 2. 1. value .

WORD PTR . modify the contents of VAR1.There is LEA (Load Effective Address) instruction and alternative OFFSET operator. check value of VAR1 by moving it to AL. word access. get address of VAR1 in BX. VAR1 . for example when you need to pass parameters to a procedure. Reminder: In order to tell the compiler about data type. b. Here is another example. Here is first example: ORG 100h MOV LEA MOV MOV RET VAR1 DB 22h END AL. byte access. . Getting the address of the variable can be very useful in some situations. VAR1 BX. Both OFFSET and LEA can be used to get the offset address of the variable. For example: BYTE PTR [BX] . .for WORD PTR assembler supports shorter prefixes as well: in certain cases the assembler can calculate the data type automatically.for word (two bytes). LEA is more powerful because it also allows you to get the address of an indexed variables. . that uses OFFSET instead of LEA: . or WORD PTR [BX] . check value of VAR1 by moving it to AL.for byte. these prefixes should be used: BYTE PTR . 44h AL. . VAR1 .for BYTE PTR w. BYTE PTR [BX].

SI. Both examples have the same functionality.ORG 100h MOV MOV MOV MOV RET VAR1 DB 22h END AL. check value of VAR1 by moving it to AL. . get address of VAR1 in BX. VAR1 BX. num num is a 16 bit value of the variable offset. OFFSET VAR1 are even compiled into the same machine code: MOV BX. . DI. OFFSET VAR1 BYTE PTR [BX]. VAR1 MOV BX. Constants Constants are just like variables. To define constants EQU directive is used: name EQU < any expression > For example: k EQU 5 . modify the contents of VAR1. After definition of a constant its value cannot be changed. 44h AL. but they exist only until your program is compiled (assembled). VAR1 . Please note that only these registers can be used inside square brackets (as memory pointers): BX. These lines: LEA BX. check value of VAR1 by moving it to AL. BP! (see previous part of the tutorial). .

octal (base 8). some symbols are invisible). . k The above example is functionally identical to code: MOV AX. SIGNED . Variable can be viewed in any numbering system: • • • • • • HEX .binary (base 2). simply double click it. UNSIGNED . OCT . so any variable can be presented as an array. BIN . To view arrays you should click on a variable and set Elements property to array size. CHAR .hexadecimal (base 16).unsigned decimal (base 10). In assembly language there are not strict data types. You can edit a variable's value when your program is running.ASCII char code (there are 256 symbols. or select it and click Edit button.signed decimal (base 10). 5 You can view variables while your program executes by selecting "Variables" from the "View" menu of emulator.MOV AX.

3. binary "b" suffix. 5 (the array can be array of bytes or words. 4. octal "o" suffix. . String can be entered this way: 'hello world'. Arrays may be entered this way: 1. 0 (this string is zero terminated)..It is possible to enter numbers in any system. it depends whether BYTE or WORD is selected for edited variable). Expressions are automatically converted. 2. for example: when this expression is entered: 5+2 it will be converted to 7 etc.. hexadecimal numbers should have "h" suffix. decimal numbers require no suffix.

does not modify the AH register on . ASCII code: 72 INT 10h . Currently we are interested in software interrupts only. You may think that there are only 256 functions. Each interrupt may have sub-functions. 100h . return. these are called hardware interrupts. This functions displays a character on the screen. so we may set it only once. INT 10h / 0Eh sub-function receives an ASCII code of the character that will be printed in AL register.com . but sometimes other registers maybe in use. instead of writing a code to print a character you can simply call the interrupt and it will do everything for you. In general AH register is used.Interrupts Interrupts can be seen as a number of functions. 'l' . The following example uses INT 10h sub-function 0Eh to type a "Hello!" message. MOV . ASCII code: 108 . 0Eh . We call such functions software interrupts. Interrupts are also triggered by different hardware. Generally other registers are used to pass parameters and data to sub-function. The sub-function that we are using . There are also interrupt functions that work with disk drive and other hardware. instruct compiler to make simple single segment . ASCII code: 101 INT 10h . To make a software interrupt there is an INT instruction. 'H' . . ORG file. print it! MOV AL. 'e' . but that is not correct. MOV AL. . print it! MOV AL. These functions make the programming much easier. generally we will use hexadecimal numbers. . it has very simple syntax: INT value Where value can be a number between 0 to 255 (or 0 to 0FFh). AH. To specify a sub-function AH register should be set before calling interrupt. Each interrupt may have up to 256 sub-functions (so we get 256 * 256 = 65536 functions). select sub-function. advancing the cursor and scrolling the screen as necessary.

and press [Compile and Emulate] button. Run it! . ASCII code: 33 INT 10h . print it! . 'o' . ASCII code: 111 INT 10h . returns to operating system. print it! MOV AL.INT 10h . print it! MOV AL. 'l' INT 10h MOV AL. Copy & paste the above program to the source code editor. '!' . print it! RET . ASCII code: 108 .

turns on the text cursor. since you only need to understand what it can do.Library of common functions .is an ASCII code for 'A' . for example: include emu8086. but it's OK.inc (located in Inc folder).macro with 2 parameters. The same as PRINT but automatically adds "carriage return" at the end of the string. 5 PUTC 65 PUTC 'B' RET . CURSOROFF .macro with 1 parameter. PRINTN string . and if it cannot find the file there . prints out a string.inc you should have the following line in the beginning of your source file: include 'emu8086. Currently you may not be able to fully understand the contents of the emu8086.it searches in Inc folder. prints out an ASCII char at current cursor position.turns off the text cursor. Compiler automatically searches for the file in the same folder where the source file is located.inc' emu8086. To make your program use functions defined in other file you should use the INCLUDE directive followed by a file name. To use any of the above macros simply type its name somewhere in your code. prints out a string. sets cursor position. To use any of the functions in emu8086. PRINT string . . 65 .macro with 1 parameter. row .emu8086.inc defines the following macros: • • • • • • PUTC char . GOTOXY col. return to operating system.macro with 1 parameter.inc To make programming easier there are some common functions that can be included in your program. and if required parameters. CURSORON .inc ORG 100h PRINT 'Hello World!' GOTOXY 10.

Procedure stops the input when 'Enter' is pressed.inc also defines the following procedures: • • PRINT_STRING . receives address of string in DS:SI register. To use it declare: DEFINE_SCAN_NUM before END directive. Generally macros are relatively small parts of code.inc file for declarations of the macros and replaces the macro names with real code. and set cursor position to top of it. The ZERO TERMINATED string should be defined just after the CALL instruction. To use it declare: DEFINE_CLEAR_SCREEN before END directive. and then use CALL instruction followed by a procedure name. PRINT_NUM_UNS . PRINT_NUM .procedure that prints out an unsigned number in AX register.END . frequent use of a macro may make your executable too big (procedures are better for size optimization). To use it declare: DEFINE_GET_STRING before END directive.procedure to get a null terminated string from a user. • GET_STRING . For example: . PTHIS . the received string is written to buffer at DS:DI.procedure that gets the multi-digit SIGNED number from the keyboard. To use it declare: DEFINE_PRINT_NUM_UNS before END directive.procedure that prints a signed number in AX register. and stores the result in CX register. emu8086. CLEAR_SCREEN .procedure to print a null terminated string at current cursor position. buffer size should be in DX. directive to stop the compiler. but receives address of string from Stack. • • • • To use any of the above procedures you should first declare the function in the bottom of your file (but before the END directive). SCAN_NUM .procedure to print a null terminated string at current cursor position (just as PRINT_STRING). When compiler process your source code it searches the emu8086.procedure to clear the screen. (done by scrolling entire screen window). For example: CALL PTHIS db 'Hello World!'. To use it declare: DEFINE_PRINT_STRING before END directive. 0 To use it declare: DEFINE_PTHIS before END directive. To use it declare: DEFINE_PRINT_NUM and DEFINE_PRINT_NUM_UNS before END directive.

since even if you call the same procedure 100 times in your code you will still have relatively small executable size. print number in AX. return to operating system. First compiler processes the declarations (these are just regular the macros that are expanded to procedures). When compiler gets to CALL instruction it replaces the procedure name with the address of the code where the procedure is declared. 0 CALL print_num RET .include 'emu8086. ask for the number CALL print_string . When CALL instruction is executed control is transferred to procedure. msg1 . CX . currently it's required that you understand the basic principle. required for print_num. 'You have entered: '. print the following string: CALL pthis DB 13. DEFINE_PTHIS END . . MOV AX. . 0 DEFINE_SCAN_NUM DEFINE_PRINT_STRING DEFINE_PRINT_NUM DEFINE_PRINT_NUM_UNS . copy the number to AX. CALL scan_num . with the time you will learn more.inc' ORG 100h LEA SI. Seems complicated. get number in CX. . directive to stop the compiler. isn't it? That's ok. This is quite useful. 10. msg1 DB 'Enter the number: '.

Arithmetic and Logic Instructions Most Arithmetic and Logic Instructions affect the processor status register (or Flags) As you may see there are 16 bits in this register. Overflow Flag (OF) . • Carry Flag (CF) ..when this flag is set to 1 CPU reacts to interrupts from external devices. When there is no overflow this flag is set to 0. For example when you add bytes 255 + 1 (result is not in range 0. Interrupt enable Flag (IF) . Actually this flag take the value of the most significant bit.set to 1 when there is a signed overflow. Even if result is a word only 8 low bits are analyzed! Auxiliary Flag (AF) . When result is positive it is set to 0.. when this flag is set to 1 the processing is done backward. Direction Flag (DF) .set to 1 when there is an unsigned overflow for low nibble (4 bits). Parity Flag (PF) . • • • • • • • .255). each bit is called a flag and can take a value of 1 or 0. For example. and to 0 when there is odd number of one bits.set to 1 when result is zero. when you add bytes 100 + 50 (result is not in range -128.127).this flag is set to 1 when there is an unsigned overflow. For none zero result this flag is set to 0... Sign Flag (SF) .this flag is used by some instructions to process data chains.this flag is set to 1 when there is even number of one bits in result.set to 1 when result is negative. Zero Flag (ZF) . when this flag is set to 0 the processing is done forward.

ZF. BX. variable. immediate REG: AX. DX. DH. . result is always stored in first operand. These instructions affect these flags only: CF. etc. These rules apply: 1 1 0 0 AND AND AND AND 1 0 1 0 = = = = 1 0 0 0 As you see we get 1 only when both bits are 1. immediate REG. CX. SF..Logical AND between all bits of two operands.There are 3 groups of instructions.CMP. CL. CMP . After operation between operands. First group: ADD. memory memory. 3Fh. CMP and TEST instructions affect flags only and do not store a result (these instruction are used to make decisions during program execution).Subtract second operand from first for flags only. SUB . DL. AND . OF. AND. SI. REG REG.. PF. TEST. SUB. REG memory. [BX+SI+7]. immediate: 5. XOR These types of operands are supported: REG.. OR. CH. DI. 10001101b. AL. • • • • ADD . -24.Subtract second operand to first. AH.add second operand to first. BH. AF. memory: [BX].. BP. etc. SP. BL.

BH. These rules apply: 1 1 0 0 XOR XOR XOR XOR 1 0 1 0 = = = = 0 1 1 0 As you see we get 1 every time when bits are different from each other. variable. IMUL. CX. [BX+SI+7]. For DIV and IDIV flags are undefined. DH. CH. DX. OR .Logical OR between all bits of two operands. memory: [BX]. CL. OF When result is over operand size these flags are set to 1.. when result fits in operand size these flags are set to 0. IDIV These types of operands are supported: REG memory REG: AX. BP. DL.• • TEST . etc. SI. MUL and IMUL instructions affect these flags only: CF. • XOR .Logical XOR (exclusive OR) between all bits of two operands. AL. DI. DIV. BL.Unsigned multiply: . AH.The same as AND but for flags only. SP. These rules apply: 1 1 0 0 OR OR OR OR 1 0 1 0 = = = = 1 1 1 0 As you see we get 1 every time when at least one of the bits is 1. BX. • MUL . Second group: MUL..

when operand is a word: AX = (DX AX) / operand DX = remainder (modulus). when operand is a word: (DX AX) = AX * operand. • DIV . when operand is a word: (DX AX) = AX * operand. when operand is a word: AX = (DX AX) / operand DX = remainder (modulus). NEG These types of operands are supported: . • IDIV .Signed divide: when operand is a byte: AL = AX / operand AH = remainder (modulus).Signed multiply: when operand is a byte: AX = AL * operand. DEC. . . NOT. . Third group: INC.Unsigned divide: when operand is a byte: AL = AX / operand AH = remainder (modulus). • IMUL . .when operand is a byte: AX = AL * operand.

AL. and -2 will become 2. DL. DH.Make operand negative (two's complement). variable. ZF. memory: [BX]. DX. BX. INC. AF. OF. PF. Actually it reverses each bit of operand and then adds 1 to it. CX.Reverse each bit of operand. CL. CH. • NOT . SP. NOT instruction does not affect any flags! NEG instruction affects these flags only: CF.. etc. SF. BP. AF. PF. . BL.. DEC instructions affect these flags only: ZF. SI. DI. SF. AH. OF. For example 5 will become -5. [BX+SI+7].REG memory REG: AX. BH. NEG .

go to 'calc'. . set ax to 5. 2 calc . this is where your program can make decisions according to certain conditions. .Program flow control controlling the program flow is a very important thing. 2 here's an example of JMP instruction: org mov mov jmp 100h ax. for example: x1: MOV AX. label can be any character combination but it cannot start with a number. • unconditional jumps The basic instruction that transfers control to another point in the program is JMP. . set bx to 2. 1 x2: MOV AX. 5 bx. just type its name and add ":" to the end. for example here are 3 legal label definitions: label1: label2: a: Label can be declared on a separate line or before any other instruction. back: jmp stop . The basic syntax of JMP instruction: JMP label To declare a label in your program. go to 'stop'.

calc: add ax, bx jmp back stop: ret

; add bx to ax. ; go 'back'.

; return to operating system.

Of course there is an easier way to calculate the some of two numbers, but it's still a good example of JMP instruction. As you can see from this example JMP is able to transfer control both forward and backward. It can jump anywhere in current code segment (65,535 bytes). Short Conditional Jumps Unlike JMP instruction that does an unconditional jump, there are instructions that do a conditional jumps (jump only when some conditions are in act). These instructions are divided in three groups, first group just test single flag, second compares numbers as signed, and third compares numbers as unsigned. Jump instructions that test single flag Instruction JZ , JE JC , JB, JNAE JS JO JPE, JP JNZ , JNE Description Jump if Zero (Equal). Jump if Carry (Below, Not Above Equal). Jump if Sign. Jump if Overflow. Jump if Parity Even. Jump if Not Zero (Not Equal). Condition ZF = 1 CF = 1 SF = 1 OF = 1 PF = 1 ZF = 0 Opposite Instruction JNZ, JNE JNC, JNB, JAE JNS JNO JPO JZ, JE


Jump if Not Carry (Not Below, Above Equal). Jump if Not Sign. Jump if Not Overflow. Jump if Parity Odd (No Parity).

CF = 0 SF = 0 OF = 0 PF = 0


as you may already notice there are some instructions that do that same thing, that's correct, they even are assembled into the same machine code, so it's good to remember that when you compile JE instruction - you will get it disassembled as: JZ, JC is assembled the same as JB etc... different names are used to make programs easier to understand, to code and most importantly to remember. very offset dissembler has no clue what the original instruction was look like that's why it uses the most common name. if you emulate this code you will see that all instructions are assembled into JNB, the operational code (opcode) for this instruction is 73h this instruction has fixed length of two bytes, the second byte is number of bytes to add to the IP register if the condition is true. because the instruction has only 1 byte to keep the offset it is limited to pass control to -128 bytes back or 127 bytes forward, this value is always signed.
• • • • • • • • • •
jnc a jnb a jae a mov ax, 4 a: mov ax, 5 ret

Jump instructions for signed numbers Instruction JE , JZ Description Jump if Equal (=). Condition ZF = 1 Opposite Instruction JNE, JNZ

Jump if Zero. JNE , JNZ Jump if Not Equal (<>). Jump if Not Zero. Jump if Greater (>). Jump if Not Less or Equal (not <=). Jump if Less (<). Jump if Not Greater or Equal (not >=). Jump if Greater or Equal (>=). Jump if Not Less (not <). Jump if Less or Equal (<=). Jump if Not Greater (not >). ZF = 0 ZF = 0 and SF = OF SF <> OF SF = OF ZF = 1 or SF <> OF JE, JZ







<> - sign means not equal. Jump instructions for unsigned numbers Instruction JE , JZ JNE , JNZ Description Jump if Equal (=). Jump if Zero. Jump if Not Equal (<>). Jump if Not Zero. Jump if Above (>). Jump if Not Below or Equal (not <=). Jump if Below (<). Condition ZF = 1 ZF = 0 CF = 0 and ZF = 0 CF = 1 Opposite Instruction JNE, JNZ JE, JZ



Jump if Not Above or Equal (not >=). Jump if Carry. JAE , JNB, JNC Jump if Above or Equal (>=). Jump if Not Below (not <). Jump if Not Carry. Jump if Below or Equal (<=). Jump if Not Above (not >). CF = 0 CF = 1 or ZF = 1 JNAE, JB



Generally, when it is required to compare numeric values CMP instruction is used (it does the same as SUB (subtract) instruction, but does not keep the result, just affects the flags). The logic is very simple, for example: it's required to compare 5 and 2, 5-2=3 the result is not zero (Zero Flag is set to 0). Another example: it's required to compare 7 and 7, 7-7=0 the result is zero! (Zero Flag is set to 1 and JZ or JE will do the jump). here's an example of CMP instruction and conditional jump:
• • • • • • • • • • •

include "emu8086.inc" org mov mov cmp je 100h al, 25 bl, 10 al, bl equal ; set al to 25. ; set bl to 10. ; compare al - bl. ; jump if al = bl (zf = 1).

decrease cx. jump to label if cx not zero and zf = 0.. decrease cx. so print 'y'. opposite instruction DEC CX and JCXZ LOOPNE LOOPE LOOPZ LOOPNZ OR CX. and this is just what loop does. decrease cx. CX and JNZ • loops are basically the same jumps. and three and etc. and .. . . jump to label if cx not zero and zf = 1. it is possible to code loops without using the loop instruction. jump to label if cx not zero and equal (zf = 1). jump to label if cx is zero. . gets here no matter what. open flags by clicking on flags button. use single step and see what happens. all loop instructions use CX register to count steps. then al <> bl. so print 'n'.• • • • • • • • • • • • putc 'n' jmp stop equal: putc 'y' stop: ret . jump to label if cx not zero. try the above example with different numbers for AL and BL. if it gets here. you can use F5 hotkey to recompile and reload the program into the emulator. if gets here. as you know CX register has 16 bits and the maximum value it can hold is 65535 or FFFF. decrease cx. • • loops instruction LOOP LOOPE LOOPNE LOOPNZ LOOPZ JCXZ operation and jump condition decrease cx. by just using conditional jumps and compare. and jump to stop. however with some agility it is possible to put one loop into another. jump to label if cx not zero and not equal (zf = 0). then al = bl. . and another into another two.

0eh int 10h push cx mov cx. it is possible store original value of cx register using push cx instruction and return it to original when the internal loop ends with pop cx. you can double click the register to see the value in all available bases. '2' mov ah. just like all other conditional jumps loops have an opposite companion that can help to create workarounds. external loop. ret bx counts total number of steps.. by default emulator shows values in hexadecimal.receive a nice value of 65535 * 65535 * 65535 . 5 k2: add bx. making total of 5 bytes instead of just 2. 1 mov al. or the end of ram or stack memory. 0eh int 10h push cx mov cx.. 5 k1: add bx. 0eh int 10h loop k3 . total step counter. pop cx loop k1 .. '1' mov ah. pop cx loop k2 .. when the address of desired location is too far assemble automatically assembles reverse and long jump instruction. 1 mov al. for example: • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • org 100h mov bx. internal loop. . 0 . 1 mov al. '3' mov ah..till infinity. internal in internal loop. mov cx. 5 k3: add bx. it can be seen in disassembler as well..

inc" org mov mov cmp 100h al. if it gets here. We can easily avoid this limitation using a cute trick: o o o Get an opposite conditional jump instruction from the table above. 5 al. . 5 bl. unlike JMP instruction they can only jump 127 bytes forward and 128 bytes backward (note that most instructions are assembled into 3 or more bytes). . je equal jne not_equal .bl. bl . so print 'n'. jmp equal not_equal: add sub xor bl. jump if al <> bl (zf = 0). there is only 1 byte . 10 al. al al.All conditional jumps have one big limitation. compare al . bl .can be any valid label name. here's an example: include "emu8086. make it jump to label_x. label_x: . then al <> bl. Define label_x: just after the JMP instruction. . and jump to stop. 256 bytes jmp skip_data db 256 dup(0) skip_data: putc 'n' jmp stop . but there must not be two or more labels with the same name. Use JMP instruction to jump to desired location.

if gets here. Note: the latest version of the integrated 8086 assembler automatically creates a workaround by replacing the conditional jump with the opposite. otherwise compiler calculates instruction that jumps directly to given offset. 2 bytes. To check if you have the latest version of emu8086 click help-> check for an update from the menu. 1 byte. . unconditional jump forward: . then al = bl.9 dec bl . b db 4 . 3 bytes. jne $-5 . 1 byte. and adding big unconditional jump. jmp $3+2 a db 3 . conditional jump back 5 bytes: mov bl.equal: putc 'y' stop: ret . . so print 'y'. c db 4 . When immediate value starts with $ relative jump is performed. Another. cmp bl. 1 byte. For example: org 100h . skip over next 3 bytes + itself . jump 5 bytes back ret . yet rarely used method is providing an immediate value instead of label. the machine code of short jmp instruction takes 2 bytes. 0 .

the same name should be in the top and the bottom. . Generally procedure returns to the same point from where it was called.. PROC and ENDP are compiler directives.. Compiler just remembers the address of procedure.is the procedure name. Procedures make program more structural and easier to understand. of the procedure . Here is an example: ORG 100h CALL m1 MOV RET m1 PROC MOV BX. 2 . Probably. so they are not assembled into any real machine code. The same instruction is used to return from procedure (actually operating system sees your program as a special procedure). CALL instruction is used to call a procedure. this is used to check correct closing of procedures. 5 AX.Procedures Procedure is a part of code that can be called from your program in order to make some specific task. you already know that RET instruction is used to return to operating system. RET name ENDP name . The syntax for procedure declaration: name PROC . return to operating system. here goes the code .

In the above example value of AL register is update every time the procedure is called. multiplies these parameters and returns the result in AX register: ORG MOV MOV CALL CALL CALL CALL RET m2 MUL RET m2 END 100h AL. the easiest way to pass parameters is by using registers. return to caller. so this algorithm calculates 2 in power of 4. that uses a procedure to print a Hello World! message: . AX = AL * BL. PROC BL ENDP . . There are several ways to pass parameters to procedure. 1 BL. and returns to the next instruction after CALL: MOV AX. does MOV BX. 5. here is another example of a procedure that receives two parameters in AL and BL registers. return to operating system. Here goes another example. 2. so final result in AX register is 16 (or 10h). BL register stays unchanged.RET m1 END ENDP . return to caller. The above example calls procedure m1. 2 m2 m2 m2 m2 .

" . . 0 . return to operating system. When you need to compare words add "w. null terminated string. the string should be null . load address of msg to SI. not words. 1 JMP next_char . MOV AH. msg . 0Eh . When one of the compared operands is a register it's not required because compiler knows the size of each register. next get ASCII char.ORG LEA 100h SI. teletype function number. =========================================== =============== . this procedure prints a string. MOV AL. . =========================================== =============== msg END DB 'Hello World!'." prefix instead. using interrupt to print a char in AL. terminated (have zero in the end). 0 . . check for zero to stop JE stop .[SI]. "b. CALL print_me RET . go back. [SI] . . and type another char.prefix before [SI] means that we need to compare bytes. stop: RET . the string address should be in SI register: print_me PROC next_char: CMP b. INT 10h . advance index of string array. ADD SI. print_me ENDP . return to caller.

-24. CX. DI... SI. etc. POP . SP.. Stack is used by CALL instruction to keep return address for procedure..gets 16 bit value from the stack. memory: [BX]. RET instruction gets this value from the stack and returns to that offset. Syntax for POP instruction: POP REG POP SREG POP memory REG: AX. SREG: DS. SS. . 16 bit variable. it stores in stack flag register. DI. code segment and offset. there are two instructions that work with the stack: PUSH .The Stack Stack is an area of memory for keeping temporary data. Syntax for PUSH instruction: PUSH REG PUSH SREG PUSH memory PUSH immediate REG: AX. 10001101b.stores 16 bit value in the stack. CS. immediate: 5. 3Fh. etc. ES. BP. SP. BX. BX. DX. CX. DX. We can also use the stack to keep any other data. SI. BP. IRET instruction is used to return from interrupt call. Quite the same thing happens when INT instruction calls an interrupt. [BX+SI+7].

5 the first value that we will get on pop will be 5. (except CS). 2. [BX+SI+7].. 2. 16 bit variable. Notes: • • PUSH and POP work with 16 bit values only! Note: PUSH immediate works only on 80186 CPU and later! The stack uses LIFO (Last In First Out) algorithm. so when program starts there is a return address in stack (generally it's 0000h). . SS. otherwise the stack maybe corrupted and it will be impossible to return to operating system. and only then 1. this means that if we push these values one by one into the stack: 1. It is very important to do equal number of PUSHs and POPs. 3. 3. As you already know we use RET instruction to return to operating system.SREG: DS. then 4. ES. 4.. etc. memory: [BX].

. Another use of the stack is for exchanging the values. store value of BX in stack. modify the AX value. restore the original value of AX. store 3434h in BX . store 1212h in AX. here is an example: ORG MOV MOV 100h AX. 5678h . 1212h . Restore the original value of the register from stack (using POP). AX . so here is a trick: • • • Store original value of the register in stack (using PUSH). BX. store value of AX in stack. 1234h PUSH AX . PUSH AX PUSH BX . Use the register for any purpose. MOV POP RET END AX. Here is an example: ORG 100h MOV AX. store value of AX in stack. 3434h .PUSH and POP instruction are especially useful because we don't have too much registers to operate with.

At the address SS:0FFFEh stored a return address for RET instruction that is executed in the end of the program. The current address pointed by SS:SP is called the top of the stack. The top of the stack is marked with "<" sign. set AX to original value of BX. set BX to original value of AX. You can visually see the stack operation by clicking on [Stack] button on emulator window. Generally operating system sets values of these registers on program start. Write the value of source to the address SS:SP. and SP (Stack Pointer) register. For COM files stack segment is generally the code segment. Add 2 to SP register. The exchange happens because stack uses LIFO (Last In First Out) algorithm. and stack pointer is set to value of 0FFFEh. . so when we push 1212h and then 3434h. The stack memory area is set by SS (Stack Segment) register. "POP destination" instruction does the following: • • Write the value at the address SS:SP to destination.POP POP RET END AX BX . . on pop we will first get 3434h and only after it 1212h. "PUSH source" instruction does the following: • • Subtract 2 from SP register.

for example: MyMacro p3 MACRO p1. If you declared a macro and never used it in your code. but not really. 5..inc is a good example of how macros can be used. p1 MOV BX. after compilation all macros are replaced with real instructions. but they exist only until your code is compiled. Macro definition: name MACRO [parameters.Macros Macros are just like procedures. p2. DX RET The above code is expanded into: . p3 ENDM ORG 100h MyMacro 1. emu8086. Macros look like procedures. macros should be defined above the code that uses it. compiler will simply ignore it.. 3 MyMacro 4. this file contains several macros to make coding easier for you. 2.] <instructions> ENDM Unlike procedures. p2 MOV CX. MOV AX..

00001h 00002h 00003h 00004h 00005h DX Some important facts about macros and procedures: • When you want to use a procedure you should use CALL instruction. The stack is used to keep the return address. and if you use the same procedure 100 times. CX. CX. You should use stack or any general purpose registers to pass parameters to procedure. BX. To pass parameters to macro. for example: CALL MyProc • When you want to use a macro. the CPU will transfer control to this part of the memory. For example: MyMacro • Procedure is located at some specific address in memory. The CALL instruction takes about 3 bytes. you should type the name of the procedure before the ENDP directive. 2. making the output executable file larger and larger. 3 • • • • • To mark the end of the macro ENDM directive is enough. the compiler expands the macro 100 times. So if you use the same macro 100 times.MOV MOV MOV MOV MOV MOV AX. AX. To mark the end of the procedure. The control will be returned back to the program by RET instruction. so the size of the output executable file grows very insignificantly. Macro is expanded directly in program's code. For example: MyMacro 1. . you can just type them after the macro name. you can just type its name. no matter how many time the procedure is used. BX. each time all instructions of a macro are inserted.

it may be a good idea to place all macros in a separate file. 2 ENDM ORG 100h MyMacro2 MyMacro2 RET If you plan to use your macros in several programs. . For example: MyMacro2 MACRO LOCAL label1. label2 CMP AX.Macros are expanded directly in code. labels or procedure names. 2 JE label1 CMP AX. To avoid such problem. Place that file in Inc folder and use INCLUDE file-name directive to use macros. therefore if there are labels inside the macro definition you may get "Duplicate declaration" error when macro is used for twice or more. use LOCAL directive followed by names of variables. 3 JE label2 label1: INC AX label2: ADD AX.

If this fails. when a computer starts it will try to load the first 512-byte sector (that's Cylinder 0. teletype function id: MOV AH.. .. corrections: ORG 7C00h PUSH CS . so inform compiler to make required . msg . Sector 1) from any diskette in your A: drive to memory location 0000h:7C00h and give it control. But using a floppy drive has several advantages: • • you can keep your existing operating system intact (windows. unix. it is easy and safe to modify the boot record of a floppy disk. Head 0. example of a simple floppy disk boot program: . load message address into SI register: LEA SI. 0Eh print: MOV AL.). [SI] CMP AL. be-os. INC SI JMP print . dos. make sure DS=CS POP DS . Boot record is loaded at 0000:7C00. linux.Making your own operating system Usually. the BIOS tries to use the MBR of the first hard drive instead. This tutorial covers booting up from a floppy drive. 0 JZ done INT 10h . the same principles are used to boot from a hard drive. print using teletype. directive to create BOOT file: #make_boot# .

. 0000h . 0000h . reboot! new_line EQU 13. or you can use the virtual drive menu to write 512 bytes at 7c00h to boot sector of a virtual floppy drive (it's "FLOPPY_0" file in Emulator's folder).bin file to 0000h:7C00h (it uses supplementary . you can run it just like a regular program. after your program is written to the virtual floppy drive.binf file to know where to load).[0072h]. 10 msg DB 'Hello This is My First Boot Program!' DB new_line. 0040h MOV DS. JMP 0FFFFh:0000h . wait for 'any key': done: MOV AH. MOV AX. the emulator automatically loads . 1234h . 'Press any key to reboot'. 0 INT 16h .cold boot. AX MOV w. cold boot. you can select boot from floppy from virtual drive menu. 0 copy the above example to the source editor and press emulate. store magic value at 0040h:0072h: .. .warm boot.

bin file to floppy. Head: 0.asm does). It's recommended to use ". Sector: 1).bin" files for this purpose (to create ". you can use additional sectors of a floppy disk.. . select "Write ...asm To create extensions for your operating system (over 512 bytes).bin" file to virtual floppy. an example of a tiny operating system can be found in c:\emu8086\examples: micro-os_loader. To write ". you will need to use a boot program to load data from other sectors (just like micro-os_loader.bin files for boot records are limited to 512 bytes (sector size)." from "Virtual drive" menu of emulator. if your new operating system is going to grow over this size. you should write it anywhere but the boot sector (which is Cylinder: 0.asm micro-os_kernel.bin" file select "BIN Template" from "File" -> "New" menu).


bin files to virtual floppy disk ("FLOPPY_0" file).bin file that is designed to be a boot record should always be written to cylinder: 0. you should remember that .parameter tells the program to write the file at sector 2 instead of sector 1. head: 0. instead of "write 512 bytes at 7c00h to boot sector" menu. such a good type of self destructing data carrier :) . sector: 1 Boot Sector Location: Cylinder: 0 Head: 0 Sector: 1 to write . even if someone gets the disk he will probably think that it's empty and will reformat it because it's the default option in windows operating system.. therefore make sure the diskette you use doesn't contain any important information. it's not even Linux or Unix compatible. however you can write and read anything to and from this disk using low level disk access interrupts.you can use this utility to write . however. just compile it to com file and run it from command prompt. but it does matter where you write them. it's even possible to protect valuable information from the others this way.asm. operating system may not allow you to read or write files on this diskette until you re-format it. it does not matter in what order you write the files onto floppy drive.bin files to real floppy disk use writebin. to write a boot record type: writebin loader. to write kernel module type: writebin kernel.bin .. mote: this boot record is not MS-DOS/Windows compatible boot sector.bin /k /k .

each cylinder has 18 sectors (1. and there are 2 heads.896 bytes) because the operating system needs place to store file names and directory structure (often called FAT or file system allocation ..1).idealized floppy drive and diskette structure: for a 1440 kb diskette: • floppy disk has 2 sides.. each sector has 512 bytes.. each side has 80 cylinders (numbered 0. • • • • note: the MS-DOS (windows) formatted floppy disk has slightly less free space on it (by about 16.79).560 bytes. one for each side (0.474. the drive heads move above the surface of the disk on each side. total size of floppy disk is: 2 x 80 x 18 x 512 = 1.18).

to read sectors from floppy drive use INT 13h / AH = 02h. if you know how to use it.less disk space. the most efficient way to store files is to write them directly to sectors instead of using file system. . more file names . and in some cases it is also the most reliable way.table).

exe . Traffic Lights Usually to control the traffic lights an array (table) of values is used. the difference maybe in base I/O port number. printer. Usually the ". realistic test for c:\emu8086\devices\Traffic_Lights. In general. stepper-motor. This principle is used for many modern devices such as micro-wave ovens and etc. In certain periods of time the value is read from the array and sent to a port. For example: . LED display. the system reads program from that chip. For technical information see I/O ports section of emu8086 reference.. robot and simple test device.. it is possible to use any x86 family CPU to control all kind of devices. . controlling external device with 8086 microprocessor. this can be altered using some tricky electronic equipment. You can view devices when you click "Virtual Devices" menu of the emulator. thermometer.bin" file is written into the Read Only Memory (ROM) chip.Controlling External Devices There are 7 devices attached to the emulator: traffic lights. loads it in RAM module and runs the program.

ax mov si. wait 5 seconds (5 million microseconds) mov cx. 4Ch .000. offset situation next: mov ax. 004C4B40h = 5. 4B40h mov ah.exe# name "traffic" mov ax. sit_end jb next mov si. [si] out 4. ax . 2 .000 mov dx. all_red out 4. offset situation jmp next . FEDC_BA98_7654_3210 situation dw 0000_0011_0000_1100b s1 dw 0000_0110_1001_1010b s2 dw 0000_1000_0110_0001b s3 dw 0000_1000_0110_0001b s4 dw 0000_0100_1101_0011b sit_end = $ all_red equ 0000_0010_0100_1001b . 86h int 15h add si. next situation cmp si.#start=Traffic_Lights.

followed by a single and so on.asm from c:\emu8086\examples See also I/O ports section of emu8086 reference.Stepper-Motor The motor can be half stepped by turning on pair of magnets.5 degrees. Robot . followed by another pair of magnets and in the end followed by a single magnet and so on. The motor can be full stepped by turning on pair of magnets.25 degrees. Half step is equal to 11. open stepper_motor. The motor can be turned both clock-wise and counter-clock-wise. The best way to make full step is to make two half steps. Full step is equal to 22.

. To control the robot a complex algorithm should be used to achieve maximum efficiency. The simplest.asm from c:\emu8086\examples It is also possible to use a data table (just like for Traffic Lights). is random moving algorithm. this can be good if robot always works in the same surroundings. yet very inefficient.Complete list of robot instruction set is given in I/O ports section of emu8086 reference. open robot.

Master your semester with Scribd & The New York Times

Special offer for students: Only $4.99/month.

Master your semester with Scribd & The New York Times

Cancel anytime.