Вы находитесь на странице: 1из 139

The Art of Assembly Language

An assembly language is a low-level programming language for a computer,microcontroller, or other programmable device, in which each statement corresponds to a single machine code instruction. Each assembly language is specific to a particularcomputer architecture, in contrast to most high-level programming languages, which are generally portable across multiple systems. Assembly language is converted into executable machine code by a utility program referred to as an assembler; the conversion process is referred to as assembly, or assembling the code. Assembly language uses a mnemonic to represent each low-level machine operation oropcode. Some opcodes require one or more operands as part of the instruction, and most assemblers can take labels and symbols as operands to represent addresses and constants, instead of hard coding them into the program. Macro assemblers include amacroinstruction facility so that assembly language text can be pre-assigned to a name, and that name can be used to insert the text into other code. Many assemblers offer additional mechanisms to facilitate program development, to control the assembly process, and to aiddebugging.
Contents

[hide]

1 Key concepts o 1.1 Assembler 1.1.1 Number of passes 1.1.2 High-level assemblers o 1.2 Assembly language 2 Language design o 2.1 Basic elements 2.1.1 Opcode mnemonics and extended mnemonics 2.1.2 Data directives 2.1.3 Assembly directives o 2.2 Macros o 2.3 Support for structured programming 3 Use of assembly language o 3.1 Historical perspective o 3.2 Current usage o 3.3 Typical applications 4 Related terminology 5 List of assemblers for different computer architectures 6 Further details 7 Example listing of assembly language source code 8 See also 9 References 10 Further reading 11 External links

[edit]Key

concepts

[edit]Assembler An assembler creates object code by translating assembly instruction mnemonics into opcodes, and by resolving symbolic

names for memory locations and other entities.[1] The use of symbolic references is a key feature of assemblers, saving tedious calculations and manual address updates after program modifications. Most assemblers also include macro facilities for performing textual substitutione.g., to generate common short sequences of instructions as inline, instead of called subroutines. Assemblers have been available since the 1950s and are far simpler to write than compilers for high-level languages as each mnemonic instruction / address mode combination translates directly into a single machine language opcode. Modern assemblers, especially forRISC architectures, such as SPARC or POWER, as well as x86 and x86-64, optimize Instruction scheduling to exploit the CPU pipeline efficiently.[citation needed] [edit]Number of passes There are two types of assemblers based on how many passes through the source are needed to produce the executable program.

One-pass assemblers go through the source code once. Any symbol used before it is defined will require "errata" at the end of the object code (or, at least, no earlier than the point where the symbol is defined) telling the linker or the loader to "go back" and overwrite a placeholder which had been left where the as yet undefined symbol was used.

Multi-pass assemblers create a table with all symbols and their values in the first passes, then use the table in later passes to generate code.

In both cases, the assembler must be able to determine the size of each instruction on the initial passes in order to calculate the addresses of subsequent symbols. This means that if the size of an operation referring to an operand defined later depends on the type or distance of the operand, the assembler will make a pessimistic estimate when first encountering the operation, and if necessary pad it with one or more "no-operation" instructions in a later pass or the errata. In an assembler with peephole optimization, addresses may be recalculated between passes to allow replacing pessimistic code with code tailored to the exact distance from the target. The original reason for the use of one-pass assemblers was speed of assembly often a second pass would require rewinding and rereading a tape or rereading a deck of cards. With modern computers this has ceased to be an issue. The advantage of the multi-pass assembler is that the absence of errata makes the linking process (or the program load if the assembler directly produces executable code) faster.[2] [edit]High-level assemblers More sophisticated high-level assemblers provide language abstractions such as:

Advanced control structures

High-level procedure/function declarations and invocations High-level abstract data types, including structures/records, unions, classes, and sets Sophisticated macro processing (although available on ordinary assemblers since the late 1950s for IBM 700 series and since the 1960s for IBM/360, amongst other machines) Object-oriented programming features such as classes, objects, abstraction, polymorphism, and inheritance[3]

See Language design below for more details. [edit]Assembly

language

A program written in assembly language consists of a series of (mnemonic) processor instructions and meta-statements (known variously as directives, pseudo-instructions and pseudo-ops), comments and data. Assembly language instructions usually consist of an opcode mnemonic followed by a list of data, arguments or parameters.[4] These are translated by an assembler into machine language instructions that can be loaded into memory and executed. For example, the instruction below tells an x86/IA-32 processor to move an immediate 8-bit value into a register. The binary code for this instruction is 10110 followed by a 3-bit identifier for which register to use. The identifier for the AL register is 000, so the followingmachine code loads the AL register with the data 01100001.[5]

10110000 01100001 This binary computer code can be made more human-readable by expressing it in hexadecimal as follows B0 61 Here, B0 means 'Move a copy of the following value into AL', and 61 is a hexadecimal representation of the value 01100001, which is 97 in decimal. Intel assembly language provides the mnemonic MOV (an abbreviation of move) for instructions such as this, so the machine code above can be written as follows in assembly language, complete with an explanatory comment if required, after the semicolon. This is much easier to read and to remember. ; Load AL with 97 decimal (61 MOV AL, 61h hex) In some assembly languages the same mnemonic such as MOV may be used for a family of related instructions for loading, copying and moving data, whether these are immediate values, values in registers, or memory locations pointed to by values in registers. Other assemblers may use separate opcodes such as L for "move memory to register", ST for "move register to memory", LR for "move register to register", MVI for "move immediate operand to memory", etc. The Intel opcode 10110000 (B0) copies an 8-bit value into the AL register, while 10110001 (B1) moves it into CL and

10110010 (B2) does so into DL. Assembly language examples for these follow.[5] ; Load AL with immediate value MOV AL, 1h 1 ; Load CL with immediate value MOV CL, 2h 2 ; Load DL with immediate value MOV DL, 3h 3 The syntax of MOV can also be more complex as the following examples show.[6] ; Move the 4 bytes in memory MOV EAX, [EBX] at the address contained in EBX into EAX MOV [ESI+EAX], CL ; Move the contents of CL into the byte at address ESI+EAX In each case, the MOV mnemonic is translated directly into an opcode in the ranges 88-8E, A0-A3, B0-B8, C6 or C7 by an assembler, and the programmer does not have to know or remember which.[5] Transforming assembly language into machine code is the job of an assembler, and the reverse can at least partially be achieved by adisassembler. Unlike high-level languages, there is usually a one-to-one correspondence between simple assembly statements and machine language instructions. However, in some cases, an assembler may provide pseudoinstructions (essentially macros) which expand into several machine language instructions to provide commonly needed functionality. For example, for a machine that lacks a "branch if greater or equal" instruction, an assembler may provide a pseudoinstruction that expands to the

machine's "set if less than" and "branch if zero (on the result of the set instruction)". Most full-featured assemblers also provide a rich macro language (discussed below) which is used by vendors and programmers to generate more complex code and data sequences. Each computer architecture has its own machine language. Computers differ in the number and type of operations they support, in the different sizes and numbers of registers, and in the representations of data in storage. While most general-purpose computers are able to carry out essentially the same functionality, the ways they do so differ; the corresponding assembly languages reflect these differences. Multiple sets of mnemonics or assembly-language syntax may exist for a single instruction set, typically instantiated in different assembler programs. In these cases, the most popular one is usually that supplied by the manufacturer and used in its documentation. [edit]Language [edit]Basic

design

elements

There is a large degree of diversity in the way the authors of assemblers categorize statements and in the nomenclature that they use. In particular, some describe anything other than a machine mnemonic or extended mnemonic as a pseudooperation (pseudo-op). A typical assembly language consists of 3

types of instruction statements that are used to define program operations:


Opcode mnemonics Data sections Assembly directives

[edit]Opcode mnemonics and extended mnemonics Instructions (statements) in assembly language are generally very simple, unlike those in high-level language. Generally, a mnemonic is a symbolic name for a single executable machine language instruction (an opcode), and there is at least one opcode mnemonic defined for each machine language instruction. Each instruction typically consists of an operation or opcode plus zero or more operands. Most instructions refer to a single value, or a pair of values. Operands can be immediate (value coded in the instruction itself), registers specified in the instruction or implied, or the addresses of data located elsewhere in storage. This is determined by the underlying processor architecture: the assembler merely reflects how this architecture works. Extended mnemonics are often used to specify a combination of an opcode with a specific operand, e.g., the System/360 assemblers use B as an extended mnemonic for BC with a mask of 15 and NOP for BC with a mask of 0. Extended mnemonics are often used to support specialized uses of instructions, often for purposes not obvious from the instruction name. For example, many CPU's do not have an explicit NOP instruction, but do have instructions that can be used for the

purpose. In 8086 CPUs the instruction xchg ax,ax is used for nop, with nop being a pseudo-opcode to encode the instruction xchg ax,ax. Some disassemblers recognize this and will decode the xchg ax,ax instruction as nop. Similarly, IBM assemblers for System/360 andSystem/370 use the extended mnemonics NOP and NOPR for BC and BCR with zero masks. For the SPARC architecture, these are known as synthetic instructions[7] Some assemblers also support simple built-in macro-instructions that generate two or more machine instructions. For instance, with some Z80 assemblers the instruction ld hl,bc is recognized to generate ld l,c followed by ld h,b.[8] These are sometimes known aspseudo-opcodes. Mnemonics are arbitrary symbols; in 1985 the IEEE published Standard 694 for a uniform set of mnemonics to be used by all assemblers. The standard has since been withdrawn. [edit]Data directives There are instructions used to define data elements to hold data and variables. They define the type of data, the length and thealignment of data. These instructions can also define whether the data is available to outside programs (programs assembled separately) or only to the program in which the data section is defined. Some assemblers classify these as pseudo-ops. [edit]Assembly directives

Assembly directives, also called pseudo opcodes, pseudooperations or pseudo-ops, are instructions that are executed by an assembler at assembly time, not by a CPU at run time. They can make the assembly of the program dependent on parameters input by a programmer, so that one program can be assembled different ways, perhaps for different applications. They also can be used to manipulate presentation of a program to make it easier to read and maintain. (For example, directives would be used to reserve storage areas and optionally their initial contents.) The names of directives often start with a dot to distinguish them from machine instructions. Symbolic assemblers let programmers associate arbitrary names (labels or symbols) with memory locations. Usually, every constant and variable is given a name so instructions can reference those locations by name, thus promoting selfdocumenting code. In executable code, the name of each subroutine is associated with its entry point, so any calls to a subroutine can use its name. Inside subroutines, GOTO destinations are given labels. Some assemblers support local symbols which are lexically distinct from normal symbols (e.g., the use of "10$" as a GOTO destination). Some assemblers, such as NASM provide flexible symbol management, letting programmers manage different namespaces, automatically calculate offsets within data structures, and assign labels that refer to literal values or the result of simple computations performed by the assembler. Labels

can also be used to initialize constants and variables with relocatable addresses. Assembly languages, like most other computer languages, allow comments to be added to assembly source code that are ignored by the assembler. Good use of comments is even more important with assembly code than with higher-level languages, as the meaning and purpose of a sequence of instructions is harder to decipher from the code itself. Wise use of these facilities can greatly simplify the problems of coding and maintaining low-level code. Raw assembly source code as generated by compilers or disassemblerscode without any comments, meaningful symbols, or data definitionsis quite difficult to read when changes must be made. [edit]Macros Many assemblers support predefined macros, and others support programmer-defined (and repeatedly re-definable) macros involving sequences of text lines in which variables and constants are embedded. This sequence of text lines may include opcodes or directives. Once a macro has been defined its name may be used in place of a mnemonic. When the assembler processes such a statement, it replaces the statement with the text lines associated with that macro, then processes them as if they existed in the source code file (including, in some assemblers, expansion of any macros existing in the replacement text).

Note that this definition of "macro" is slightly different from the use of the term in other contexts, like the C programming language. C macros created through the #define directive typically are just one line, or a few lines at most. Assembler macro instructions can be lengthy "programs" by themselves, executed by interpretation by the assembler during assembly. Since macros can have 'short' names but expand to several or indeed many lines of code, they can be used to make assembly language programs appear to be far shorter, requiring fewer lines of source code, as with higher level languages. They can also be used to add higher levels of structure to assembly programs, optionally introduce embedded debugging code via parameters and other similar features. Macro assemblers often allow macros to take parameters. Some assemblers include quite sophisticated macro languages, incorporating such high-level language elements as optional parameters, symbolic variables, conditionals, string manipulation, and arithmetic operations, all usable during the execution of a given macro, and allowing macros to save context or exchange information. Thus a macro might generate a large number of assembly language instructions or data definitions, based on the macro arguments. This could be used to generate record-style data structures or "unrolled" loops, for example, or could generate entire algorithms based on complex parameters. An organization using assembly language that has been heavily extended using such a macro suite can be considered to be working in a higher-

level language, since such programmers are not working with a computer's lowest-level conceptual elements. Macros were used to customize large scale software systems for specific customers in the mainframe era and were also used by customer personnel to satisfy their employers' needs by making specific versions of manufacturer operating systems. This was done, for example, by systems programmers working with IBM's Conversational Monitor System / Virtual Machine (VM/CMS) and with IBM's "real time transaction processing" add-ons, Customer Information Control System CICS, and ACP/TPF, the airline/financial system that began in the 1970s and still runs many large computer reservations systems (CRS) and credit card systems today. It was also possible to use solely the macro processing abilities of an assembler to generate code written in completely different languages, for example, to generate a version of a program in COBOL using a pure macro assembler program containing lines of COBOL code inside assembly time operators instructing the assembler to generate arbitrary code. This was because, as was realized in the 1960s, the concept of "macro processing" is independent of the concept of "assembly", the former being in modern terms more word processing, text processing, than generating object code. The concept of macro processing appeared, and appears, in the C programming language, which supports "preprocessor instructions" to set variables, and make conditional tests on their values. Note that

unlike certain previous macro processors inside assemblers, the C preprocessor was notTuring-complete because it lacked the ability to either loop or "go to", the latter allowing programs to loop. Despite the power of macro processing, it fell into disuse in many high level languages (major exceptions being C/C++ and PL/I) while remaining a perennial for assemblers. Macro parameter substitution is strictly by name: at macro processing time, the value of a parameter is textually substituted for its name. The most famous class of bugs resulting was the use of a parameter that itself was an expression and not a simple name when the macro writer expected a name. In the macro: foo: macro a load a*b the intention was that the caller would provide the name of a variable, and the "global" variable or constant b would be used to multiply "a". If foo is called with the parameter a-c, the macro expansion of load a-c*b occurs. To avoid any possible ambiguity, users of macro processors can parenthesize formal parameters inside macro definitions, or callers can parenthesize the input parameters.[9] [edit]Support

for structured programming

Some assemblers have incorporated structured programming elements to encode execution flow. The earliest example of this approach was in the Concept-14 macro set, originally proposed by Dr. H.D. Mills (March, 1970), and implemented by Marvin Kessler at IBM's Federal Systems Division, which extended the S/360 macro assembler with

IF/ELSE/ENDIF and similar control flow blocks.[10] This was a way to reduce or eliminate the use of GOTO operations in assembly code, one of the main factors causing spaghetti code in assembly language. This approach was widely accepted in the early '80s (the latter days of large-scale assembly language use). A curious design was A-natural, a "stream-oriented" assembler for 8080/Z80 processors[citation needed] from Whitesmiths Ltd.(developers of the Unix-like Idris operating system, and what was reported to be the first commercial C compiler). The language was classified as an assembler, because it worked with raw machine elements such as opcodes, registers, and memory references; but it incorporated an expression syntax to indicate execution order. Parentheses and other special symbols, along with block-oriented structured programming constructs, controlled the sequence of the generated instructions. A-natural was built as the object language of a C compiler, rather than for hand-coding, but its logical syntax won some fans. There has been little apparent demand for more sophisticated assemblers since the decline of large-scale assembly language development.[11] In spite of that, they are still being developed and applied in cases where resource constraints or peculiarities in the target system's architecture prevent the effective use of higherlevel languages.[12] [edit]Use

of assembly language
perspective

[edit]Historical

Assembly languages date to the introduction of the storedprogram computer. The EDSAC computer (1949) had an assembler calledinitial orders featuring one-letter mnemonics.[13] Nathaniel Rochester wrote an assembler for an IBM 701 (1954). SOAP (Symbolic Optimal Assembly Program) (1955) was an assembly language for the IBM 650 computer written by Stan Poley.[14] Assembly languages eliminated much of the error-prone and time-consuming first-generation programming needed with the earliest computers, freeing programmers from tedium such as remembering numeric codes and calculating addresses. They were once widely used for all sorts of programming. However, by the 1980s (1990s on microcomputers), their use had largely been supplanted by high-level languages[citation needed], in the search for improved programming productivity. Today assembly language is still used for direct hardware manipulation, access to specialized processor instructions, or to address critical performance issues. Typical uses are device drivers, low-level embedded systems, and real-time systems. Historically, a large number of programs have been written entirely in assembly language. Operating systems were entirely written in assembly language until the introduction of the Burroughs MCP (1961), which was written in ESPOL, an Algol dialect. Many commercial applications were written in assembly language as well, including a large amount of the IBM mainframe software written by large

corporations. COBOL, FORTRAN and some PL/I eventually displaced much of this work, although a number of large organizations retained assembly-language application infrastructures well into the '90s. Most early microcomputers relied on hand-coded assembly language, including most operating systems and large applications. This was because these systems had severe resource constraints, imposed idiosyncratic memory and display architectures, and provided limited, buggy system services. Perhaps more important was the lack of first-class high-level language compilers suitable for microcomputer use. A psychological factor may have also played a role: the first generation of microcomputer programmers retained a hobbyist, "wires and pliers" attitude. In a more commercial context, the biggest reasons for using assembly language were minimal bloat (size), minimal overhead, greater speed, and reliability. Typical examples of large assembly language programs from this time are IBM PC DOS operating systems and early applications such as the spreadsheet program Lotus 1-2-3. Even into the 1990s, most console video games were written in assembly, including most games for the Mega Drive/Genesis and the Super Nintendo Entertainment System[citation needed]. According to some industry insiders, the assembly language was the best computer language to use to get the best performance out of the Sega Saturn, a console that was notoriously challenging to develop and

program games for.[15] The popular arcade game NBA Jam (1993) is another example. Assembly language has long been the primary development language for many popular home computers of the 1980s and 1990s (such as theSinclair ZX Spectrum, Commodore 64, Commodore Amiga, and Atari ST). This was in large part because BASIC dialects on these systems offered insufficient execution speed, as well as insufficient facilities to take full advantage of the available hardware on these systems. Some systems, most notably the Amiga, even have IDEs with highly advanced debugging and macro facilities, such as the freeware ASM-One assembler, comparable to that of Microsoft Visual Studio facilities (ASM-One predates Microsoft Visual Studio). The Assembler for the VIC-20 was written by Don French and published by French Silk. At 1,639 bytes in length, its author believes it is the smallest symbolic assembler ever written. The assembler supported the usual symbolic addressing and the definition of character strings or hex strings. It also allowed address expressions which could be combined with addition, subtraction, multiplication, division,logical AND, logical OR, and exponentiation operators.[16] [edit]Current

