This is a page of notes to help you learn to write simple programs in Intel Assembler and debug your programs using DOS DEBUG.
- You can find information on 80x86 addressing in this Power Point presentation and this web page.
- An explanation of the relationship between mnemonics, bytes, bits, and opcodes is in this web page.
- A list of Intel 8088 assembler mnemonics is in this PDF file [153KB]: 22Instructions_x86.pdf.
- Using DEBUG to examine registers, trace execution, assemble code, and unassemble code, is covered in 23DEBUGExecution.htm.
- DOS interrupt services are covered below and in this Power Point presentation and these web pages: 24MSDOSInterruptServices.htm, 24HardwareInterrupts.htm.
- A list and explanation of some basic Intel Assembler mnemonics is here: 25BasicAssembler.htm.
- An introduction to coding high level language control structures in assembly language is in this Power Point presentation and this web page.
This page contains the following sections:
The Stack is an essential part of assembly language programming. This section starts with the simple Intel PUSH/POP instructions and then shows how other assembly language instructions such as CALL and INT also use The Stack.
The PUSH, POP, CALL, and INT instructions all use the stack (the memory area pointed at by SS:SP). In .COM format executables, the stack is at the very top (high memory) of the single segment, typically starting at FFFEh, and it grows downward in memory (toward zero) with each PUSH. You cannot PUSH single bytes - the stack pointer always moves by a multiple of two bytes.
When a segment:offset register pair need to be pushed on the stack, the segment is pushed first, followed by the offset, e.g. CS is pushed before IP, and thus IP is stored at a lower memory address than CS. You can either remember that the push happens in natural CS, IP order, or you can remember that, in little-endian fashion, the Little register (the offset register) ends up in lower memory and the Big register (the segment register) ends up in higher memory. (If the Flags need to be pushed as well, as with interrupts, the flags are pushed before CS:IP.)
The following DEBUG scripts and their annotated output files show how the Intel CPU handles each type of instruction. Each of the input text files was run through DEBUG to produce the corresponding output file, to which explanatory comments were added by hand:
C:\> debug <push_pop.txt >push_pop_out.txt C:\> debug <call_push.txt >call_push_out.txt C:\> debug <int_push.txt >int_push_out.txtRead these annotated output files for examples and explanations of the workings of PUSH/POP, CALL, and INT:
DEBUG Input Annotated DEBUG Output push_pop.txt push_pop_out.txt call_push.txt call_push_out.txt int_push.txt int_push_out.txt Homework, test, and exam questions typically show one of these types of instructions and ask you to describe, in execution order, which registers and what memory is affected as the instruction executes (see the Homework questions!). For example:
Exam Question: Given the following register contents, if the listed "CALL FABC:DEF0" instruction were executed, list, in execution order, the changes to registers and memory that would occur. Give both the old value and new value of each register that changes. You must address all of these items:
a) What registers would change, and what would be their old and new values?
b) What memory values would change? Give the memory addresses in both segment:offset and 20-bit real form.
c) What would be the values put in those changed memory locations?
d) What value would appear in the byte addressed by the final value of the stack pointer?AX=A1DF BX=F04C CX=F02C DX=791A SP=E010 BP=123F SI=23F3 DI=A0A0 DS=24FC ES=5AF9 SS=000F CS=1E5A IP=0AB3 NV UP EI PL NZ NA PO NC 1E5A:0AB3 9AF0DEBCFA CALL FABC:DEF0Answers:
(Remember: A "far CALL" into a different segment saves the full CS:IP return address before jumping to the subroutine.)ANSWER (a-c): Note that order is important here! IP: 0AB3 -> 0AB8 ; add 5 for length of instruction "9AF0DEBCFA" SP: E010 -> E00E ; decrement to PUSH two bytes of CS [SP] <- 1E5A (CS) so [SS:SP] <- 1E5A so [000F:E00E] <- 1E5A so [0E0FE] <- 1E5A SP: E00E -> E00C ; decrement to PUSH two bytes of IP [SP] <- 0AB8 (IP) so [SS:SP] <- 0AB8 so [000F:E00C] <- 0AB8 so [0E0FC] <- 0AB8 IP: 0AB8 -> DEF0 ; load new IP CS: 1E5A -> FABC ; load new CS ANSWER (d): B8 (the low byte of the last value pushed: 0AB8)
Assembly language has no high-level control flow statements. To achieve the same effect, you must use conditional branching. See the Control Structure page and this Power Point presentation for details on how to turn structured programming statements such as IF, WHILE, FOR, etc., into assembly language.
Copying and modifying well-written existing programs is a good way to learn the style of assembly language programs. We have a small selection of sample programs for you to examine in this course. (Not all are well-written!)
Note that the comment style used in these programs is that of an instructor teaching a student how to use assembly language. It is not the style that you would use when writing programs in the real world, and it it not the style you should use when submitting your projects. Comment your algorithm, not how the language works.
If you can't figure out what your program is doing, you can debug it one-line-at-a-time using the DOS DEBUG command. The DOS DEBUG command lets you single-step through your programs to debug them.
Using DEBUG to examine registers and trace execution is covered in this web page.
You can see an example of tracing a very small assembler program here.
For more information on using DEBUG, see the Using DOS DEBUG page.