usage

There have always been debates over the usefulness and performance of assembly language relative to high-level languages. Assembly language has specific niche uses where it is important; see below. But in general, modern optimizing

compilers are claimed[17] to render high-level languages into code that can run as fast as hand-written assembly, despite the counter-examples that can be found.[18][19][20] The complexity of modern processors and memory sub-systems makes effective optimization increasingly difficult for compilers, as well as assembly programmers.[21][22] Moreover, and to the dismay of efficiency lovers, increasing processor performance has meant that most CPUs sit idle most of the time,[citation needed] with delays caused by predictable bottlenecks such as I/Ooperations and paging. This has made raw code execution speed a nonissue for many programmers. There are some situations in which developers might choose to use assembly language:

A stand-alone executable of compact size is required that must execute without recourse to the run-time components or librariesassociated with a high-level language; this is perhaps the most common situation. For example, firmware for telephones, automobile fuel and ignition systems, airconditioning control systems, security systems, and sensors. Code that must interact directly with the hardware, for example in device drivers and interrupt handlers. Programs that need to use processor-specific instructions not implemented in a compiler. A common example is the bitwise rotation instruction at the core of many encryption algorithms. Programs that create vectorized functions for programs in higher-level languages such as C. In the higher-level language

this is sometimes aided by compiler intrinsic functions which map directly to SIMD mnemonics, but nevertheless result in a one-to-one assembly conversion specific for the given vector processor.

Programs requiring extreme optimization, for example an inner loop in a processor-intensive algorithm. Game programmers take advantage of the abilities of hardware features in systems, enabling games to run faster. Also large scientific simulations require highly optimized algorithms, e.g. linear algebra with BLAS[18][23] or discrete cosine transformation (e.g. SIMD assembly version fromx264[24]) Situations where no high-level language exists, on a new or specialized processor, for example. Programs need precise timing such as real-time programs such as simulations, flight navigation systems, and medical equipment. For example, in a fly-bywire system, telemetry must be interpreted and acted upon within strict time constraints. Such systems must eliminate sources of unpredictable delays, which may be created by (some) interpreted languages, automatic garbage collection, paging operations, or preemptive multitasking. However, some higher-level languages incorporate run-time components and operating system interfaces that can introduce such delays. Choosing assembly or lower-level languages for such systems gives programmers greater visibility and control over processing details. cryptographic algorithms that must always take strictly the same time to execute, preventing timing attacks.

Situations where complete control over the environment is required, in extremely high security situations where nothing can be taken for granted. Computer viruses, bootloaders, certain device drivers, or other items very close to the hardware or low-level operating system. Instruction set simulators for monitoring, tracing and debugging where additional overhead is kept to a minimum Reverse-engineering and modifying program files such as existing binaries that may or may not have originally been written in a high-level language, for example when trying to recreate programs for which source code is not available or has been lost, or cracking copy protection of proprietary software. Video games (also termed ROM hacking), which is possible via several methods. The most widely employed is altering program code at the assembly language level. Self modifying code, to which assembly language lends itself well. Games and other software for graphing calculators.[25]

Assembly language is still taught in most computer science and electronic engineering programs. Although few programmers today regularly work with assembly language as a tool, the underlying concepts remain very important. Such fundamental topics as binary arithmetic, memory allocation, stack processing, character set encoding, interrupt processing, and compiler design would be hard to study in detail without a grasp of how a computer operates at the hardware level. Since a

computer's behavior is fundamentally defined by its instruction set, the logical way to learn such concepts is to study an assembly language. Most modern computers have similar instruction sets. Therefore, studying a single assembly language is sufficient to learn: I) the basic concepts; II) to recognize situations where the use of assembly language might be appropriate; and III) to see how efficient executable code can be created from high-level languages. [26] This is analogous to children needing to learn the basic arithmetic operations (e.g., long division), although calculatorsare widely used for all except the most trivial calculations. [edit]Typical

applications

Assembly language is typically used in a system's boot code, (BIOS on IBM-compatible PC systems and CP/M), the lowlevel code that initializes and tests the system hardware prior to booting the OS, and is often stored in ROM. Some compilers translate high-level languages into assembly first before fully compiling, allowing the assembly code to be viewed for debugging and optimization purposes. Relatively low-level languages, such as C, allow the programmer to embed assembly language directly in the source code. Programs using such facilities, such as the Linux kernel, can then construct abstractions using different assembly language on each hardware platform. The

system's portable code can then use these processor-specific components through a uniform interface.

Assembly language is valuable in reverse engineering. Many programs are distributed only in machine code form which is straightforward to translate into assembly language, but more difficult to translate into a higher-level language. Tools such as theInteractive Disassembler make extensive use of disassembly for such a purpose. Assemblers can be used to generate blocks of data, with no high-level language overhead, from formatted and commented source code, to be used by other code.[citation needed]

[edit]Related

terminology

Assembly language or assembler language is commonly called assembly, assembler, ASM, or symbolic machine code. A generation of IBM mainframe programmers called it ALC for Assembly Language Code or BAL[27] for Basic Assembly Language. Calling the language assembler might be considered potentially confusing and ambiguous, since this is also the name of the utility program that translates assembly language statements into machine code. However, this usage has been common among professionals and in the literature for decades.[28] Similarly, some early computers called their assembler their assembly program.[29])

The computational step where an assembler is run, including all macro processing, is termed assembly time. The assembler is said to be "assembling" the source code. The use of the word assembly dates from the early years of computers (cf. short code, speedcode). A cross assembler (see also cross compiler) is an assembler that is run on a computer or operating system of a different type from the system on which the resulting code is to run. Cross-assembling may be necessary if the target system cannot run an assembler itself, as is typically the case for small embedded systems. The computer on which the cross assembler is run must have some means of transporting the resulting machine code to the target system. Common methods involve transmitting an exact byte-by-byte copy of the machine code or an ASCII representation of the machine code in a portable format (such as Motorola orIntel hexadecimal) through a compatible interface to the target system for execution. An assembler directive or pseudo-opcode is a command given to an assembler "directing it to perform operations other than assembling instructions."[1] Directives affect how the assembler operates and "may affect the object code, the symbol table, the listing file, and the values of internal assembler parameters." Sometimes the term pseudo-opcode is reserved for directives that generate object code, such as those that generate data.[30]

A meta-assembler is "a program that accepts the syntactic and semantic description of an assembly language, and generates an assembler for that language." [31]

[edit]List

of assemblers for different computer architectures


Main article: List of assemblers [edit]Further

details

For any given personal computer, mainframe, embedded system, and game console, both past and present, at least one possibly dozens of assemblers have been written. For some examples, see the list of assemblers. On Unix systems, the assembler is traditionally called as, although it is not a single body of code, being typically written anew for each port. A number of Unix variants use GAS. Within processor groups, each assembler has its own dialect. Sometimes, some assemblers can read another assembler's dialect, for example, TASM can read old MASM code, but not the reverse. FASM and NASM have similar syntax, but each support different macros that could make them difficult to translate to each other. The basics are all the same, but the advanced features will differ.[32] Also, assembly can sometimes be portable across different operating systems on the same type of CPU. Calling conventions between operating systems often differ slightly or not

at all, and with care it is possible to gain some portability in assembly language, usually by linking with a C library that does not change between operating systems.[citation needed] An instruction set simulator can process theobject code/ binary of any assembler to achieve portability even across platforms with an overhead no greater than a typical bytecode interpreter.[citation needed] This is similar to use of microcode to achieve compatibility across a processor family. Some higher level computer languages, such as C and Borland Pascal, support inline assembly where sections of assembly code, in practice usually brief, can be embedded into the high level language code. The Forth language commonly contains an assembler used in CODE words. An emulator can be used to debug assembly-language programs. [edit]Example

listing of assembly language

source code
Example: x86, 32 bit, using NASM. Note: this is a subroutine not a complete program. 178 ;ccccccccccccccccccccccccccccccccccccccccccccccc cccccccccccccccccccccccccccccccccccccccccccc 179 ; 180 ; counts a zero terminated ASCII string to determine its size 181 ; in: eax = start address of the zero terminated string

182 ; out: ecx = count = the length of the string 183 184 zstr_count: ; entry point 185 186 00000030 B9FFFFFFFF mov ecx, -1 ; init the loop counter, pre-decrement to compensate for the increment 187 188 .loop: 189 00000035 41 inc ecx ; add 1 to the loop counter 190 191 00000036 803C0800 cmp BYTE [eax + ecx], 0 ; compare the value at the string's [starting memory address Plus the loop offset], to zero 192 0000003A 75F9 jne .loop ; if the memory value is Not Equal to Zero then jump to the label called '.loop' ; otherwise continue to the next line of code. 193 194 .done: 195 ; we don't do a final increment, because even though the count is base 1, we 196 ; do not include the zero terminator in the string's length. 197 0000003C C3 ret ; return to the calling program 198

The above is the List output of NASM, the first column (on the left) is simply the line number in the listing and is otherwise meaningless. This subroutine was extracted from a much larger program, that's why it does not start at zero. The next (second) column is the relative address, in hex, of where the code will be placed in memory. The third column is the actual compiled code. For instance, B9 is the x86 opcode to (load) MOV ECX with an immediate value; the FFFFFFFF is a -1 in 2's complement binary arithmetic (32 bits). The lines with colons are symbolic labels, the labels do not create code, they are a way to tell the assembler that we want to be able to reference those locations. The .done: label is only there for clarity of where the program ends, it does not serve any other purpose. Putting a dot '.' in front of a label is a feature of NASM, it declares the label as local to the subroutine.

This article, which discusses assembly language programming, accompanies the book Embedded Microcomputer Systems: Real Time Interfacing published by Brooks-Cole 1999. This document has four overall parts Overview Syntax (fields, pseudo ops) (this document) Local variables Examples Assembly Language Syntax Programs written in assembly language consist of a sequence of source statements. Each source statement consists of a sequence of ASCII characters ending with a

carriage return. Each source statement may include up to four fields: a label, an operation (instruction mnemonic or assembler directive), an operand, and a comment. The following are examples of an assembly directive and a regular machine instruction.
PORTA Inp equ ldaa $0000 PORTA ; Assembly time constant ; Read data from fixed address I/O data port

An assembly language statement contains the following fields. Label Field can be used to define a symbol Operation Field defines the operation code or pseudo-op Operand Field specifies either the address or the data. Comment Field allows the programmer to document the software. Sometimes not all four fields are present in an assembly language statement. A line may contain just a comment. The first token in these lines must begin with a star (*) or a semicolon (;). For example,
; This line is a comment * This is a comment too * This line is a comment

Instructions with inherent mode addressing do not have an operand field. For example,
label clra deca cli inca comment comment comment comment

Recommendation: For small programs, you enable automatic assembly colors. The editor will then color each field according to its type. Recommendation: For large programs, you disable automatic assembly colors, because the system will run too slow. Instead, use the assembler to color the source code. -------------------------------------------------------------------------------------Label Field The label field occurs as the first field of a source statement. The label field can take one of the following three forms: A. An asterisk (*) or semicolon (;) as the first character in the label field indicates that the rest of the source statement is a comment. Comments are ignored by the Assembler, and are printed on the source listing only for the programmer's information. Examples:

* This line is a comment ; This line is also a comment

B. A white-space character (blank or tab) as the first character indicates that the label field is empty. The line has no label and is not a comment. These assembly lines have no labels:
ldaa 0 rmb 10

C. A symbol character as the first character indicates that the line has a label. Symbol characters are the upper or lower case letters a- z, digits 0-9, and the special characters, period (.), dollar sign ($), and underscore (_). Symbols consist of one to 15 characters, the first of which must be alphabetic or the special characters period (.) or underscore (_). All characters are significant and upper and lower case letters are distinct. A symbol may occur only once in the label field. If a symbol does occur more than once in a label field, then each reference to that symbol will be flagged with an error. The exception to this rule is the set pseudo-op that allows you to define and redefine the same symbol. We typically use set to define the stack offsets for the local variables in a subroutine. For more information see the examples of local variables. Set allows two separate subroutines to re-use the same name for their local variables. With the exception of the equ = and set directives, a label is assigned the value of the program counter of the first byte of the instruction or data being assembled. The value assigned to the label is absolute. Labels may optionally be ended with a colon (:). If the colon is used it is not part of the label but merely acts to set the label off from the rest of the source line. Thus the following code fragments are equivalent:
here: deca bne here

here

deca bne here

A label may appear on a line by itself. The assembler interprets this as set the value of the label equal to the current value of the program counter. A label may occur on a line with a pseudo-op. The symbol table has room for at least 2000 symbols of length 8 characters or less. Additional characters up to 15 are permissible at the expense of decreasing the maximum number of symbols possible in the table.

-------------------------------------------------------------------------------------Operation Field The operation field occurs after the label field, and must be preceded by at least one white-space character. The operation field must contain a legal opcode mnemonic or an assembler directive. Upper case characters in this field are converted to lower case before being checked as a legal mnemonic. Thus 'nop', 'NOP', and 'NoP' are recognized as the same mnemonic. Entries in the operation field may be one of two types: Opcode. These correspond directly to the machine instructions. The operation code includes any register name associated with the instruction. These register names must not be separated from the opcode with any white-space characters. Thus 'clra' means clear accumulator A, but 'clr a' means clear memory location identified by the label 'a'. The available instructions depend on the microcomputer you are using Directive. These are special operation codes known to the Assembler that control the assembly process rather than being translated into machine instructions. The pseudoop codes supported by this assembler are
Group A Group B Group C

org =

org equ set dc.b db fcb fcc dc.w dw fdb dc.l dl ds ds.b ds.w ds.l end rmb

.org

meaning Specific absolute address to put subsequent object code Define a constant symbol Define or redefine a constant symbol

.byte .word .long .blkb .blkw .blkl .end

Allocate byte(s) of storage with initialized values Create an ASCII string (no termination character) Allocate word(s) of storage with initialized values Allocate 32 bit long word(s) of storage with initialized values Allocate bytes of storage without initialization Allocate bytes of storage without initialization Allocate 32 bit words of storage without initialization Signifies the end of the source code (TExaS ignores these)

end

-------------------------------------------------------------------------------------Operand Field The operand field's interpretation is dependent on the contents of the operation field. The operand field, if required, must follow the operation field, and must be preceded by at least one white-space character. The operand field may contain a

symbol, an expression, or a combination of symbols and expressions separated by commas. There can be no white-spaces in the operand field. For example the following two lines produce identical object code because of the space between data and + in the first line:
ldaa ldaa data data + 1

The operand field of machine instructions is used to specify the addressing mode of the instruction, as well as the operand of the instruction. The following table summarizes the operand field formats.
Operand Format no operand accumulator and inherent <expression> direct, extended, or relative #<expression> immediate <expression>,R indexed with address register <expr>,<expr> bit set or clear <expr>,<expr>,<expr> bit test and branch <expr>,R,<expr>,<expr> bit test and branch 6811/6812example clra ldaa 4 ldaa #4 ldaa 4,x bset 4,#$01 brset 4,#$01,there brset 4,x,#$01,there

The valid syntax of the operand field depends on the microcomputer. For a detailed explanation of the instructions and their addressing modes, see the help system with the TExaS application. -------------------------------------------------------------------------------------Expressions. An expression is a combination of symbols, constants, algebraic operators, and parentheses. The expression is used to specify a value that is to be used as an operand. Expressions may consist of symbols, constants, or the character '*' (denoting the current value of the program counter) joined together by one of the operators: + - * / %&|^. + add - subtract * multiply / divide % remainder after division & bitwise and | bitwise or ^ bitwise exclusive or

Expressions may include parentheses and other expressions. Expressions are evaluated using the standard arithmetic precedence. Evaluation occurs left to right for multiple operations with the same precedence. Arithmetic is carried out in signed 32bit twos-complement integer precision (on the IBM PC).
Precedence Highest 2 3 lowest operation parentheses unary + - ~ binary * / % & binary + - ^ |

Each symbol is associated with a 16-bit integer value that is used in place of the symbol during the expression evaluation. The asterisk (*) used in an expression as a symbol represents the current value of the location counter (the first byte of a multibyte instruction) Constants represent quantities of data that do not vary in value during the execution of a program. Constants may be presented to the assembler in one of four formats: decimal, hexadecimal, binary, or ASCII. The programmer indicates the number format to the assembler with the following prefixes: 0x hexadecimal, C syntax % binary 'c' ASCII code for a single letter c Unprefixed constants are interpreted as decimal. The assembler converts all constants to binary machine code and are displayed in the assembly listing as hexadecimal. A decimal constant consists of a string of numeric digits. The value of a decimal constant must fall in the range 0-65535, inclusive. The following example shows both valid and invalid decimal constants:
VALID 12 12345 INVALID 123456 12.3 REASON INVALID more than 5 digits invalid character

A hexadecimal constant consists of a maximum of four characters from the set of digits (0-9) and the upper case alphabetic letters (A-F), and is preceded by a dollar sign ($). Hexadecimal constants must be in the range $0000 to $FFFF. The following example shows both valid and invalid hexadecimal constants:

VALID $12 $ABCD $001F

INVALID ABCD $G2A $2F018

REASON INVALID no preceding "$" invalid character too many digits

A binary constant consists of a maximum of 16 ones or zeros preceded by a percent sign (%). The following example shows both valid and invalid binary constants:
VALID %00101 %1 %10100 INVALID 1010101 %10011000101010111 %210101 REASON INVALID missing percent too many digits invalid digit

A single ASCII character can be used as a constant in expressions. ASCII constants are surrounded by a single quotes ('). Any character, except the single quote, can be used as a character constant. The following example shows both valid and invalid character constants:
VALID '*' INVALID 'VALID' REASON INVALID too long

For the invalid case above the assembler will not indicate an error. Rather it will assemble the first character and ignore the remainder. -------------------------------------------------------------------------------------Comment Field The last field of an Assembler source statement is the comment field. This field is optional and is only printed on the source listing for documentation purposes. The comment field is separated from the operand field (or from the operation field if no operand is required) by at least one white-space character. The comment field can contain any printable ASCII characters. As software developers, our goal is to produce code that not only solves our current problem, but can serve as the basis of our future problems. In order to reuse software we must leave our code in a condition such that future programmer (including ourselves) can easily understand its purpose, constraints, and implementation. Documentation is not something tacked onto software after it is done, but rather a discipline built into it at each stage of the development. We carefully develop a programming style providing appropriate comments. I feel a comment that tells us why we perform certain functions is more informative than comments that tell us what the functions are. An examples of bad comments would be:
clr Flag Flag=0

sei ldaa $1003

Set I=1 Read PortC

These are bad comments because they provide no information to help us in the future to understand what the program is doing. An example of good comments would be:
clr Flag sei ldaa $1003 Signifies no key has been typed The following code will not be interrupted Bit7=1 iff the switch is pressed

These are good comments because they make it easier to change the program in the future. Self-documenting code is software written in a simple and obvious way, such that its purpose and function are self-apparent. To write wonderful code like this, we first must formulate the problem organizing it into clear well-defined subproblems. How we break a complex problem into small parts goes a long way making the software self-documenting. Both the concept of abstraction (introduced in the last section) and modular code (to be presented in the next section) address this important issue of software organization. Maintaining software is the process of fixing bugs, adding new features, optimizing for speed or memory size, porting to new computer hardware, and configuring the software system for new situations. It is the MOST IMPORTANT phase of software development. My personal opinion is that flowchart charts or software manuals are not good mechanisms for documenting programs because it is difficult to keep these types of documentation up to date when modifications are made. We should use careful indenting, and descriptive names for variables, functions, labels, I/O ports. Liberal use of equ provide explanation of software function without cost of execution speed or memory requirements. A disciplined approach to programming is to develop patterns of writing that you consistently follow. Software developers are unlike short story writers. It is OK to use the same subroutine outline over and over again. In the following program, notice the following style issues: 1) Begins and ends with a line of *'s 2) States the purpose of the subroutine 3) Gives the input/output parameters, what they mean and how they are passed 4) Different phases (submodules) of the code delineated by a line of -'s
******************* Max ******************************* * Purpose: returns the maximum of two 16 bit numbers * This subroutine creates three 16 bit local variables * Inputs: Num1 and Num2 are two 16 bit unsigned numbers

* passed in on the stack * Output: RegX is the maximum of X,Y * Destroyed: CCR * Calling sequence * ldx #100 * pshx Num1 pushed on stack * ldx #200 * pshx Num2 pushed on stack * jsr Max * puly Balance stack * puly Result in RegX First set 0 The first 16 bit local variable Second set 2 The second 16 bit local variable Result set 4 The Maximum of first,second Num1 set 12 Input parameter1 Num2 set 10 Input parameter2 Max pshy Save registers, that will be modified * - - - - - - - - - - - - - - - - - - - - - - - - - - pshx Allocate Result local variable pshx Allocate Second local variable pshx Allocate First local variable * - - - - - - - - - - - - - - - - - - - - - - - - - - tsx Create stack frame pointer ldy Num1,X sty First,X Initialize First=Num1 ldy Num2,X sty Second,X Initialize Second=Num2 ldy First,X sty Result,X Guess that Result=First cpy Second,X bhs MaxOK Skip if First>=Second ldy Second,X Since First<Second sty Result,X make Result=Second MaxOK ldx Result,X Return Result in RegX * - - - - - - - - - - - - - - - - - - - - - - - - - - puly Deallocate local variables puly puly * - - - - - - - - - - - - - - - - - - - - - - - - - - puly Restore registers rts ****************** End of Max *****************************

-------------------------------------------------------------------------------------Assembly Listing The assembler output includes an optional listing of the source program and an object files. The listing file is created when the TheList.RTF file is open. Each line of the listing contains a reference line number, the address and bytes assembled, and the original source input line. If an input line causes more than 8 bytes to be output (e.g., a long FCC directive), the additional bytes are included in the object code (S19 file or loaded into memory) but not shown in the listing. There are

three assembly options, each can be toggled on/off using the Assembly->Options command.
(4) [100] {PPP} cycles total type shows the number of cycles to execute this instruction gives a running cycle total since last org pseudo-op gives the cycle type

The codes used in the cycle type are different for each microcomputer The assembly listing may optionally contain a symbol table. The symbol table is included at the end of the assembly listing if enabled. The symbol table contains the name of each symbol, along with its defined value. Since the set pseudo-op can be used to redefine the symbol, the value in the symbol table is the last definition. -------------------------------------------------------------------------------------Assembly Errors Programming errors fall into two categories. Simple typing/syntax error will be flagged by the TExaS assembler as an error when the assembler tries to translate source code into machine code. The more difficult programming errors to find and remove are functional bugs that can be identified during execution, when the program does not perform as expected. Error messages are meant to be self-explanatory. The assembler has a verbose (see Assembler->Options command) mode that provides more details about the error and suggests possible solutions. Assembler Error Types 1) Undefined symbol: Program refers to a label that does not exist How to fix: check spelling of both the definition and access 2) Undefined opcode or pseudo-op How to fix: check the spelling/availability of the instruction 3) Addressing mode not available How to fix: look up the addressing modes available for the instruction 4) Expression error How to fix: check parentheses, start with a simpler expression 5) Phasing Error occurs when the value of a symbol changes from pass1 to pass2 How to fix: first remove any undefined symbols, then remove forward references 6) Address error How to fix: use org pseudo-ops to match available memory. Error diagnostic messages are placed in the listing file just after the line containing the error. If there is no TheList.RTF file, then assembly errors are reported

inTheLog.RTF file. If there is neither TheList.RTF or TheLog.RTF files, then assembly errors are not reported. -------------------------------------------------------------------------------------Phasing errors A phasing error occurs during Pass 2 of the assembler when the address of a label is different than when it was previously calculated. The purpose of Pass 1 of the assembler is to create the symbol table. In order to calculate the address of each assembly line, the assembler must be able to determine the exact number of bytes each line will take. For most instructions, the number of bytes required is fixed and easy to calculate, but for other instructions, the number of bytes can vary. A phasing errors occur when the assembler calculates the size of an instruction different in Pass 2 than previously calculated in Pass 2. Sometimes a phasing error often occurs on a line further down in the program than where the mistake occurs. A phasing error usually results from the use of forward references. In this 6812 example the symbol "index" is not available at the time of assembling the ldaa index,x. The assembler incorrectly chooses the 2 byte IDX addressing mode version rather than the correct 3 byte IDX1 mode.
ldaa index,x index equ 100 ; ... loop ldaa #0

The listing shows the phasing error


Copyright 1999-2000 Test EXecute And Simulate $0000 A6E064 ldaa index,x $0064 index equ 100 ; ... $0003 8600 loop ldaa #0 ##### Phasing error This line was at address $0002 in pass 1, now in pass 2 it is $0003 ***************Symbol Table********************* index $0064 loop $0002 ##### Assembly failed, 1 errors!

When the assembler gets to loop, the Pass 1 and Pass 2 values are off by one causing a phasing error at the loop ldaa #0 instruction. The solution here to simply put the index equ 100 first.

-------------------------------------------------------------------------------------Assembler pseudo-op's

Pseudo-op's are specific commands to the assembler that are interpreted during the assembly process. A few of them create object code, but most do not. There are two common formats for the pseudo-op's used when developing Motorola assembly language. The TExaS assembler supports both categories. If you plan to export software developed with TExaS to another application, then you should limit your use only the psuedo-op's compatible with that application. Group A is supported by Motorola's MCUez, HiWare and ImageCraft's ICC11 and ICC12 Group B is supported by Motorola's DOS level AS05, AS08, AS11 and AS12 Group C are some alternative definitions
Group A Group B Group C

org =

org equ set dc.b db fcb fcc dc.w dw fdb dc.l dl ds ds.b ds.w ds.l end rmb

.org

meaning Specific absolute address to put subsequent object code Define a constant symbol Define or redefine a constant symbol

.byte .word .long .blkb .blkw .blkl .end

Allocate byte(s) of storage with initialized values Create an ASCII string (no termination character) Allocate word(s) of storage with initialized values Allocate 32 bit long word(s) of storage with initialized values Allocate bytes of storage without initialization Allocate bytes of storage without initialization Allocate 32 bit words of storage without initialization Signifies the end of the source code (TExaS ignores these)

end

-------------------------------------------------------------------------------------equ equate symbol to a value <label> equ <expression> (<comment>) <label> = <expression> (<comment>) The EQU (or =) directive assigns the value of the expression in the operand field to the label. The equ directive assigns a value other than the program counter to the label. The label cannot be redefined anywhere else in the program. The expression cannot contain any forward references or undefined symbols. Equates with forward references are flagged with Phasing Errorsphasing_error. In the following example, the local variable names can not be reused in another subroutine:
; MC68HC812A4 ; *****binding phase***************

I equ -4 PT equ -3 Ans equ -1 ; *******allocation phase ********* function pshx save old Reg X tsx create stack frame pointer leas -4,sp allocate four bytes for I,PT,Result ; ********access phase ************ clr I,x Clear I ldy PT,x Reg Y is a copy of PT staa Ans,x store into Ans ; ********deallocation phase ***** txs deallocation pulx restore old X rts

In the following example, the equ pseudo-op is used to define the I/O ports and to access the various elements of the linked structure.
* ***********Moore.RTF********************* * Jonathan W. Valvano 7/18/98 10:54:28 PM * Moore Finite State Machine Controller * PC1,PC0 are binary inputs, PB1,PB0 are binary outputs PORTB equ 0x01 DDRB equ 0x03 PORTC equ 0x04 DDRC equ 0x06 TCNT equ 0x84 ; 16 bit unsigned clock, incremented each cycle TSCR equ 0x86 ; set bit 7=1 to enable TCNT * Finite State Machine Controller * C1,C0 are inputs B1,B0 are outputs org $800 variables go in RAM StatePt rmb 2 Pointer to the current state org $F000 Put in EEPROM so it can be changed Out equ 0 offset for output value * 2 bit pattern stored in the low part of an 8 bit byte Wait equ 1 offset for time to wait Next equ 2 offset for 4 next states * Four 16 bit unsigned absolute addresses InitState fdb S1 Initial state S1 fcb %01 Output fcb 5 Wait Time fdb S2,S1,S2,S3 S2 fcb %10 Output fcb 10 Wait Time fdb S3,S1,S2,S3 S3 fcb %11 Output fcb 20 Wait Time fdb S1,S1,S2,S1 org $F800 programs go in ROM Main lds #$0C00 movb #$FF,TSCR enable TCNT ldaa #%11111111 staa DDRB B1,B0 are LED outputs ldaa #%00000000 staa DDRC C1,C0 are switch inputs ldx InitState State pointer

stx StatePt * Purpose: run the FSM * 1. Perform output for the current state * 2. Wait for specified amout of time * 3. Input from the switches * 4. Go to the next state depending on the input * StatePt is the current state pointer FSM ldx StatePt 1. Do output ldab Out,x Output value for this state in bits 1,0 stab PORTB ldaa Wait,x 2. Wait in this state bsr WAIT ldab PORTC 3. Read input andb #$03 just interested in bits 1,0 lslb 2 bytes per 16 bit address abx add 0,2,4,6 depending on input ldx Next,x 4. Next state depending on input stx StatePt bra FSM * Reg A is the time to wait (256 cycles each) WAIT tfr a,b clra RegD= number of cycles to wait addd TCNT TCNT value at the end of the delay Wloop cpd TCNT EndT-TCNT<0 when EndT<Tcnt bpl Wloop rts org $FFFE fdb Main reset vector

-------------------------------------------------------------------------------------set equate symbol to a value <label> set <expression> (<comment>) The SET directive assigns the value of the expression in the operand field to the label. The set directive assigns a value other than the program counter to the label. Unlike the equ pseudo-op, the label can be redefined anywhere else in the program. The expression should not contain any forward references or undefined symbols. The use of this pseudo-op with forward references will not be flagged with Phasing Errors. In the following example, the local variable names could be reused in another subroutine:
; MC68HC812A4 ; *****binding phase*************** I set -4 PT set -3 Ans set -1 ; *******allocation phase ********* function pshx save old Reg X tsx create stack frame pointer leas -4,sp allocate four bytes for I,PT,Result ; ********access phase ************

clr I,x Clear I ldy PT,x Reg Y is a copy of PT staa Ans,x store into Ans ; ********deallocation phase ***** txs deallocation pulx restore old X rts

-------------------------------------------------------------------------------------fcb Form Constant Byte (<label>) fcb <expr>(,<expr>,...,<expr>) (<comment>) (<label>) dc.b <expr>(,<expr>,...,<expr>) (<comment>) (<label>) db <expr>(,<expr>,...,<expr>) (<comment>) (<label>) .byte <expr>(,<expr>,...,<expr>) (<comment>) The FCB directive may have one or more operands separated by commas. The value of each operand is truncated to eight bits, and is stored in a single byte of the object program. Multiple operands are stored in successive bytes. The operand may be a numeric constant, a character constant, a symbol, or an expression. If multiple operands are present, one or more of them can be null (two adjacent commas), in which case a single byte of zero will be assigned for that operand. An error will occur if the upper eight bits of the evaluated operands' values are not all ones or all zeros. A string can be included, which is stored as a sequence of ASCII characters. The delimitors supported by TExaS are " ' and \. The string is not terminated, so the programmer must explicitly terminate it. For example: str1 fcb "Hello World",0 In the following finite state machine the fcb definitions are used to store outputs and wait times.
Out equ 0 offset for output value * 2 bit pattern stored in the low part of an 8 bit byte Wait equ 1 offset for time to wait Next equ 2 offset for 4 next states * Four 16 bit unsigned absolute addresses InitState fdb S1 Initial state S1 fcb %01 Output fcb 5 Wait Time fdb S2,S1,S2,S3 S2 fcb %10 Output fcb 10 Wait Time fdb S3,S1,S2,S3 S3 fcb %11 Output fcb 20 Wait Time fdb S1,S1,S2,S1

-------------------------------------------------------------------------------------fcc Form Constant Character String (<label>) FCC <delimiter><string><delimiter> (<comment>) The FCC directive is used to store ASCII strings into consecutive bytes of memory. The byte storage begins at the current program counter. The label is assigned to the first byte in the string. Any of the printable ASCII characters can be contained in the string. The string is specified between two identical delimiters. The first non-blank character after the FCC directive is used as the delimiter. The delimitors supported by TExaS are " ' and \. Examples: LABEL1 LABEL2 LABEL4 FCC fcc fcc 'ABC' "Jon Valvano " /Welcome to FunCity!/

The first line creates the ASCII characters ABC at location LABEL1. Be careful to position the fcc code away from executable instructions. The assembler will produce object code like it would for regular instructions, one line at a time. For example the following would crash because after executing the LDX instruction, the 6811 would try to execute the ASCII characters "Trouble" as instructions.
ldaa 100 ldx #Strg Strg fcc "Trouble"

Typically we collect all the fcc, fcb, fdb together and place them at the end of our program, so that the microcomputer does not try to execute the constant data. For example
ldaa Con8 ldy Con16 ldx #Strg bra loop * Since the bra loop is unconditional, * the 6811 won't go beyond this point. Strg fcc "No Trouble" Con8 fcb 100 Con16 fdb 1000

-------------------------------------------------------------------------------------fdb Form Double Byte

(<label>) fdb <expr>(,<expr>,...,<expr>) (<comment>) (<label>) dc.w <expr>(,<expr>,...,<expr>) (<comment>) (<label>) dw <expr>(,<expr>,...,<expr>) (<comment>) (<label>) .word <expr>(,<expr>,...,<expr>) (<comment>) The FDB directive may have one or more operands separated by commas. The 16-bit value corresponding to each operand is stored into two consecutive bytes of the object program. The storage begins at the current program counter. The label is assigned to the first 16-bit value. Multiple operands are stored in successive bytes. The operand may be a numeric constant, a character constant, a symbol, or an expression. If multiple operands are present, one or more of them can be null (two adjacent commas), in which case two bytes of zeros will be assigned for that operand. In the following finite state machine the fdb definitions are used to define state pointers. E.g., the InitState and the four Next pointers.
Out equ 0 offset for output value * 2 bit pattern stored in the low part of an 8 bit byte Wait equ 1 offset for time to wait Next equ 2 offset for 4 next states * Four 16 bit unsigned absolute addresses InitState fdb S1 Initial state S1 fcb %01 Output fcb 5 Wait Time fdb S2,S1,S2,S3 S2 fcb %10 Output fcb 10 Wait Time fdb S3,S1,S2,S3 S3 fcb %11 Output fcb 20 Wait Time fdb S1,S1,S2,S1

-------------------------------------------------------------------------------------dc.l Define 32 bit constant (<label>) dc.l <expr>(,<expr>,...,<expr>) (<comment>) (<label>) dl <expr>(,<expr>,...,<expr>) (<comment>) (<label>) .long <expr>(,<expr>,...,<expr>) (<comment>) The dl directive may have one or more operands separated by commas. The 32-bit value corresponding to each operand is stored into four consecutive bytes of the object program (big endian). The storage begins at the current program counter. The label is assigned to the first 32-bit value. Multiple operands are stored in successive bytes. The operand may be a numeric constant, a character constant, a symbol, or an expression. If multiple operands are present, one or more of them can be null (two adjacent commas), in which case four bytes of zeros will be assigned for that operand.

In the following finite state machine the dl definitions are used to define 32 bit constants.
S1 S2 S3 dl 100000,$12345678 .long 1,10,100,1000,10000,100000,1000000,10000000 dc.l -1,0,1

-------------------------------------------------------------------------------------org Set Program Counter to Origin org<expression> (<comment>) .org<expression> (<comment>) The ORG directive changes the program counter to the value specified by the expression in the operand field. Subsequent statements are assembled into memory locations starting with the new program counter value. If no ORG directive is encountered in a source program, the program counter is initialized to zero. Expressions cannot contain forward references or undefined symbols. The org statements in the following skeleton place the variables in RAM and the programs in EEPROM of a MC68HC812A4
* ********** <<Name>> ******************** org $800 variables go in RAM * <<globals defined with rmb's go here>> org $F000 programs in EEPROM Main: lds #$0C00 initialize stack to RAM * <<one time initializations go here>> loop: * <<repeated operations go here>> bra loop * <<subroutines go here>> org $FFFE fdb Main reset vector

-------------------------------------------------------------------------------------rmb Reserve Multiple Bytes (<label>) rmb <expression> (<comment>) (<label>) ds <expression> (<comment>) (<label>) ds.b <expression> (<comment>) (<label>) .blkb <expression> (<comment>) The RMB directive causes the location counter to be advanced by the value of the expression in the operand field. This directive reserves a block of memory the length of which in bytes is equal to the value of the expression. The block of memory reserved is not initialized to any given value. The expression cannot contain any

forward references or undefined symbols. This directive is commonly used to reserve a scratchpad or table area for later use. -------------------------------------------------------------------------------------ds.w Reserve Multiple Words (<label>) ds.w <expression> (<comment>) (<label>) .blkw <expression> (<comment>) The ds.w directive causes the location counter to be advanced by 2 times the value of the expression in the operand field. This directive reserves a block of memory the length of which in words (16 bit) is equal to the value of the expression. The block of memory reserved is not initialized to any given value. The expression cannot contain any forward references or undefined symbols. This directive is commonly used to reserve a scratchpad or table area for later use. -------------------------------------------------------------------------------------ds.l Reserve Multiple 32-bit Words (<label>) ds.l <expression> (<comment>) (<label>) .blkl <expression> (<comment>) The ds.l directive causes the location counter to be advanced by 4 times the value of the expression in the operand field. This directive reserves a block of memory the length of which in words (32 bit) is equal to the value of the expression. The block of memory reserved is not initialized to any given value. The expression cannot contain any forward references or undefined symbols. This directive is commonly used to reserve a scratchpad or table area for later use. -------------------------------------------------------------------------------------end End of program (optional) end (<comment>) .end (<comment>) The END directive signifies the end of the source code. The TExaS assembler will ignore these pseudo operation codes. -------------------------------------------------------------------------------------ASCII Character codes

BITS 4 to 6 0 0 B I T S 0 T O 3 1 2 3 4 5 6 7 8 9 A B C D E F NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO S1 1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 2 SP : ! # $ % & ' ( ) * + , . / 3 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 4 @ A B C D E F G H I J K L M N O 5 P Q R S T U V W X Y Z [ \ ] ^ _ 6 ` a b c d e f g h i j k l m n o 7 p q r s t u v w x y z { ; } ~ DEL

-------------------------------------------------------------------------------------S-19 Object code The S-record output format encodes program and data object modules into a printable (ASCII) format. This allows viewing of the object file with standard tools and allows display of the module while transferring from one computer to the next or during loads between a host and target. The S-record format also includes information for use in error checking to insure the integrity of data transfers. S-Records are character strings made of several fields that identify the record type, record length, memory address, code/data, and checksum. Each byte of binary data is encoded as a 2-character hexadecimal number: the first character representing the high-order 4 bits, and the second the low-order 4 bits of the byte. The 5 fields that comprise an S-record are: 1) Type S0, S1 or S9 2) Record Length 3) Address 4) Code/Data 5) Checksum Eight types of S-records have been defined to accommodate various encoding, transportation, and decoding needs, but only three types are used in most Motorola

microcontrollers. The S0 record is a title record containing the ASCII name of the file in the Code/Data field. The address field of this type is usually 0000. The S1 record is a data record containing the information to be loaded sequentially starting at the specified address. The S9 record is a end of file marker, and sometimes contains the starting address to begin execution. In an embedded microcomputer environment, the starting address must be programmed at the appropriate place. For most Motorola microcontrollers, the reset vector is the last two bytes of ROM or EEPROM. The Record Length contains the count of the character pairs in the length record, excluding the type and record length. For S0, S1, S9 record types, the Address field is a 4-byte value. For the S1 record type the address specifies where the data field is to be loaded into memory. There are from 0 to n bytes in the Code/Data field. This information contains executable code, memory loadable data, or descriptive information. The Checksum field is 2 ASCII characters used for error checking. The least significant byte of the one's complement of the sum of the values represented by the pairs of characters making up the record length, address, and the code/data fields. When generating a checksum, one adds (call the result sum) the record length, address and code/data field using 8 bit modulo arithmetic (ignoring overflows.) The checksum is calculated checksum = $FF - sum When verifying a checksum, one adds (call the result sum) the record length, address code/data field and checksum using 8 bit modulo arithmetic (ignoring overflows.) The sum should be $FF. Each record may be terminated with a CR/LF/NULL. The following is a typical S-record module: S1130000285F245F2212226A000424290008237C2A S11300100002000800082629001853812341001813 S113002041E900084E42234300182342000824A952 S107003000144ED492 S9030000FC The module consists of four code/data records and an S9 termination record. The first S1 code/data record is explained as follows:

S1 S-record type S1, indicating a code/data record to be loaded/verified at a 2-byte address. 13 Hex 13 (decimal 19), indicating 19 character pairs, representing 19 bytes of binary data, follow. 00 Four-character 2-byte address field: hex address 0000, indicates location where the following data is to be loaded. The next 16 character pairs are the ASCII bytes of the actual program code/data 2A Checksum of the first S1 record. The second and third S1 code/data records each also contain $13 character pairs and are ended with checksums. The fourth S1 code/data record contains 7 character pairs. The S9 termination record is explained as follows: S9 S-record type S9, indicating a termination record. 03 Hex 03, indicating three character pairs (3 bytes) to follow. 00 Four character 2-byte address field, zeroes. 00 FC Checksum of S9 record. This document has four overall parts Overview Syntax (fields, pseudo ops) (this document) Local variables Examples

Early computer systems were literally programmed by hand. Front panel switches were used to enter instructions and data. These switches represented the address, data and control lines of the computer system.To enter data into memory, the address switches were toggled to the correct address, the data switches were toggled next, and finally the WRite switch was toggled. This wrote the binary value on the front panel data switches to the address specified. Once all the data and instruction were entered, the run switch was toggled to run the program.

The programmer also needed to know the instruction set of the processor. Each instruction needed to be manually converted into bit patterns by the programmer so the front panel switches could be set correctly. This led to errors in translation as the programmer could easily misread 8 as the value B. It became obvious that such methods were slow and error prone. With the advent of better hardware which could address larger memory, and the increase in memory size (due to better production techniques and lower cost), programs were written to perform some of this manual entry. Small monitor programs became popular, which allowed entry of instructions and data via hex keypads or terminals. Additional devices such as paper tape and punched cards became popular as storage methods for programs. Programs were still hand-coded, in that the conversion from mnemonics to instructions was still performed manually. To increase programmer productivity, the idea of writing a program to interpret another was a major breakthrough. This would be run by the computer, and translate the actual mnemonics into instructions. The benefits of such a program would be

reduced errors faster translation times changes could be made easier and faster

As programmers were writing the source code in mnemonics anyway, it seemed the logical next step. The source file was fed as input into the program, which translated the mnemonics into instructions, then wrote the output to the desired place (paper-tape etc). This sequence is now accepted as common place. The only advances have been the increasing use of high level languages to increase programmer productivity. Assembly language programming is writing machine instructions in mnemonic form, using an assembler to convert these mnemonics into actual processor instructions and associated data. The disadvantages of assembly language programming are

the programmer requires knowledge of the processor architecture and instruction set many instructions are required to achieve small tasks source programs tend to be large and difficult to follow

programs are machine dependent, requiring complete rewrites if the hardware is changed

THE PROGRAM TRANSLATION SEQUENCE developing a software program to accomplish a particular task, the implementor chooses an appropriate language, develops the algorithm (a sequence of steps, which when carried out in the order prescribed, achieve the desired result), implements this algorithm in the chosen language (coding), then tests and debugs the final result. here is also a probable maintenance phase also associated. The chosen language will undoubtably need to be converted into the appropriate binary bit-patterns which make sense to the target processor (the processor on which the software will be run). This process of conversion is called translation. The following diagram illustrates the translation sequence necessary to generate machine code from specific languages.

ASSEMBLY LANGUAGE PROGRAMMING Asemblers are programs which generate machine code instructions from a source code program written in assembly language. The features provided by an assembler are,

allows the programmer to use mnemonics when writing source code programs. variables are represented by symbolic names, not as memory locations symbolic code is easier to read and follow error checking is provided changes can be quickly and easily incorporated with a re-assembly programming aids are included for relocation and expression evaluation

In writing assembly language programs for micro-computers, it is essential that a standardized format be followed. Most manufacturers provide assemblers, which are

programs used to generate machine code instructions for the actual processor to execute. The assembler converts the written assembly language source program into a format which run on the processor. Each machine code instruction (the binary or hex value) is replaced by a mnemonic. A mnemonic is an abbreviation which represents the actual instruction.
+----------+---------+-----------------+ | Binary | Hex | Mnemonic | +----------+---------+-----------------+ | 01001111 | 4F | CLRA | Clears the A accumulator +----------+---------+-----------------+ | 00110110 | 36 | PSHA | Saves A acc on stack +----------+---------+-----------------+ | 01001101 | 4D | TSTA | Tests A acc for 0 +----------+---------+-----------------+

Mnemonics are used because they


are more meaningful than hex or binary values reduce the chances of making an error are easier to remember than bit values

Assemblers also accept certain characters as representing number bases and addressing modes.
$ prefix or h suffix for hexadecimal $24 or 24h D for decimal numbers 24D 67 B for binary numbers 0101111B O or Q for octal numbers 377O 232Q # for immediate addressing LDAA #$34

,X for indexed addressing LDAA 01,X

Assembly language statements are written one per line. A machine code program thus consists of a sequence of assembly language statements, where each statement contains a mnemonic. Each line of an assembly language program is split into four fields, as shown below
LABEL OPCODE OPERAND COMMENTS

The label field is optional. A label is an identifier (or text string symbol). Labels are used extensively in programs to reduce reliance upon programmers remembering where data or code is located. A label can be used to refer to< a memory location the value of a piece of data the address of a program, subroutine, code portion etc. The maximum length of a label differs between assemblers. Some accept up to 32 characters long, others only four characters. A label, when declared, is suffixed by a colon, and begins with a valid character (A..Z). Consider the following example.
START: LDAA #24H

Here, the label START is equal to the address of the instruction LDAA #24H. The label is used in the program as a reference, eg,
JMP START

This would result in the processor jumping to the location (address) associated with the label START, thus executing the instruction LDAA #24H immediately after the JMP instruction. When a label is referenced later on in the program, it is done so without the colon suffix. An advantage of using labels is that inserting or re-arranging code statements do not necessitate re-working actual machine instructions. A simple re-

assembly is all that is required. In hand-coding, such changes can take hours to perform. Each instruction consists of an opcode and possible one or more operands. In the above instruction
JMP START

the opcode is JMP and the operand is the address of the label START. The opcode field contains a mnemonic. Opcode stands for operation code, ie, a machine code instruction. The opcode may also require additional information (operands). This additional information is separated from the opcode by using a space (or tab stop). The operand field consists of additional information or data that the opcode requires. In certain types of addressing modes, the operand is used to specify
o o o o

constants or labels immediate data data contained in another accumulator or register an address

Examples of operands are


TAB ; operand specified by opcode LDAA 0100H ; two byte operand LDAA START ; label operand LDAA #0FH ; immediate operand

The comment field is optional, and is used by the programmer to explain how the coded program works. Comments are preceded by a semi-colon. The assembler, when generating instructions from the source file, ignores all comments. Consider the following examples,

; H means hexadecimal values ORG 0100H ;This program starts at address 0100 hex STATUS: DFB 23H ;This byte is identified as STATUS, and is ;initialized to a value of 23 hex CODE: LDAA STATUS ;The label called CODE is identified as a

JMP CODE

;machine code instruction which loads the ;A accumulator with the contents of the ;memory location associated with the label ;STATUS, ie, the value 23 ;Jump to the address associated with CODE

Note that the programmer does not need to worry about bit patterns, hex values, and the addresses of STATUS or CODE. The assembler, when fed the above program, will generate the correct code. The code output from the assembler will be,
Memory location 0100 0101 0102 0103 0104 0105 0106 Byte value 23 B6 01 00 7E 01 01

Location 0100 holds the value associated with the label STATUS Locations 0101 to 0103 perform the LDAA STATUS instruction Locations 0104 to 0106 perform the JMP CODE instruction

The statement ORG 0100H in the above program is not a machine code instruction. It is an instruction to the assembler, which instructs the assembler to generate the code to run at the designated origin address. Instructions to assemblers are called pseudo-ops. These are used for
o o o o

reserving memory for data variables, arrays and structures determining the start address of the program determining the entry address of the program initializing variable values

The assembler does not generate any machine code instructions for pseudo-ops or comments. Assemblers scan the source program, generating machine instructions. Sometimes, the assembler reaches a reference to a variable which has not yet been defined. This is referred to as a forward reference problem. The assembler can tackle this problem in a number of ways. It is resolved in a two pass assembler as follows,

On the first pass, the assembler simply reads the source file, counting up the number of locations that each instruction will take, and builds a symbol table in memory which lists all the defined variables cross-referenced to their associated memory address. On the second pass, the assembler substitutes opcodes for the mnemonics, and variable names are replaced by the memory locations obtained from the symbol table.

OPERATION OF A TWO-PASS ASSEMBLER Consider the following source code program for a hypothetical computer. The program computes the so-called Fibonacci numbers, printing all such numbers up to that specified by LIMIT.
Line 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Label Operation COPY ZERO COPY ONE READ LIMIT WRITE OLD FRONT: LOAD OLDER ADD OLD STORE NEW SUB LIMIT BRPOS FINAL WRITE COPY COPY BR FINAL: WRITE STOP ZERO: CONST ONE CONST OLDER SPACE OLD SPACE NEW SPACE LIMIT SPACE Operand 1 Operand 2 OLDER OLD

NEW OLD NEW FRONT LIMIT 0 1

OLDER OLD

The instruction set of the computer is as follows,


Operation Code Symbolic Machine Number of Length Operands Action

ADD OPD1 BR BRPOS ACC> 0 COPY LOAD READ stream STOP STORE SUB OPD1) WRITE OPD1

02 00 01 13 03 12 11 07 06 08

2 2 2 3 2 2 1 2 2 2

1 1 1 2 1 1 0 1 1 1

ACC <- ACC + Branch to OPD1 Branch to OPD1 if OPD2 <- OPD1 ACC <- OPD1 OPD1 <- input Halt execution OPD1 <- ACC ACC <- (ACC output stream <-

The functions that the assembler will perform in translating the program are, 9. replace symbolic addresses by numeric addresses 10. replace symbolic operation codes by machine operation codes 11. reserve storage for instructions and data 12. translate constants into machine representation

IMPLEMENTATION The assembler uses two counters to keep track of the machine language program. One counter, called the location counter, keeps track of the physical address location being used, and will initially be set to zero for this program (or the value designated by the ORG directive). The other counter is the line counter, which keeps track of the line number being processed. After each source line has been examined on the first pass, the location counter is incremented by the correct number of bytes. When the assembler processes line 1 of the source, it cannot replace the symbols ZERO and OLDER by their addresses because those symbols have not yet been defined. This is called a forward reference problem. The assembler will place the symbols into the symbol table, determine the number of bytes to advance by altering the contents of the location counter to 3, then proceed to process the next source line. After processing line 3 of the source, the current state will be,

Line 1 2 3

Address Label 0 3 6

Operation COPY COPY READ

OPD1

OPD2 ZERO ONE LIMIT

OLDER OLD

and the contents of the symbol table will be


Symbol Address ZERO --OLDER --ONE --OLD --LIMIT --Location Counter: 8 Line Counter: 4

The symbol table currently holds five symbols, none of which yet has an address. During processing of line 4, the assembler picks up the symbol OLD. It establishes that it is already in the symbol table, so does not enter it again. During line 5, the assembler encounters FRONT, and it is entered into the symbol table. The assembler also knows its address (10), so it is also placed into the table. After processing line 9 of the program, the current state is,
Line 1 2 3 4 5 6 7 8 9 Address Label Operation OPD1 0 COPY 3 COPY 6 READ 8 WRITE 10 FRONT LOAD 12 ADD 14 STORE 16 SUB 18 BRPOS OPD2 ZERO OLDER ONE OLD LIMIT OLD OLDER OLD NEW LIMIT FINAL

and the contents of the symbol table will be


Symbol ZERO OLDER Address -----

ONE --OLD --LIMIT --FRONT 10 NEW --FINAL --Location Counter: 20 Line Counter: 10

The first pass continues, building up the symbol table. When the assembler determines the address of the various symbols in lines 16 to 21, these are entered into the table. At the end of pass 1, the symbol table should list all declared symbols as well as their addresses. The state at the end of the first pass is,
Line 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Address 0 3 6 8 10 12 14 16 18 20 22 25 28 30 32 33 34 35 36 37 38 Label Operation COPY COPY READ WRITE FRONT LOAD ADD STORE SUB BRPOS WRITE COPY COPY BR FINAL WRITE STOP ZERO CONST ONE CONST OLDER SPACE OLD SPACE NEW SPACE LIMIT SPACE OPD1 ZERO ONE LIMIT OLD OLDER OLD NEW LIMIT FINAL NEW OLD NEW FRONT LIMIT 0 1 OPD2 OLDER OLD

OLDER OLD

and the contents of the symbol table will be


Symbol ZERO Address 33

OLDER 35 ONE 34 OLD 36 LIMIT 38 FRONT 10 NEW 37 FINAL 30 Location Counter: 39 Line Counter: 22

Code generation is performed on the second pass. Before starting, the line and location counters will be reset to 1 and 0 respectively. The assembler now generates one line of object code for each source line. Line one is translated to
Address Length Opcode OPD1 00 3 13 33 OPD2 35

Successive lines are translated in the same manner. On encountering the label FRONT in line 5, the assembler ignores it. Lines 16 to 21, where space is reserved for variables, the assembler may leave these undefined, or initialize them to zero. The object code generated by the second pass will be,
Address 00 03 06 08 10 12 14 16 18 20 22 25 28 30 32 33 34 35 36 37 Length Opcode OPD1 3 13 33 3 13 34 2 12 38 2 08 36 2 03 35 2 02 36 2 07 37 2 06 38 2 01 30 2 08 37 3 13 36 3 13 37 2 00 10 2 08 38 1 11 1 00 1 01 1 xx 1 xx 1 xx OPD2 35 36

35 36

38

xx

ASSEMBLER DIRECTIVES As mentioned previously, assembler directives are instructions to the assembler, and are not translated into machine instructions. The use of directives gives the programmer some control over the operation of the assembler, increasing flexibility in the way programs are written. The following is a list of the common pseudo-ops.

EQUATE is used to make programs easier to write. The EQU directive creates absolute symbols and aliases by assigning an expression or value to the declared variable name. Its format is, name: EQU expression Consider the following statement. NUMBER1: EQU 36H The assembler will replace every occurrence of the label NUMBER1 with the value its been equated to, ie, 36 hexadecimal. The statement LDAA #NUMBER1 will be interpreted by the assembler as LDAA #36H An absolute symbol represents a 16bit value; an alias is a name that represents another symbol. The declared name must be unique, one that has not been previously declared. The redefining of a previous symbol is normally not allowed. NUM1: EQU 20H ... ... NUM1: EQU 30H ; error

ORIGIN This specifies the address to be used for the generation of code. Subsequent instructions and data address's begin at the new value. Normally, it is used to set the start address of the program, but can also set the location counter to the value specified.

ORG 120H LDAA #FFH The statement LDAA #FFH begins at byte 120h. ORG $ + 2 start: LDAA #34H The instruction associated with the label start is declared to start at the address 2bytes beyond the current value of the location counter (specified by $).

CPU TYPES This directive is used by multi-purpose assemblers to specify which target processor is being used. The format for CRS8 is CPU cpuname where cpuname consists of a valid processor name, eg CPU 6802 This directive appears before any machine instructions.

OUTPUT FORMATS This directive is used to select the output format for the generated machine instructions. Several output formats are available for downloading into EPROM or a target system. HOF recordtype where recordtype is one of the following MOT ; motorola formats INT ; intel formats TEK ; tektronix formats This directive appears before any machine instructions.

BYTE STORAGE The directive used to allocate and initialize bytes (8bits) of storage is DFB definebyte

Its format is, name: DFB initialvalue,,, The name portion is optional. Consider the following examples for CRS8. value1: DFB 16 form: DFB 6*2 text: DFB "Enter your name: " In the first example, the label value1 is assigned a single byte of storage, which is initialized to 16 decimal.The second example allocates a single byte of storage for the label form, and initializes it equal to 12. The last example allocates 17 bytes of storage for the label text. The first byte will be initialized to E, whilst the last byte is initialized to an ASCII space.

WORD STORAGE The directive used to allocate and initialize words (two bytes) of memory storage is, DWM define word, most significant byte first DWL define word, least significant byte first Its format is, name: DWM initialvalue,,, The name portion is optional. DWM 1687H mess: DWM 'ab' The first example allocates one word of storage, having the values 16H followed by 87H. The second example defines mess as a word initialized with the character values a followed by b. The b will be placed in the low-order byte, and the a will be placed in the high order byte. If only one character is specified, the high-order byte will contain 0. Strings when using the DW directive must not contain more than two characters.

DATA STORAGE RESERVATION This directive is used to reserve storage for later use.

array: DFS 100 This example allocates 100 storage bytes, associating the first byte with the label array. The value of these bytes is indeterminate at this point. The 100 bytes will be allocated relative to the current location counter.

END and Optional Start Address The END directive specifies the end of the assembly language source listing. It may be followed by an optional entry address. The optional entry address is used by LOADERS to initialize the Program Counter before running the program. If no entry address is specified, execution will start at the first location allocated by the assembler. END In this example, the END directive informs the assembler that there is no more source statements. ORG 0100H start: LDAA #3FH JMP start END start In this example, the END directive also specifies that the entry point to the program is the label start, whose address is 0100H.

SAMPLE PROGRAM FOR MC6802 USING CRS8 The following source file has been named MC6802.ASM
CPU HOF ORG DFB EQU DFS ORG LDX LDAB LDAA 6802 ; 6802 processor MOT ; Motorola Records 0100H ; Start of Data 'Hello and Welcome' $ - Source ;Length of Source Length ; Buffer which has same ; length as Source 0120H ; Start of Code #Source ; Point Index Reg to ; Source string #Length ; Number of characters to move 0,X

Source: Length: Destin:

Entry:

Loop:

Fin:

STAA INX DECB BNE JMP END

Length,X

Loop Fin Entry

This program is assembled by typing the following command


CRS8 MC6802

It is not necessary to type the extension .ASM, and CRS8 will produce two output files.
MC6802.PRN MC6802.HEX ; a list file showing the code generated ; the record file for downloading to the ; target system or Eprom programmer

The listing file MC6802.PRN looks like


C:6802.TBL C:6802.HEX CPU HOF 6802 MOT ; 6802 processor ; Motorola Records

0100 ORG 0100H ; Start of Data 0100 48656C6C6F Source: DFB 'Hello and Welcome' 0011 = Length: EQU $ - Source ; Length of Source 0111 Destin: DFS Length ; Buffer which has same ; length as Source 0120 ORG 0120H ; Start of Code 0120 CE0100 Entry: LDX #Source ; Point Index Reg to ; Source string 0123 C611 LDAB #Length ; Number of characters ; to move 0125 A600 Loop: LDAA 0,X 0127 A711 STAA Length,X 0129 08 INX 012A 5A DECB 012B 26F8 BNE Loop 012D 7E012D Fin: JMP Fin 0130 END Entry

The first column is the address, the second the instructions or data, and then the mnemonics and comments. This listing is used by the programmer to verify that the assembler has produced the correct instructions and data at the correct addresses. We can clearly see that it has correctly interpreted the address of Source in the statement LDX #Source as the bytes CE 0100. The record format file MC6802.HEX looks like
S00D0000433A363830322E48455892 S113010048656C6C6F20616E642057656C636F6D1D S10401106585 S1130120CE0100C611A600A711085A26F87E012D9F S9030120DB

The format of a motorola record is


Digit 0,1 Record Type = S0, S1 or S9 2,3 Number of bytes in Record which includes the load address and checksum bytes 4,5,6,7 Load Address 8 to n-2 Data or coded instructions n-1 to n Checksum value The S0 record identifies the program name The S1 record identifies the data and coded instructions The S9 record identifies the program entry point eg S1 04 0110 65 85 ^ ^ ^ ^ ^checksum ^ ^ ^ ^ data ^ ^ ^ load address ^ ^ number of bytes in record ^ record type

The file is then downloaded to the target system.

ELEMENTARY DATA TYPES Most programming languages support data types like characters and integers. At the processor level, some instructions support integer type operations such as multiply or divide (except 6802).The programmer is responsible for keeping track of data types.

The processor treats all data the same, and if the program goes astray, can interpret data as instructions and vise versa. Lets look at how elementary data is represented by the programmer for use in assembly language programs.

Characters are single eight bit values represented using the ASCII code. Values range from 0 to 127. The statement
Letter: DFB 'A'

associates one byte of storage to the variable Letter, initializing it to the ASCII character 'A' (41H).

Character Strings are multiple bytes, each byte holding an ASCII character. The statement
String: DFB 'Hello there.'

allocates 12 bytes of storage space. The variable String has the address of the first byte, which has been allocated the character 'H' (48H).

Integers are stored as 16 bit values (two bytes or one word), and are signed or unsigned. The statement
Number1: DWM -17D

allocates a word of storage for the variable Number1, initializing the word to 17 decimal. Some processors have different instructions for operations on signed and unsigned integers. If the processor cannot handle a 16bit value (ie, has only eight bit registers), software will need to be written to do any comparisons on these types.

Character Arrays are essentially text strings. Each element of the array has storage space for an ASCII character. Strings in some HLL's are terminated with an End-Of-String symbol (in C it is ASCII 00h, in PCDOS it is ASCII '$'). The following statement
Digits: DFS 10

allocates 10 locations for a character based array called Digits. The following code routine initializes the Digits array (each successive element) to the digits 0 to 9.
ORG start: 0120H LDAA LDAB LDX STAA INX INCA DECB BNE ....

loop:

#30H ; ASCII '0' #11 ; ten digits #Digits 0,X ; next element ; next digit loop

exit:

Typical Array Operations The following routines are typical of functions which are performed on character based arrays.

Copy This copys a source string to a destination area. The declaration of the routines is, copystr( src, dest )where src and dest represent the address of the source and destination strings. Writing this type of routine is ideally suited to a processor which has more than one Index or Base register. The MC6802, having only one Index register, presents a small problem. The following code shows this program implemented for the MC6802.
CPU HOF ORG DFS EQU DFS 6802 MOT 100H 10 $ - str1 10

str1: st1len: str2:

ORG 120 HSRCINX:DFS 02H ; pointer for src string DSTINX: DFS 02H ; pointer for dest string start: LDX #str1 ; store address of str1 STX SRCINX LDX #str2 ; store address of str2 STX DSTINX jsr initstr1 ; initialise str1

exit:

jsr bra

copystr ; copy str1 to str2 exit #41H #11 #str1 0,X ; character 'A' ; elements 1 - 30 ; point to str1 ; store character ; next element ; next value ; loop around ; null terminator

initstr1: LDAA LDAB LDX lp1: STAA INX INCA DECB BNE LDAA STAA RTS copystr: LDX cplp2: LDAA CMPA BEQ INX STX LDX STAA INX STX LDX BRA cpstrq: LDX CLR RTS END start1

lp1 #00 0,X

SRCINX 0,X #00H cpstrq SRCINX DSTINX 0,X DSTINX SRCINX cplp2 DSTINX 00,X

; pick up source pointer ; get source character ; eostr? ; yes, then exit ; else inc source pointer ; store source ptr ; get destination pointer ; store character ; inc dest ptr ; store dest ptr ; reload source pointer ; repeat ; Null terminate dest str

String Length Returns the length of a terminated string. The following code shows this routine implemented for the MC6802. Strlen is entered with the Index register pointing to the string, and returns the length of the string in the B accumulator.
CPU HOF EOFSTR: ORG str1: DFB ORG start: LDX exit: bra 6802 MOT EQU 00H 100H 'Hello and Welcome.', 00H 120H #str1 ; point to string jsr strlen ; find length of str1 exit

strlen: strlp1:

LDAB LDAA CMPA BEQ INX INCB BRA RTS END

#00

; character count

exit:

0,X ; read character #EOFSTR ; is it end of string strexit ; yes, then exit ; no, inc str ptr ; inc character count strlp1 ; and repeatstr ; acc B has length start2

Search for first occurrence of a character This routine returns the address of the specified character found in the string. If the address returned is zero, it indicates the character was not found. The following code shows this routine implemented for the MC6802. Strpos is entered with the Index register pointing to the search string, and the A accumulator with the character search value.
CPU HOF EOFSTR: ORG str1: DFB ORG start: LDAA LDX exit: strpos: bra CMPA BEQ CMPA BEQ INX BRA LDX RTS END 6802 MOT EQU 00H 100H 'Hello and Welcome.', 00H 120H #6FH ; ASCII 'o' #str1 ; point to src string jsr strpos ; find first 'o' in str1 exit 0,X strex2 #EOFSTR strex1 strpos #0000H start3 ; is char = search value ; yes then exit ; is it end of string ; yes, then exit ; no, inc str ptr ; and repeat ; not found ; Index reg has address

strex1: strex2:

Search for Substring This routine is used to find the starting address of a substring within a larger string. It accepts a source string pointer, and a pointer to a substring. It returns the address of the substring, if not found it returns address zero.

Substring Insertion/Replacement This routine inserts a substring into an existing string. Most versions overwrite existing characters. It accepts a pointer to the source string, a pointer to the substring to insert, and a numeric value representing the start position where insertion should begin. No characters should overwrite the end-of-string terminator, or be written to memory locations after the terminator. The routine should return a numeric value representing the number of characters inserted. String Reverse This routine reverses the characters in a string. It accepts the address of the string. Hello becomes olleH String to Uppercase This routine converts all characters of a string to uppercase. It accepts the address of the string. Hello becomes HELLO String to Lowercase This routine converts all characters of a string to lowercase. It accepts the address of the string. Hello becomes hello

ARRAY INDEX CALCULATIONS This refers to calculating the address of a specified element within an array. In single dimensioned arrays, this is equivalent to
BASE_ADDRESS + (ELEMENT_NUMBER * NUMBER_OF_BYTES_PER_ELEMENT)

In multi-dimensioned arrays, this is equivalent to


BASE_ADDRESS + (Col_Num + (Row_num * Num_Col_per_row)) * Num_Bytes_per_Element)

IMPLEMENTATION OF HIGH LEVEL LANGUAGE CONSTRUCTS In High Level Languages such as PASCAL and BASIC, several constructs are available which help to implement programs. You should know how these constructs are implemented in assembly language.The constructs that we will now deal with

involve SELECTION and ITERATION. Both types of constructs are implemented using the conditional BRANCH instructions of the processor. These types of instructions test the state of the various flags of the status register. All variables are memory based. Any manipulation of variables normally involves three steps, 1. Load the variable into a register 2. Perform the operation 3. Store the result back into the variables location

SIMPLE STATEMENT ASSIGNMENTS Assigning a constant value to a variable 1. Load the constant into a register 2. Store the register to the variables memory location eg,
3. 4. 5. 6. 7. X1 := 20; LDX STX #20 X1

Use eight bit registers for bytes/characters, and 16bit registers for integers. eg,
Letter := 'Y'; LDAA STAA #'Y' Letter

Assigning a variables value to another variable 8. Load the second variable into a register 9. Store the register into the first variables memory location eg,
10. 11. 12. 13. 14. 15.

X1 := Y; LDX STX Y X1

Addition
X1 := Y + 7 ; Calculate the right side first. ; Load Y into a register, use an immediate add with 7, ; then store into variable X1 (following example uses

; BYTE integers) LDAA Y ADDA #7 STAA X1 eg, X1 := Y + Z

; Calculate the right side first. Load Y and Z into ; registers, add the two registers together, store the ; result into variable X1.

LDAA LDAB ABA STAA

Y Z X1

eg, X1 := Y + Z + 3 + T ; Calculate the right hand side first. If the ; number of variables/constants exceed the number of ; registers available, parenthesise and calculate portions ; at a time. Finally, store the result back into the left ; side variable X1. LDAA T ADDA #3 ;3+T LDAB Z ABA ;+Z LDAB Y ABA ;+Y STAA X1

Subtraction
X1 := Y - 7 ; Calculate the right side first. Load Y into a register, ; use an immediate subtract with 7, then store into ; variable X1.

LDAA SUBA STAA

Y #7 X1

eg, X1 := Y - Z

; Calculate the right side first. Load Y and Z into ; registers, subtract the two registers together, store ; the result into variable X1.

LDAA LDAB SBA STAA

Y Z ; subtract bx from ax, Z from Y X1

eg, X1 := Y - Z - 3 - T ; Calculate the right hand side first. If the ; number of variables/constants exceed the number of ; registers available, parenthesise and calculate portions ; at a time. Finally, store the result back into the left ; side variable X1. Take special note of the order of ; evaluation, in this case Z is subtracted from Y, 3 ; subtracted from that and so on. LDAA Y LDAB Z ABA ;Y-Z SUBA #3 ;-3 LDAB T SBA ;-T STAA X1

Compound Statements
X1 := Y + 4 - Z * 7 ; Calculate the right hand side first. If the ; number of variables/constants exceed the number of registers available, ; parenthesise and calculate portions at a time. Finally, store the result ; back into the left side variable X1. Take special note of the order of ; evaluation, in this case multiplication occurs before addition or ; subtraction. ; The statement can be interpreted as, X1 := (Y + 4) - ( Z * 7 ) ; or X1 := Y + (4 - Z) * 7 ; Assuming that the real intention is the first grouping, first calculate ; the term (Z * 7), then the term (Y + 4), and subtract the first term ; from the second, storing the result into X1. LDAA LDAB mult LDAB ADDB ABA STAA Z #7 A,B Y #4 X1

; ; (Z * 7) ; (Y + 4)

WHERE THE EXPRESSION IS COMPLEX AND INVOLVES A LARGE NUMBER OF TERMS, THIS WILL REQUIRE THE USE OF TEMPORARY STORAGE LOCATIONS FOR STORING INTERMEDIATE RESULTS.

IF STATEMENTS This involves the use of an appropriate branch false instruction after a comparison test to the end of the if body. 1. An IF label with a comparison test 2. Branch false to an endif label 3. The if body statements preceed the endif label
if: ; comparison test ; jump false to endif ; if body statements

endif:

Comparing a variable and a constant 1. Load the variable into a register 2. Compare the register against the constant 3. Branch false to a label after the body of the if statement
IF X1 < 10 then Y := Z * 2; if: LDAA CMPA BCC LDAA LDAB STAA X #10 endif Z 2 Y

; if body, Y := Z * 2 ; mult a, b

endif:

Comparing a Variable against another Variable 1. Load the second variable into a register (t2) 2. Load the first variable into a register (t1) 3. Compare the two registers (t1-t2 > t1) 4. Branch false to a label after the body of the if statement
IF X1 >= Z then Y := X; if: LDAB LDAA CBA BLT LDAA STAA Z X endif X1 Y

; if body, Y := X1;

endif:

Comparing a Variable for Logic 1 or TRUE

1. Load the variable into a register (t1) 2. Compare the register against zero 3. Branch equal to a label after the body of the if statement

IF X1 then Y := X / 2; if: LDAA CMPA BEQ LDAA LDAB STAA X #0 endif X1 #2 Y

; if body, Y:=X1 / 2; ; div A, B

endif:

Comparing a Variable for Logic 0 or FALSE 1. Load the variable into a register (t1) 2. Compare the register against zero 3. Branch above or greater to a label after the body of the if statement
IF X1 then Y := X + 2; if: LDAA CMPA BHI LDAA ADDA STAA X1 #0 endif X1 #2 Y

; if body, Y:=X1 + 2;

endif: WHERE THE CONDITION OF THE IF STATEMENT IS EXPRESSED NEGATIVELY, USING A NOT INSTRUCTION, THEN A BRANCH TRUE INSTRUCTION SHOULD BE USED INSTEAD OF A BRANCH FALSE INSTRUCTION. eg, IF X1 = 2 then Y := 4; if: LDAA CMPA BNE LDAA STAA X1 #2 endif #4 Y

endif:

IF NOT X1 = 2 then Y := 4;

if:

LDAA CMPA BEQ LDAA STAA

X #2 endif #4 Y

endif:

IF THEN ELSE STATEMENTS This involves an extension to the previous IF body. The conditional false branch now jumps to an else clause, and the if body jumps unconditionally to the end of the if else statement.
if: ; comparison ; branch false to else clause ; if body statements jmp endif ; else statements ; endif:

else:

The same principles apply to the various forms that expressions can take. eg,
IF X = 2 THEN Y := Y + 4 ELSE Z := 0; if: LDAA X CMPA #2 BNE else LDAA Y ADDA #4 STAA Y JMP endif LDAA #0 STAA Z

else: endif:

WHILE LOOPS This involves the use of a conditional test at the entry of the while body, which branches or jumps false to an endwhile label. The last statement in the while body is a jump unconditional to the start of the while body. 1. Use a while label, comparison test

2. Branch false to an endwhile label 3. The last statement of the while body is a jump to the while label

while:

; comparison test ; branch false to endwhile ; while body statements jmp while endwhile:

WHILE X < 10 DO BEGIN Y := Y + X; X := X + 1 END;

while:

LDAA CMPA BHI LDAB LDAA ABA STAA LDAA ADDA STAA JMP endwhile:

X #10 endwhile X Y Y X #1 X while

; Y := Y + X

; X := X + 1

PREVIOUS RULES CONCERNING NEGATION ALSO APPLY. NOTE THAT ALL PREVIOUS FUNDAMENTALS OF STATEMENT ASSIGNMENT AND TESTING OF VARIABLES AGAINST EACH OTHER OR CONSTANTS ARE STILL BEING RIGIDLY APPLIED.

FOR NEXT LOOPS 1. Initialise the loop variable 2. Use a for label, Perform the comparison test with the final value 3. Branch false to an endfor label 4. Inside the for loop body, the last statement, should adjust the loop variable, and use an unconditional branch back to the for label
initfor: for: ; initialise loop variable ; comparison test

; jump false endfor ; for body statements ; adjust loop variable for next step jmp for endfor:

FOR X := 1 to 10 do BEGIN Y := Y + X END; initfor: for: LDAA #1 STAA LDAA CMPA BHI LDAB LDAA ABA STAA LDAA ADDA STAA JMP

X X #10 endfor X Y Y X ; NEXT X #1 X for

; Y := Y + X

endfor: PREVIOUS RULES CONCERNING NEGATION ALSO APPLY. NOTE THAT ALL PREVIOUS FUNDAMENTALS OF STATEMENT ASSIGNMENT AND TESTING OF VARIABLES AGAINST EACH OTHER OR CONSTANTS ARE STILL BEING RIGIDLY APPLIED.

6802 Processor Examples 1. The IF statement In comparing the value of operands, consider the following example.
2. 3. 4. IF X = 2 THEN Y = X

The compare statement must be coded in such a way as to compare the value of X against the constant 2. As the variable X is stored in memory, the programmer should first load a register with the variable X before making the

comparison (because most processors do not support a compare between memory contents and immediate data).This example gets coded as,
X: Y: DFB DFB .... LDAA CMPA BNE LDAA STAA ..... 10 00 X #02 IF1 X Y ; load A acc with value of X ; compare A acc with immediate data ; exit if false ; get value of X ; store value of X at variable Y ; next statement after if construct

IF1:

Lets consider another example.


IF X = Y THEN Y = 0

In this case, the code to be generated by the assembler for the compare statement depends upon the addressing modes available. The options available are,
memory to memory compare CMP [X], [Y] register to memory compare CMPA [Y] register to register compare CMPAB

Both X and Y variables are memory based, so if the processor supports a comparison of two memory operands, it could be coded as,
CMP [X],[Y] ; sample only

However, most processors do not support this. The most common option is the comparison of a register variable against memory contents. This is coded as follows,
LDAA X CMPA Y ; get variable X ; compare with variable Y

IF1:

BNE LDAA STAA ....

IF1 #00H Y

; exit it not equal ; set variable Y to zero

This code can clearly be optimized (ie, some instructions can be removed without affecting the original intent of the code). So far we have considered comparisons for equality. The conditional branch instruction will vary depending upon what type of comparison test is used. The following tables illustrate common comparison tests and their associated conditional branch instructions.
+-----------------+-------------+--------------+ | Signed Operands | Branch True | Branch False | +-----------------+-------------+--------------+ |r>m | BGT | BLE | +-----------------+-------------+--------------+ | r >=m | BGE | BLT | +-----------------+-------------+--------------+ |r=m | BEQ | BNE | +-----------------+-------------+--------------+ | r <=m | BLE | BGT | +-----------------+-------------+--------------+ |r<m | BLT | BGE | +-----------------+-------------+--------------+ If ....... Then ---- Use Branch False If NOT ... Then ---- Use Branch True +-----------------+-------------+--------------+ |UnSigned Operands| Branch True | Branch False | +-----------------+-------------+--------------+ |r>m | BHI | BLS | +-----------------+-------------+--------------+ | r >=m | BCC | BCS | +-----------------+-------------+--------------+ |r=m | BEQ | BNE | +-----------------+-------------+--------------+ | r <=m | BLS | BHI | +-----------------+-------------+--------------+ |r<m | BCS | BCC | +-----------------+-------------+--------------+

The following table represents a cross-reference between branch instructions and the flags they test.
+------+----------------+----------+ | 6802 | Flags Tested | 8088 | +------+----------------+----------+ | BCC | C = 0 | JNB, JAE | +------+----------------+----------+ | BCS | C = 1 | JB, JNAE | +------+----------------+----------+ | BNE | Z = 0 | JNE, JNZ | +------+----------------+----------+ | BEQ | Z = 1 | JE, JZ | +------+----------------+----------+ | BPL | N = 0 | JNS | +------+----------------+----------+ | BMI | N = 1 | JS | +------+----------------+----------+ | BHI | C + Z = 0 | JNBE, JA | +------+----------------+----------+ | BLS | C + Z = 1 | JBE, JNA | +------+----------------+----------+ | BGE | N EOR V = 0 | JNL, JGE | +------+----------------+----------+ | BLT | N EOR V = 1 | JL, JNGE | +------+----------------+----------+ | BGT |Z + (N EOR V)= 0| JG, JNLE | +------+----------------+----------+ | BLE |Z + (N EOR V)= 1| JLE,JNG | +------+----------------+----------+

These tables are useful in determining the correct conditional instruction to use for a particular comparison on specific data types. Coding the following statement applicable to two unsigned 8bit data values
IF X <= Y THEN Y = 4 X: Y: IF: DFB DFB 10H 12H

LDAA X CMPA Y BHI IF1

IF1:

LDAA STAA ....

#04H Y

5. The IF THEN ELSE statement In comparing the value of operands, consider the following example.
6. 7. IF X = 2 THEN Y = X ELSE X = Y

This becomes coded as,


X: Y: IF: DFB DFB LDAA CMPA BNE LDAA STAA JMP ELSE1: LDAA STAA IF1: .... 00 00 X #02D ELSE1 X Y IF1 Y X

8. The WHILE WEND statement Consider the following example for unsigned values.
9. 10. 11. 12. WHILE X < 10 DO Y=Y+1X=X+1 WEND

This becomes coded as,


X: Y: DO1: DFB DFB LDAB CMPB BCC LDAA ADDA STAA LDAA ADDA 00H 00H X #10D EXIT1 Y #01 Y X #01

; for signed use BLT ; increment value of Y

; increment value of X

STAA JMP EXIT1: ...

X DO1

Consider the coding of the following HLL program into 6802 assembler.
Program HLLTest(); var loop, val1, val2 : Byte; Begin val1 := 0; val2 := 0; loop := 0; while loop <= 10 do begin val2 := val2 + loop; loop := loop + 1 end; if val1 < val2 then val1 := val2 else val2 := val1 end.

The 6802 assembler version is


; HLLtest.asm CPU 6802 HOF MOT ORG 100h loop: dfb val1: dfb val2: dfb ORG 120h Begin: LDAA STAA LDAA STAA LDAA STAA LDAA CMPA BGT LDAA

0 0 0

While:

#0 val1 #0 val2 #0 loop loop #10 if1 val2

; val1 := 0 ; val2 := 0 ; loop := 0 ; while loop <= 10 do

; val2 := val2 + loop

if1:

Else: Endif:

LDAB ABA STAA LDAA ADDA STAA JMP LDAA CMPA BGE LDAA STAA JMP LDAA STAA NOP SWI End

loop val2 loop #01 loop While val2 val1 Else val2 val1 endif val1 Val2

; loop := loop + 1

; endwhile ; if val1 < val2 then

; val1 := val2

; else val2 := val1

Begin

DATA CONVERSION ROUTINES Computer systems use character based keyboards and displays for inputting and outputting data. Conversion routines are necessary to convert data types to character strings and back again. Consider the entry from a keyboard of an integer value 276. This represents a three character sequence of '2', '7' and '6'. This character sequence will need to be converted into an appropriate 16bit value representing an integer. Also consider displaying the value of a byte as two hex digits. Each nibble must be converted to an ASCII character before displaying on the terminal screen.

HEX BYTE TO ASCII CHARACTERS This routine converts a byte to TWO ASCII characters. eg,
AFH becomes 41H 46H

The algorithm for this is


GET DIGIT MASK OFF HIGH NIBBLE

CONVERT TO ASCII MASK OFF LOW NIBBLE CONVERT TO ASCII

The following code shows an MC6802 implementation.


CPU HOF ORG Val1: DFB Result: DFS ORG Start: LDAA PSHA ANDA LSRA LSRA LSRA LSRA JSR STAA PULA ANDA JSR STAA Exit: BRA Conv: CMPA BLS ADDA ASCZ: ADDA RTS END 6802 MOT 0100H 3FH 02H 120H Val1 #0F0H ; get val1 ; save val1 ; mask high byte ; shift to low nibble

Conv Result

; convert high nibble ; store it

#0FH ; mask low nibble Conv ; convert low nibble Result+1 ; store it Exit #9H ASCZ #07 #30H ; check for digit ; adjust for letter ; adjust to ASCII

Start

ASCII STRING TO HEX BYTE This routine converts a two character sequence into a hexadecimal byte, eg.
41H 46H becomes AFH

The algorithm for implementing this routine is,

Get Digit Subtract 30H from Digit If Digit greater than Nine Subtract 07H from Digit EndIf Shift into High Nibble and Store Get Next Digit Subtract 30H from Next Digit If Next Digit greater than Nine Subtract 07H from Next Digit EndIf OR Next Digit with stored High Nibble and Store

The following code shows an MC6802 implementation.


CPU HOF ORG DFB DFB DFB ORG 6802 MOT 0100H 33H 46H 00H 120H

ASC1: ASC2: HexB:

Start: LDAA SUBA CMPA BLS SUBA ASLA ASLA ASLA ASLA STAA LDAA SUBA CMPA BLS SUBA ORAA STAA END ASC1 #30H #09H If1 #07h ; get first digit

If1:

If2:

HexB ASC2 #30H #09H If2 #07h HexB HexB Start

; get next digit

8BIT MULTIPLY This routine multiplys two 8bit values together generating a 16bit result. The following algorithm (for two unsigned 8bit values) is based on processors which do not have MULTilpy instructions.
Set Product equal to Zero Set Counter equal to Eight While counter not equal to zero Left Shift Product (Multiply by 2) Shift Multiplier so bit goes into Carry If Carry bit is Set Product equals Product plus Multipicand Endif Subtract one from Counter EndWhile

The following program implements this for an MC6802.


CPU HOF ORG Val1: DFB Val2: DFB Result: DFS ORG Start: CLRA CLRB LDX CPX BEQ ASLB ROLA ASL BCC ADDB ADCA DEX BRA STAA STAB BRA END 6802 MOT 0100H 10H 20H 02H 0120H ; product MSB = zero ; product LSB = zero ; multiplier = 8

Shift:

#0008H #0000H Exit

; shift product left 1 bit Val1 Decr Val2 #00H Shift Result Result+1 Finish Start ; shift multiplier left to ; examine next bit ; Add multiplicand to ; product if carry is set ; loop till all 8bits are done

Decr: Exit: Finish:

8BIT DIVIDE This routine divides two 8bit values generating an 8bit quotient and 8bit remainder.The following algorithm (for two unsigned 8bit values) is based on processors which do not have DIVide instructions.
Set Quotient equal to Zero Set Counter equal to Eight While Counter not equal to zero Left Shift Dividend (Multiply by 2) Left Shift Quotient If 8 MSB's of Dividend >= Divisor then MSB of Dividend = MSB of Divident - Divisor Add one to Quotient EndIf Subtract one from Counter EndWhile Remainder = MSB of Dividend

The following program implements this for an MC6802.


CPU HOF ORG DFB DFB DFB DFB ORG 6802 MOT 0100H 10H 20H 00H 00H 0120H #0008H Val1 #0000H Exit

Val1: Val2: Quot: Rem:

; Dividend ; Divisor

Start:

LDX CLRA LDAB Div: CPX BEQ ASLB ROLA CMPA BCS SUBA INCB ChkCnt: DEX BRA Exit: STAB STAA END

; Number of bits in Divisor ; Get Dividend

; Shiftv Dividend and Quotient Val2 ChkCnt Val2 ; is subtraction successful ; Yes, subtract and set bit in quotient

Div Quot Rem Start

ASCII TO INTEGER This routine converts an ASCII character string into a 16bit signed integer value. To implement this routine, the following variables are used.
OFFSET DFB BUFFER BINV DFW BASE DFW ;offset into ASCII string DFS ;space for ASCII string ;integer result ;base 10 value

The algorithm for implementing the routine is,


Position Offset to last character in Buffer Set Base equal to 1 Set BinV equal to zero While Offset is not zero do Get Character stored at Buffer[Offset] If character not '-' sign then Mask out high nibble Multiply by Base value Add result to Binv Base equals Base * 10 Subtract one from Offset Else Set HighBit of BinV to 1 Set Offset equal to zero Endif EndWhile

INTEGER TO ASCII This routine converts a 16bit signed integer into an ASCII character string. To implement this routine, the following variables are used.
OFFSET DFB BUFFER BINV DFW BASE DFW DFS ;offset into ASCII string ;space for ASCII string ;integer value ;base 10 value

The algorithm for implementing the routine is,


Offset equals last position in Buffer

Get BinV Save BinV value for later use While BinV not less than 10 Divide BinV by 10 BinV equals remainder added with 30H Store result at Buffer[Offset] Offset equals Offset - 1 EndWhile Add 30H to BinV Store result at Buffer[Offset] Restore original BinV value If highbit set on BinV Subtract one from Offset Store '-' sign at Buffer[Offset] Endif

PACKED BCD TO DECIMAL This routine converts a two digit packed BCD number into an 8bit decimal number. 93 becomes 5DH. The algorithm for performing this is,
Get Packed BCD Value into Byte Move High Nibble to Low Nibble of Byte Zero High Nibble of Byte Multiply Byte by 10 Add Low Nibble of BCD Value to Byte

The following program shows how this is implemented on the MC6802.


CPU HOF ORG Val1: DFB Val2: DFB Result: DFS PackBCD:DFB DecVal: DFB ORG Start: LDAA LSRA LSRA LSRA LSRA STAA 6802 MOT 0100H 00H 0AH 02H 93H 00H 0110H PackBCD ; shift high nibble to low nibble

; multiply by 10 decimal ; result of Val1 * Val2

Val1

; multiply high nibble by 10

Finish:

JSR LDAA ANDA ADDA STAA BRA

Multiply PackBCD #0FH Result+1 DecVal Finish

; mask high byte ; add to high byte * 10 ; store decimal value

Multiply: CLRA CLRB LDX Shift: CPX BEQ ASLB ROLA ASL BCC ADDB ADCA setDecr: DEX BRA Exit: STAA STAB RTS END

; product MSB = zero ; product LSB = zero #0008H ; multiplier = 8 #0000H Exit ; shift product left 1 bit Val1 Decr Val2 #00H ; shift multiplier left to ; examine next bit ; Add multiplicand to ; product if carry is

Shift ; loop till all 8bits are done Result Result+1 Start

MODERN 16 BIT MICROPROCESSORS [8086] In the code examples so far, we have separated out the coded instructions from the data. Modern processors like the 8088 have separate registers which deal with each section of a program.
CS and IP = instructions DS, BX, SI= data ES, BX, DI= extra data SS, SP, BP= stack

In writing programs for modern processors like the 8088, the program is structured with a minimum of three sections, called SEGMENTS. The three segments represent the CODE, DATA and STACK areas of the program. Information within each segment is accessed differently depending upon the segment type. To access data in

the stack segment requires the use of the SS, SP and or BP registers. The following diagrams illustrates how information in the stack and data segments are accessed.

Special assembler directives are used to specify the different segments

SEGMENT DIRECTIVES The following directives illustrate how to define the three basic segments for an 8088 assembly language program.
.STACK 100H .DATA .CODE

The value following the stack directive specifies the size of the stack segment. The programmer is responsible for initializing the segment registers DS and ES to the correct segments of the program. Failure to do so will result in a program which will not access the data and extra data segments properly. The operating system will only initialize the CS, SS, SP and IP registers. The following code portion illustrates how to setup the data segment register. This is performed at the beginning of the code segment.
.STACK 100H .DATA .CODE MOV AX, @DATA MOV DS, AX

; initialize DS

DIFFERENT SIZED MEMORY MODELS The 8088 processor supports several different memory models. We shall look at the most common types.

SMALL memory model The small memory model is limited to a single combined segment of 64k bytes. This segment is a combination of the stack, code and data segments. The assembler directive used to specify a small memory model is,
.MODEL SMALL

LARGE memory model The large memory model supports multiple segments, each segment limited to 64k bytes. The code and stack segments are limited to 64k bytes each, but we can have two data segments of 64k bytes each. The assembler directive used to specify a large memory model is,
.MODEL LARGE

Use this memory model for all your programs.

SUPPORT FOR DIFFERENT CPU TYPES The following directives are used to specify the processor type.
.186 .286 .386 .8087 .8086

RETURNING TO PCDOS When an assembly language program running under PCDOS terminates, it must return to the operating system so that the user shell program can be re-loaded. The correct format is to use the following code sequence
mov ax, 4c00h int 21h

ASSEMBLER DIRECTIVES FOR IBM-PC PROGRAMS The following is a discussion of the assembler directives applicable to packages like Microsoft Masm and Turbo Assembler. These packages are used to write machine code programs which run under PCDOS.

EQUATES The EQU directive creates absolute symbols and aliases by assigning an expression or value to the declared variable name. Its format is,
name EQU expression

An absolute symbol represents a 16bit value; an alias is a name that represents another symbol. The declared name must be unique, one that has not been previously declared.
pi EQU clearax EQU 3.14159 xor ax,ax

The first example directs the assembler to replace every occurrence of the name pi with the value 3.1459, whilst the second example instructs the assembler to replace every occurrence of clearax with the instruction xor ax,ax

BYTE STORAGE The DB directive allocates and initializes a byte (8bits) of storage for each argument. Its format is,
name DB initialvalue,,,

The name portion is optional.


value1 form text DB DB DB 16 6*2 "Enter your name:"

In the first example, value1 is assigned a byte, and is initialized to 16, the second example sets form equal to 12 and assigns it a byte, and in the last example,text is defined as a sequence of bytes which each contain a character from the specified string. The first byte will be initialized to 'E', whilst the last byte will be initialized to a space character.

WORD STORAGE The DW directive allocates a word (2bytes) of storage for each initialized value. Its format is,
name DW initialvalue,,,

The name portion is optional.


DW DW ? 'ab'

mess

The first example allocates one word of storage, but does not define its initial value (?). The second example defines mess as a word initialized with the character string 'ab'. Strings when using the DW directive must not contain more than two characters. The 'b' will be placed in the low-order byte, and the 'a' will be placed in the high order byte. If only one character is specified, the high-order byte will contain 00H. The low-order byte appears FIRST for Intel Processors.

TITLE The title directive specifies the program listing title.


TITLE Graphics

This appears at the top of each page in the assembler list file, after the source file name.

NAME The name directive is used to set the name of the current module. The module name is used by the linker when displaying error messages. If no module name is used, the linker will use the name specified using the title directive.
NAME Calculate_Gross

PAGE CONTROL The PAGE directive can be used to designate the line length and width for the program listing; normally used to generate a page break in the assembler listing file. When assembly is taking place, and the page directive is encountered, the assembler generates a form-feed character to set a new page, and continues the assembly on the new page. In this way, the programmer can organize a printout of modules on a per page basis, so that the printout of more than one module per page does not occur.
PAGE 66,132 PAGE ; 66 lines per page ; 132 characters wide ; go to new page in list file

PROCEDURES These directives are used to implement small procedures (modules).


name PROC codetype .... ret name ENDP

The last instruction in a procedure is a RETurn instruction. The codetype is FAR for large memory models, NEAR for small memory models. A procedure must be entered using the appropriate CALL instruction.

DEFINE DOUBLE WORD, DEFINE QUAD WORD and DEFINE TEN The DD directive defines a double word [4bytes] of storage. This is used to reserve storage for 32 bit integers, floating point numbers, or far pointers to code or data [segment:offset pair]. The DQ directive defines a quad word [8bytes] of storage for double precision floating point numbers. The DT directive defines 10bytes of storage. This is normally used for Packed BCD numbers and a 10 byte temporary real floating point value, as this storage format is also used by the 80x87 arithmetic co-processor.

OFFSET The offset directive returns the number of bytes a variable begins at, relative to the start of the segment it is in. This is necessary when calling PCDOS routines.
temp mess .DATA db 10 db 'Hi there','$' .CODE mov mov mov mov int mov int END

start:

ax, @data ds, ax ah, 9h dx, OFFSET mess ;1 byte in .DATA segment 21h ;print message ax, 4c00h ;return to PCDOS 21h start

SAMPLE PROGRAM FOR IBM-PC


TITLE Doscall .MODELSMALL CR equ 0ah LF equ 0dh EOSTR equ '$' .stack 200h .datamessage ;Doscall.asm source file

db db

'Hello and welcome.' CR, LF, EOSTR

.code

print

proc mov int ret print endp start: mov mov mov call mov int end

near ah,9h 21h

;PCDOS print function

ax, @data ds, ax dx, offset message print ax, 4c00h 21h start

The program is assembled by typing


$ TASM DOSCALL Turbo Assembler V1.0 Copyright(c)1988 by Borland International Assembling file: DOSCALL.ASM Error messages: None Warning messages: None Remaining memory: 257k $

This produces an object file named DOSCALL.OBJ which must be linked to create an executable file which can run under PCDOS.
$ TLINK DOSCALL Turbo LinkV2.0 Copyright (c) 1987, 1988 Borland International $

The program when run, produces the following output.


$ DOSCALL Hello and welcome. $

MACROS The macro directive allows the programmer to write a named block of source

statements, then use that name in the source file to represent the group of statements. During the assembly phase, the assembler automatically replaces each occurrence of the macro name with the statements in the macro definition. Macros are expanded on every occurrence of the macro name, so they can increase the length of the executable file if used repeatably. Procedures or subroutines take up less space, but the increased overhead of saving and restoring addresses and parameters can make them slower. In summary, the advantages and disadvantages of macros are, Advantages

Repeated small groups of instructions replaced by one macro Errors in macros are fixed only once, in the definition Duplication of effort is reduced In effect, new higher level instructions can be created Programming is made easier, less error prone Generally quicker in execution than subroutines

Disadvantages In large programs, produce greater code size than procedures When to use Macros

To replace small groups of instructions not worthy of subroutines To create a higher instruction set for specific applications To create compatibility with other computers To replace code portions which are repeated often throughout the program

MACRO DEFINITION Defining Macros is done as follows,


name MACRO [optional arguments] statements statements ENDM

Consider the following macro to return to PCDOS from an assembly language program.
exittodos MACRO mov ax,4C00h int 21h

ENDM

Macros are expanded when the program is assembled. This means that every occurrence of the macro name (apart from the definition) is replaced by the statements in the macro definition. An example will demonstrate this.
TITLE dosmacro .MODELsmall exittodos MACRO mov ENDM .STACK 100h .DATA message DB

ax,4C00h int 21h

'Hello and Welcome', '$'

start:

.CODE mov ax, @data mov ds, ax mov ah, 9h mov dx, OFFSET message int 21h exittodos END start

When assembled, the macro is replaced and the internal representation of the file looks like,
TITLE dosmacro .MODELsmall exittodos MACRO mov ENDM .STACK 100h .DATA message DB

ax,4C00h int 21h

'Hello and Welcome', '$'

start:

.CODE mov ax, @data mov ds, ax mov ah, 9h

mov int mov int END

dx, OFFSET message 21h ax,4C00h 21h start

Macros can also accept values (parameters).


addup MACRO ad1,ad2, ad3 mov ax, ad1 mov dx, ad2 mov cx, ad3 ENDM

In this example a macro named addup is created. It accepts three parameters, ad1, ad2 and ad3. The code which follows, consisting of the mov statements, will be used to replace every occurrence of the macro name addup in the source file. The macro is terminated with the ENDM statement.Calling a macro with arguments is done as follows,
addup bx, 2, count

This has the effect of loading the ax register with the contents of the bx register, the dx register with the value 2, and the cx register with the value of count. Macro definitions may include other macro names, and macros may also be recursive: they can call themselves, eg,
pushall MACRO reg1, reg2, reg3, reg4, reg5, reg6 IFNB <reg1> ;; If parameter not blank push reg1 ;; push one register and ;; repeat pushall reg2, reg3, reg4, reg5, reg6 ENDIF ENDM pushall ax, bx, si, ds pushall cs, es

This shows a recursive macro called pushall that continues to call itself until it encounters a blank argument. In effect, it pushes the registers specified in the macro call onto the stack. The ;; indicates that the comment field of the macro should not be expanded with the macro statements.

IMPLEMENTING FP NUMBERS, ARRAYS, RECORDS AND JUMP TABLES Floating Point Numbers The following example shows the declaration of a single precision floating point decimal number (stored in IEEE 754 standard).
FPnum1 DD 1.32740

BCD strings The following example declares a packed BCD constant.


BCDval DT 123456

Ten bytes are allocated, giving a number range of 0 to 99,999,999,999,999,999,999.

HANDLING ARRAYS Arrays and array elements are dealt with using pointers. This involves either based or indexed addressing.

Manipulating an Array Element


1: Load a base/index register with the address of the first element 2: Calculate the offset position of the required element (1 byte for characters, 2 bytes for integers etc) 3: Perform the operation by either a) incrementing the base/index register by the required amount b) use based indexed addressing eg, X := IntArray[4]; mov bx, offset IntArray ; base address mov ax, 4 ; calculate offset

mul ax, 2 mov si, ax mov X, [bx + si]

Cycling through an Array using a Loop count variable The principles are the same, but the offset is the loop count variable adjusted by the number of bytes per element.eg,
FOR Loop := 1 to 10 do BEGIN sum := sum + IntArray[Loop] END; initfor:mov mov mov for: mov cmp ja mov mul mov mov mov add mov jmp forexit: ax, 1 ; Loop := 1 Loop, ax bx, offset IntArrat ; setup base register ax, Loop ax, 10 forexit ax, Loop ; calculate offset ax, 2 si, ax ax, [bx + si] cx, sum ; add sum and intArray[Loop] ax, cx sum, ax ; update sum for

Integer Arrays Integer arrays occupy two bytes per element. A typical operation is to sum the contents of an integer array. The following code for an 8086 shows this.
TITLE IntArray .MODEL .STACK 200h .DATA mess db result dw IntArry dw IntAlen dw buff db dup( 20h )

Large

'The total is ','$' ? 10, 34, 76, 25, 14, 9, 3, 22 ($ - IntArry) / 2 6 db '$'

.CODE binasc proc mov mov push mov mov shl shr cmp jb mov div add mov dec jmp add mov pop or jns dec mov mov ret endp mov mov mov mov mov mov xor add inc inc dec jne mov mov mov int call far ax, 0 ax, [result] ax si, offset buff[5] cx, 10 ax, 1 ax, 1 ax, 10 exit1 dx, 0 cx dl, 30h [si], dl si do1 al, 30h [si], al ax ax, ax exit2 si bl, 2dh [si], bl ; convert result to ascii string ; get number to convert ; save it ; point to string area ; divide base factor ; clear sign bit ; compare with base fact ; clear upper numerator ; divide by base factor ; convert to ASCII ; and store it ; next character ; convert last character ; and store it ; recover ; and test for sign bit ; store '-' sign

do1:

exit1:

exit2: binasc start:

lp1:

ax, @data ds, ax [result], 0000h ; clear result cx, IntAlen ; count of elements bx, offset IntArry ; point to IntArry si, 0000h ; first element ax, ax ; clear total ax, [bx + si] ; add value to total si ; next element si cx lp1 [result], ax ; store total dx, offset mess ; print message ah, 9h 21h binasc ; convert result to ASCII

mov mov int mov int END

dx, offset buff ah, 9h 21h ax, 4c00h 21h start

; exit to DOS

Other typical operations involve the determination of the minimum and maximum values.

Records (Structures) Records in Pascal support the use of different sized field items. Consider the storage of the following record.
Var example_record = RECORD int_number : integer; fp_number : real; letter : character; END;

The same record is implemented in assembly language by first defining its composition.
ex_rec STRUC int_num dw fp_num dd lett db ENDS

ex_rec

The next step creates a record which has the composition of the previous records definition.
my_rec ex_rec <22, 3.2, 'Hi there.$'>

Each field of the record is accessed in a similar method to that of Pascal, eg,
ex_rec.lett

accesses the lett field of the record ex_rec. The following program shows an implementation for the 8088 processor.
TITLE Records .MODEL Large ex_rec STRUC int_num dw fp_num dd mess db "" ENDS

ex_rec

.STACK 200h .DATA myrec ex_rec <22,1.30, "Hello there.$"> .CODE mov mov mov mov int mov int END

start:

ax, @data ds, ax dx, offset myrec.mess ah, 9h 21h ax,4c00h 21h start

Jump Tables Jump tables are an efficient method of implementing switch/case type statements. A jump table consists of an array of addresses. Using an offset into the array selects the address of the routine which handles that particular value. Jump tables are efficient, because it always take the same time to select any routine from the table. The order may be re-arranged or new routines added simply be increasing the size of the table. The following program implements a jump table.
TITLE Jump.asm .MODEL Large .STACK 200h .DATA

help

db 'This program exits when a function key is pressed.' db 10, 13, 'Ctrl A generates underline.', 10, 13 db 'Ctrl B generates bold.', 10, 13 db 'Ctrl C generates blinking.', 10, 13 db 'All other control codes return to normal text.', 10, 13 db 10, 13, 'Start typing characters.', 10, 13, '$'attrib db 07h ; screen attribute byte ; a table of addresses used to decipher recieve control codes ; each entry is the address of the appropriate routine label word dw ctrl_null dw ctrla ; 1 dw ctrlb ; 2 dw ctrlc ; 3 dw ctrld ; 4 dw ctrle ; 5 dw ctrlf ; 6 dw ctrlg ; 7 dw ctrlh ; 8 10 dw ctrli ; 9 11 dw ctrlj ; a 12 dw ctrlk ; b 13 dw ctrll ; c 14 dw ctrlm dw ctrln ; e 16 dw ctrlo ; f 17 dw ctrlp ; 10 20 dw ctrlq ; 11 21 dw ctrlr ; 12 22 dw ctrls ; 13 23 dw ctrlt ; 14 24 dw ctrlu ; 15 25 dw ctrlv ; 16 26 dw ctrlw ; 17 27 dw ctrlx ; 18 30 dw ctrly ; 19 31 dw ctrlz ; 1a 32 dw ctrl_lbkt dw ctrl_bslash dw ctrl_rbkt dw ctrl_carat dw ctrl_ul

ctl_tbl

;0

; d 15

; 1b 33 ; 1c 34 ; 1d 35 ; 1e 36 ; 1f 37

.CODE bumpcur proc

far

; move cursor right one character

mov ah, 3 xor bh, bh int 10h ; read int dh, dl inc dl ; next column cmp dl, 80 ; end of line? jle short bpcur1 xor dl, dl ; go to start of next line inc dh cmp dh, 24 ; end of screen? jl short bpcur1 mov ax, 0601h ; then scroll up xor cx, cx push dx mov dh, 24 mov dl, 80 mov bh, [attrib] int 10h pop dx mov dh, 24 ; position bottom linebpcur1: xor bh, bh ; set cursor position mov ah, 2 int 10h ret bumpcur endp ctrl_code proc far ; process Control CODES push bx cbw ; convert AL to AX mov bx,ax ; use bx and an index into shl bx,1 ; the ctrl_tbl jmp ctl_tbl[bx] ; jump to key routine ctrla: and byte ptr [attrib], 0f9h ; underline jmp ctrl_exit ctrlb: or byte ptr [attrib], 08h ; bold jmp ctrl_exit ctrlc: or byte ptr [attrib], 80h ; blink on jmp ctrl_exit ctrld: ; all others normal ctrl_null: ctrle: ctrlf: ctrlg: ctrlh: ctrli: ctrlj:

ctrlk: ctrll: ctrlm: ctrln: ctrlo: ctrlp: ctrlq: ctrlr: ctrls: ctrlt: ctrlu: ctrlv: ctrlw: ctrlx: ctrly: ctrlz: ctrl_lbkt: ctrl_bslash: ctrl_rbkt: ctrl_carat: ctrl_ul: mov byte ptr [attrib], 07h ctrl_exit: pop bx ret ctrl_code endp start:

; normal attribute

hlp1:

disp1:

mov ax, @data mov ds, ax mov ah, 9h ;print help message mov dx, offset help int 21 mov ah, 06h ; read character from keyboard mov dl, 0ffh int 21h jz lp1 ; repeat if character not ready cmp al, 00h ; if function key then exit je exit cmp al, 32 ; else if control code jae disp1 call ctrl_code ; then process control code jmp lp1 push bx xor bx, bx ; page zero on video memory mov bl, [attrib] ; get character attribute mov cx, 1 ; one character to write mov ah, 9 ; write char + attribute

exit: END

int 10h call bumpcur jmp lp1 mov ax, 4c00h int 21h start

; use BIOS call ; next cursor position ; repeat

PARAMETER PASSING Parameter passing refers to the exchange of data between modules. There are many ways this information can be exchanged. 1. GLOBAL DATA USING COMMON BUFFER OR MEMORY The data is stored in memory accessible to all modules. The disadvantage of this technique is that the data may be modified by any module, which makes debugging harder. Consider the following simple program which adds two numbers together, storing the result. All data has been declared as common.
TITLE CommonData .MODEL Large .STACK 200h .DATA num1 dw 22 num2 dw 32 result dw 0 .CODE addnum proc far mov ax, [num1] mov bx, [num2] add ax, bx mov [result], ax ret addnum endp start: mov ax, @data mov ds, ax call addnum ; add num1 and num2

mov ax, 4c00h int 21h END start

2. REGISTER VARIABLES This technique involves passing and returning values using processor registers. Routines must ensure that they do not corrupt any registers other than those which have been specified. The programmer first determines which registers will be used and which can be altered (contents destroyed). Consider the following implementation of the previous addition program to use register variables.
TITLE CommonData .MODEL Large .STACK 200h .DATA num1 dw 22 num2 dw 32 result dw 0 .CODE addnum proc far ; accepts num1 in ax, num2 in bx, returns result in dx push ax add ax, bx mov dx, ax pop ax ret addnum endp start: mov ax, @data mov ds, ax mov ax, [num1] mov bx, [num2] call addnum ; add num1 and num2 mov [result], dx mov ax, 4c00h int 21h END start

The advantage is that only the calling module alters the data, whilst the module addnum only works on copies of the data. In this way, it is easier to track which modules affect the data variables. 3. STACK VARIABLES Parameters may also be passed using the stack. This involves pushing the values onto the stack before the module is called. This may also involve pushing space onto the stack for a return result. The module then accesses the parameters on the stack using the appropriate addressing mode. Upon return to the calling module, the stack space is deallocated using appropriate pop or stack pointer adjustment instructions. There are two ways in which data may be referenced using the stack. 1. Call by Value This refers the placing of copies of the data value on the stack. Only the copy is worked with, the original remains unmodified. 2. Call by Reference This refers to the passing of the address of the variable using the stack. This address is used to access the data, thus the original data is used. Call by value is normally used for simple data types, whilst call by reference is used for data types like arrays and records, because of the amount of memory space they occupy (and stack space is normally limited). Consider the following program for an MC6802 processor which uses Call by Value to add two variables together.
CPU 6802 HOF MOT ORG 100H Num1: DFB 10 Num2: DFB 20 Result: DFB 0 Start: PSHA ; Make room for result on stack LDAA Num1 LDAB Num2 PSHA ; Place copy Num1 on stack

PSHB ; Place copy of Num2 on stack JSR Addup PULB ; remove copy of Num2 PULA ; remove copy of Num1 PULA ; get result from Addup STAA Result Exit: BRA Exit Addup: TSX ; transfer SP into IX register PSHA ; save registers PSHB LDAA 02,X ; Get Num2 LDAB 03,X ; Get Num1 ABA ; Add Num1 and Num2 STAA 04,X ; Store on stack for return PULB ; Recover original register values PULA RTS END Start

PARAMETER PASSING FOR THE 8088 PROCESSOR ACCESSING THE STACK FRAME INSIDE A MODULE Lets look at how a module handles the stack frame. Because each module will use the BP register to access any parameters, its first chore is to save the contents of BP.
push bp

It then transfers the address of SP into BP; BP now points to the top of the stack.
mov bp,sp

thus the first two instructions in a module will be the combination,


push bp mov bp,sp

ALLOCATION OF LOCAL STORAGE INSIDE A MODULE Local variables are allocated on the stack using a

sub sp, n

instruction. This decrements the stack pointer by the number of bytes specified by n. For example, a module might want to use temporary storage space for an integer i, which equates to the machine code instruction
sub sp, 2

Pictorially, the stack frame looks like,


+---------+ | ihigh |<-- SP +---------+ | ilow | +---------+ | BPhigh |<-- BP +---------+ | BPlow | +---------+

The local variable i can be accessed using SS:BP - 2, so the statement,


i = 24;

is equivalent to
mov [bp - 2], 18

Note that twenty-four decimal is eighteen hexadecimal.

DEALLOCATION OF LOCAL VARIABLES WHEN THE MODULE TERMINATES When the module terminates, it must deallocate the space it allocated for the variable i on the stack. Referring to the above diagram, it can be seen that BP still holds the top of the stack as it was when the module was first entered. BP has been used for two purposes,

to access parameters relative to it to remember where SP was upon entry to the module

The deallocation of any local variables (in our case the variable i) will occur with the following code sequence,
mov sp, bp ;this recovers SP, deallocating i pop bp ;SP now is the same as on entry to module

THE PASSING OF PARAMETERS TO A MODULE Consider the following module call in a high level langauge.
add_two( 10, 20 );

The language pushes parameters (the values 10 and 20) right to left, thus the sequence of statements which implement this are,
push ax ; assume ax contains 2nd parameter, ie, integer ; value 20 push cx ; assume cx contains 1st parameter, ie, integer ; value 10 call add_two

The stack frame now looks like,


+---------+ | Return |<-- SP +---------+ | address | +---------+ | 00 | ;1st parameter, integer value 10 +---------+ | 0A | +---------+ | 00 | ;2nd parameter, integer value 20 +---------+ | 14 | +---------+

Remembering that the first two statements of module add_two() are,


add_two: push bp mov bp, sp

The stack frame now looks like (after those first two instructions inside add_two)
+---------+ | BPhigh |<-- BP <-- SP +---------+ | BPlow | +---------+ | Return | +---------+ | address | +---------+ | 0A | ;1st parameter, integer value 10 +---------+ | 00 | +---------+ | 14 | ;2nd parameter, integer value 20 +---------+ | 00 | +---------+

ACCESSING OF PASSED PARAMETERS WITHIN THE CALLED MODULE It should be clear that the passed parameters to module add_two() are accessed relative to BP, with the 1st parameter residing at [BP+4], and the 2nd parameter residing at [BP+6].

DEALLOCATION OF PASSED PARAMETERS The two parameters passed in the call to module add_two() were pushed onto the stack frame before the module was called. Upon return from the module, they are still on the stack frame, so now they must be deallocated. The instruction which does this is,
add sp, 4

where SP is adjusted upwards four bytes (ie, past the two integers).

INTERFACING TO HLL ROUTINES There are times that high level languages need to call assembly language modules. This results due to constraints like speed and memory space. We shall look at interfacing a Pascal program to an assembly language module. The Pascal program will declare an integer based array, and pass the address of this array, and the number of elements in the array, to an assembly language module. Using the address, the assembly language module will add the sum of the array, returning the result to the Pascal program. The assembly language module is shown below.
TITLE Addup88 .MODEL TPASCAL .CODE PUBLIC Addup Addup Proc Far Array : DWORD, Elements : WORD RETURNS Reslt : WORD push ds ; save ds register push cx ; save cx register push si ; save si register lds si, Array ; point DS:SI to array element1 mov cx, Elements ; count of elements xor ax, ax ; clear total lp1: add ax, [si] ; add value to total inc si ; next element inc si dec cx jne lp1 pop si pop cx pop ds RET ; exit to Pascal Module with ; result in AX Addup ENDP END

This is compiled to OBJECT code by the command

$TASM ADDUP88

The Pascal module is shown below.


Program ADDDEMO (input, output); Uses DOS, CRT; Type IntArray = Array[1..20] of Integer; Var Numbers : IntArray; Result : Integer; Loop : Integer; {$F+} Function Addup( var Numbers : IntArray; Elements : Integer ) : Integer ; EXTERNAL; {$L ADDUP88.OBJ} {$F-} begin for loop:= 1 to 20 do Numbers[loop] := loop; Result := Addup( Numbers, 20 ); Writeln('The sum of the array is ', Result) End.

When compiled under Turbo Pascal, the two object modules are linked together, creating an executable file.

ASSEMBLER OPTIONS Various options are supported by most assemblers. These options provide for

increase productivity to check operation of assembler - macros, equates to simplify control provide flexibility

COMMAND FILES Command files are text files which contain commands to the assembler.

$TASM @MYCMDFIL

will invoke the assembler using the options specified in the file Mycdfil. If this file contained the following,
/a /e myprog, myobj, mylst;

this is equivalent to typing


$TASM /a /e myprog, myobj, mylst;

This simplifies the process of having to repeat all the command line options whilst the program is being debugged.

CONDITIONAL ASSEMBLY OF SOURCE CODE STATEMENTS The following directives are used to specify to the assembler, whether or not to assemble the bracketed group of statements which follow.
IF ELSE ENDIF IFDEF IFNDEF

The IF directives and the ENDIF and ELSE directives can be used to enclose the statements to be considered for conditional assembly. The conditional block of statements is used as follows,
IF debug xor ax,ax ELSE xor bx,bx ENDIF

If the symbol debug equates to true (non-zero), the ax register will be cleared, otherwise the bx register will be cleared.

The IFDEF and IFNDEF directives test whether or not the given variable name/symbol has been defined.
IFDEF buffer buf1 DB 10 DUP(?) ENDIF

In this example, buf1 is allocated only if buffer has previously been defined. It consists of ten bytes whose initial value is undefined.

THE INCLUSION OF SOURCE MACROS AND DEFINITIONS A macro or definition file is a collection of definitions or program code which can be included into the source code program. A macro file is simply a file containing macro definitions. The programmer adds these definitions to the source file using the include directive, and may remove unwanted definitions using the purge directive. The include directive inserts the definitions or code statements from the specified file into the current source file during assembly, and allows any variables or declarations in the include file to be referenced or accessed in the source program being written.
INCLUDE entry INCLUDE b:\include\c_stuff

LIST FILES List files have already been covered under section 6 dealing with CRS8. The format for invoking the 8088 assembler is,
TASM sourceasmfile, objfilename, listfilename

or the /l option can be specified on the command line. The following 8088 assembler directives can disable and enable the output listing.
%NOLIST

%LIST

Consider the following 8088 assembly language program.


TITLE Doscall ;Doscall.asm source file .MODEL SMALL CR equ 0ah LF equ 0dh EOSTR equ '$' .stack 200h .data message db 'Hello and welcome.' db CR, LF, EOSTR .code print proc near mov ah,9h ;PCDOS print function int 21h ret print endp start: mov ax, @data mov ds, ax mov dx, offset message call print mov ax, 4c00h int 21h end start

When assembled with the following command line options,


$TASM /l /n Doscall;

It generates a listing file. The list file for the program looks like,
Turbo Assembler Version 1.0 21-05-89 13:27:31 Page 1 DOSCALL.ASM Doscall 1 0000 .MODEL SMALL 2 3 = 000A CR equ 0ah 4 = 000D LF equ 0dh

5 = 0024 EOSTR equ '$' 6 7 0000 .stack 200h 8 9 0000 .data 10 0000 48 65 6C 6C 6F 20 61 + message db 'Hello and welcome.' 11 6E 64 20 77 65 6C 63 + 12 6F 6D 65 2E 13 0012 0A 0D 24 db CR, LF, EOSTR 14 15 0015 .code 16 0000 print proc near 17 0000 B4 09 mov ah,9h ;PCDOS print function 18 0002 CD 21 int 21h 19 0004 C3 ret 20 0005 print endp 21 22 0005 B8 0000s start: mov ax, @data 23 0008 8E D8 mov ds, ax 24 000A BA 0000r mov dx, offset message 25 000D E8 FFF0 call print 26 0010 B8 4C00 mov ax, 4c00h 27 0013 CD 21 int 21h 28 29 end start

The s on line 22 indicates a segment register value which is filled in by the DOS loader when the program is loaded into memory. The r on line 24 indicates a relative value which is also filled in by the DOS loader.

SYMBOLIC INFORMATION Symbolic information is useful in determining the size and location of variables, segments etc. This information is used when debugging the program or locating the program in Eprom. The 8088 assembler options available are,
/c cross-reference in list file /l listfile generated /n suppress symbol table in list file /zd line numbers in object code /zi debug info in object code for debugger

When the previous program Doscall.asm is assembled with a list file and symbol plus cross-referencing, the additional information appended to the list file is,
Turbo Assembler Version 1.0 21-05-89 13:35:03 Page 2 Symbol Table Symbol Name Type Value Cref defined at # ??DATE Text "21-05-89" ??FILENAME Text "DOSCALL " ??TIME Text "13:35:03" ??VERSION Number 0100 @CODE Text _TEXT #1 #15 @CODESIZE Text 0 #1 @CPU Text 0101H @CURSEG Text _TEXT #9 #15 @DATA Text DGROUP #1 22 @DATASIZE Text 0 #1 @FILENAME Text DOSCALL @WORDSIZE Text 2 #9 #15 CR Number 000A #3 13 EOSTR Number 0024 #5 13 LF Number 000D #4 13 MESSAGE Byte DGROUP:0000 #10 24 PRINT Near _TEXT:0000 #16 25 START Near _TEXT:0005 #22 29 Groups & Segments Bit Size Align Combine Class Cref defined at # DGROUP Group #1 1 22 STACK 16 0200 Para Stack STACK #7 _DATA 16 0015 Word Public DATA #1 #9 _TEXT 16 0015 Word Public CODE #1 1 #15 15

This also shows which lines variables and labels were defined and referenced.

PROGRAM MANAGEMENT TOOLS These tools are designed to make the process of maintaining programs easier. MAKE This utility is designed to ease updating of programs, especially multiple module programs.

It works by using a list of dependencies. These dependencies illustrate the relationship between the source, include, object and executable versions of the program. The dependencies are stored in a file called makefile. Consider a program which has the following dependencies.
MYDBASE.EXE comprises the modules start.obj search.obj fileio.obj keybdio.obj videoio.obj

Each object file is generated from an assembler source file of the same name. The command sequence to create the executable program is,
tasm start tasm search tasm fileio tasm keybdio tasm videoio tlink start search fileio keybdio videio, mydbase;

The dependencies and command sequences required are entered into the makefile as follows.
mydbase.exe: start.obj search.obj fileio.obj keybdio.obj videoio.obj tlink start search fileio keybdio videio, mydbase; start.obj: start.asm tasm start search.obj: search.asm tasm search fileio.obj: fileio.asm tasm fileio keybdio.obj: keybdio.asm tasm keybdio videoio.obj: videoio.asm tasm videio

The program is assembled and linked by typing

make

It works by comparing date and time stamps of the files in each dependency list. Consider the lines
keybdio.obj: keybdio.asm tasm keybdio

It compares the date/time stamp of keybdio.asm against keybdio.obj. If the object file is newer than the assembly file, it will not re-assemble. If the assembler file has a newer date/time stamp, it will execute the command tasm keybdio to generate a new object file. The use of make files simplifies the re-assembly by only assembling those files which have been modified.

SOURCE CODE REVISION SYSTEMS Source code revision systems are used to keep track of different versions of a program. It keeps a record of all the changes made to the program. Previous revisions can be extracted from the database, and a printout detailing the changes (time, who, line#) can be obtained.

LIBRARY MAINTENANCE This applies to the maintenance of OBJECT code libraries. An Object code library contains routines which can be reused in any program. The code for the routine is extracted from the library and combined with the users object code at linking time. Users can create their own library routines. The source files are assembled into object code then added to a library. The following code represents a routine for placement into a Video routines library.
TITLE SetCur

.CODE PUBLIC setcur setcur proc far ; set cursor to position in DX register mov ah, 2 ; dh = y co-ordinate, dl = x co-ordinate xor bh, bh int 10h ret setcur endp END

After assembling into Object code, the object code is placed into a video library using the TLIB utility.
TLIB video +setcur.obj

The following source file shows how to use the code in a library.
TITLE Libdemo .STACK 200h .CODE EXTRN setcur:far start: mov dx, 0 ; cursor 0,0 call setcur mov ax, 4c00h int 21h END start

After assembling the file Libdemo.asm, the command to link the object and library code together is,
TLINK Libdemo,libdemo.exe,libdemo.map, video

LINKERS The assembler for 8088 PCDOS programs generates object code files. These cannot be executed directly on the computer system, but require further processing in order to generate a runfile. This further process is called the linking phase. Functions performed by a linker include:

combines object modules together

combines segments of the same type together resolves addresses unknown at assembly time allocates storage generates symbolic information generates a load module

8088 LINKER OPTIONS The following options are used to obtain information which is helpful in debugging programs; or generate code for 386 processors.
/m add public symbols /x no map file /s map file with segments, publics symbols and start address /t generate .COM file /v add debug info /3 386 code

The Map File Facility If the linker is requested to generate a map file, it will list the names, load addresses, and lengths of all segments in a program. It also lists the names and load addresses of any groups in the program, the start address, and messages about any errors the linker may have encountered. The map file generated by the linker for the program DOSCALL.ASM is,
Start Stop Length Name Class 00000H 00014H 00015H _TEXT CODE 00016H 0002AH 00015H _DATA DATA 00030H 0022FH 00200H STACK STACK Address Publics by Name Address Publics by Value Program entry point at 0000:0005

DEFINITION OF LINKING TERMS

Relocatable/Relative The code generated by the linker is all relative to the location counter. This

means that all references to memory is relative to a base/index register, segment register or program counter. This allows the operating system to load the program anywhere in physical memory. Relocatable code is a must for multi-user and multi-tasking operating systems. The program is preceded by a header file, which the operating systems loader uses to perform relocation.

Absolute/Fixed If the linker generates code which is absolute, all memory references are to absolute addresses, thus the program must reside in a designated memory space. If this space is unavailable, the program cannot be run and must wait. Absolute code is normally used on small single processor systems (ie, CPM), and is not suitable for multi-user environments. Absolute code does not contain a header file used for relocation, if a header file exists, it will specify the absolute load address of the code which follows the header file.

Common Variables, labels or symbols may be designated as common. In this way, they are made accessible to those modules which wish to reference them by way of calls or data usage. The common data is shared by the various modules. The linker combines multiple definitions into a single overlayed segment.

External/Public Public data segments are located in one module but called from another. The Public directive makes the variable, label or symbol in the current segment available to all other modules. It thus transforms locally defined symbols into global symbols. The Extern directive makes a global symbols name and type known in a source file so that it may be used/referenced in that file. An extern item is a variable, label or symbol that has been declared using the public directive in another module of the program. Example of program using public/extern directives:
Main Module

NAME main .MODEL small PUBLIC exit EXTERN print:near .STACK 100h .DATA .CODE start: mov ax, @data mov ds, ax jmp print exit: mov ax, 4C00h int 21h END start Task Module NAME task .MODEL small PUBLIC print EXTERN exit:near

;defines exit as being known to other modules ;defines print as existing in another module

; Load segment location ; into DS register ; goto PRINT in other module ; call terminate function

;defines print as public so it can ;be used by the calling module ;defines exit as existing in another module ; outside this one

.DATA string DB "Hello",13,10,"$" .CODE print: mov dx, OFFSET string ;Load location of string mov ah, 09h ;call string display function int 21h jmp exit ;go back to main module END

In this example, the symbol exit is declared public in the main module so that it can be accessed from another source module (task). The main module also contains an external declaration of the symbol print. This declaration defines print to be a near label so that it can be accessed from the module main, even though it is assumed to be located and declared public in another source module. A jmp instruction later in main has the label print as its destination. The symbol print is declared public in the task module so that it may be accessed from another module (main).

The symbol exit is defined as a near label so that it can be accessed from this module, even though it is assumed to be located and declared public in the other module. Before this program can be executed, the two source files (one containing main, the other task) must be assembled individually, then linked together using a linker. The symbol listing for each source file shows the segment allocations.
MAIN.ASM Symbol Table Symbol Name Type Value ??DATE Text "21-05-89" ??FILENAME Text "MAIN " ??TIME Text "14:20:27" ??VERSION Number 0100 @CODE Text _TEXT @CODESIZE Text 0 @CPU Text 0101H @CURSEG Text _TEXT @DATA Text DGROUP @DATASIZE Text 0 @FILENAME Text MAIN @WORDSIZE Text 2 EXIT Near _TEXT:0008 PRINT Near ----:---- Extern START Near _TEXT:0000 Groups & Segments Bit Size Align Combine Class DGROUP Group STACK 16 0100 Para Stack STACK _DATA 16 0000 Word Public DATA _TEXT 16 000D Word Public CODE TASK.ASM Symbol Table Symbol Name Type Value ??DATE Text "21-05-89" ??FILENAME Text "TASK " ??TIME Text "14:20:14" ??VERSION Number 0100 @CODE Text _TEXT @CODESIZE Text 0 @CPU Text 0101H @CURSEG Text _TEXT @DATA Text DGROUP @DATASIZE Text 0

@FILENAME @WORDSIZE EXIT PRINT STRING Groups & Segments DGROUP _DATA _TEXT

Text TASK Text 2 Near ----:---- Extern Near _TEXT:0000 Byte DGROUP:0000 Bit Size Align Combine Class Group 16 0008 Word Public DATA 16 000A Word Public CODE

The map listing form the linker clearly shows how these segments have been combined.
MAIN.MAP (Output from Linker) Start Stop Length Name 00000H 00017H 00018H _TEXT 00018H 0001FH 00008H _DATA 00020H 0011FH 00100H STACK

Class CODE DATA STACK

Detailed map of segments 0000:0000 000D C=CODE S=_TEXT G=(none) M=MAIN.ASM ACBP=48 0000:000E 000A C=CODE S=_TEXT G=(none) M=TASK.ASM ACBP=48 0001:0008 0000 C=DATA S=_DATA G=DGROUP M=MAIN.ASM ACBP=48 0001:0008 0008 C=DATA S=_DATA G=DGROUP M=TASK.ASM ACBP=48 0002:0000 0100 C=STACK S=STACK G=DGROUP M=MAIN.ASM ACBP=74 Address 0000:0008 0000:000E Address 0000:0008 0000:000E Publics by Name EXIT PRINT Publics by Value EXIT PRINT

Program entry point at 0000:0000

SEGMENT DIRECTIVES So far, 8088 programs have been implemented using single segments with the directives
.CODE .STACK .DATA

This simplifies writing programs, but has several drawbacks.


little control over segment placing and combining limited to three segments

The use of the segment directives provide the necessary controls for implementing large multiple segment programs. The programmer can specify which segments should be overlayed, combined, or stand alone. Segment over-ride prefixs may be applied to certain instructions.
mov ax, cs:20h

obtains data from the code segment rather than the data segment. The format for declaring a segment is,
name SEGMENT align combine_type class name ENDS

Align specifies whether the segment starts at a byte, word or paragraph (10 byte) boundary. The default is paragraph. Combine_type specifies whether the segment is PUBLIC, COMMON, MEMORY, PRIVATE, PUBLIC or STACK.
PUBLIC The linker concatenates all segments with the same name to form a single contigous segment. The length is the sum of all the segments. COMMON The linker locates all segments with the same name at the same address (overlayed on top of each other). The length becomes the longest segment. MEMORY Same as Public PRIVATE The linker does not combine this segment with any other segment. STACK The linker concatenates all segments with the same name to form a single contiguous segment. The

length is the sum of all the segments. SS is initialised to the beginning of the segment, SP to the length of the segment.

Class controls the ordering of the segments at linking time. Segments with the same class name are loaded together. A segment of class CODE would be loaded before a segment of class STACK. The class name is enclosed using single or double quotes. An example program follows.
TITLE Segdemo stck segment para private 'STACK' db 200h dup (?) stck ends data segment byte public 'DATA' message db 'Hello there','$' data ends data2 segment byte public 'DATA2' message2 db 'Segment Data2','$' data2 ends code segment para private 'CODE' assume ds:data, ss:stck start: mov ax, seg data mov ds, ax mov ah, 9 mov dx, offset message int 21h assume ds:data2 mov ax, seg data2 mov ds, ax mov ah, 9 mov dx, offset message2 int 21h mov ax, 4c00h int 21h code ends end start

The map file for segdemo.exe is,

Start Stop Length Name Class 00000H 001FFH 00200H STCK STACK 00200H 0020BH 0000CH DATA DATA 0020CH 00219H 0000EH DATA2 DATA2 00220H 0023CH 0001DH CODE CODE Detailed map of segments 0000:0000 0200 C=STACK S=STCK G=(none) M=SEGDEMO.ASM ACBP=60 0020:0000 000C C=DATA S=DATA G=(none) M=SEGDEMO.ASM ACBP=28 0020:000C 000E C=DATA2 S=DATA2 G=(none) M=SEGDEMO.ASM ACBP=28 0022:0000 001D C=CODE S=CODE G=(none) M=SEGDEMO.ASM ACBP=60

This clearly shows the ordering (class) and concatenation of segments which are the same type.

Assembly Language The following is provided as reference material to the Assembly process, and the LC3b AssemblyLanguage. It has been extracted from Intro to Computing Systems: From bits and gates to C and beyond, 2e, McGraw-Hill, 2004. In my urgency to get this on theweb site, I may have inadvertentlycreated inconsistencies. If yound anything here that is missing an antecedent or otherwise makes no sense, please contact me and/or one of the TAs. Yale Patt 7.1 LC-3b Assembly Language We will begin our study of the LC-3b assembly language by means of an example. The program in Figure 7.1 multiplies the positive integer initially stored in NUMBER by six by adding the integer to itself six times. For example, if the integer is 123, the program computes the product by adding 123 123 123 123 123 123. The program consists of 21 lines of code. We have added a line number to each line of the program in order to be able to refer to individual lines easily. This is a common practice. These line numbers are not part of the program. Ten lines start with a semicolon, designating that they are strictly for the benet of the human reader. More on this momentarily. Seven lines (06, 07, 08, 0C, 0D, 0E, and 10) specify actual instructions to be translated into instructions in the ISA of the LC-3b, which will actually be carried out when the program runs. The remaining four lines (05, 12, 13, and 15) contain pseudoops,which are messages from the programmer to the translation program to help in the translation process. The translation program is called an assembler (in

this case the LC-3b assembler), and the translation process is called assembly. 7.1.1 Instructions Instead of an instruction being 16 0s and 1s, as is the case in the LC-3b ISA, an instruction in assembly language consists of four parts, as shown below: LABEL OPCODE OPERANDS ; COMMENTS.

Вам также может понравиться