Assembly Language Project
-
Upload
janinelazaro -
Category
Documents
-
view
277 -
download
2
description
Transcript of Assembly Language Project
The Artof
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 conceptso 1.1 Assembler
1.1.1 Number of passes 1.1.2 High-level assemblers
o 1.2 Assembly language2 Language designo 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 Macroso 2.3 Support for structured programming
3 Use of assembly languageo 3.1 Historical perspectiveo 3.2 Current usageo 3.3 Typical applications
4 Related terminology5 List of assemblers for different computer architectures6 Further details7 Example listing of assembly language source code8 See also9 References10 Further reading11 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 substitution—e.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.
MOV AL, 61h ; Load AL with 97 decimal (61 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]
MOV AL, 1h ; Load AL with immediate value 1MOV CL, 2h ; Load CL with immediate value 2MOV DL, 3h ; Load DL with immediate value 3
The syntax of MOV can also be more complex as the following
examples show.[6]
MOV EAX, [EBX] ; Move the 4 bytes in memory at the address contained in EBX into EAXMOV [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 design
[edit]Basic 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 pseudo-
operation (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, pseudo-
operations 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 self-
documenting 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 disassemblers—code without
any comments, meaningful symbols, or data definitions—is 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 higher-
level languages.[12]
[edit]Use of assembly language
[edit]Historical perspective
Assembly languages date to the introduction of the stored-
program 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 non-
issue 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, air-
conditioning 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-by-wire 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 low-
level 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 ;ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc 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 equ $0000 ; Assembly time constantInp ldaa PORTA ; 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 comment deca comment cli comment inca 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 FieldThe 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 pseudo-op codes supported by this assembler are
Group A Group B Group C meaning
org org .org Specific absolute address to put subsequent object code
= equ Define a constant symbol
set Define or redefine a constant symbol
dc.b db fcb .byte Allocate byte(s) of storage with initialized values
fcc Create an ASCII string (no termination character)
dc.w dw fdb .word Allocate word(s) of storage with initialized values
dc.l dl .long Allocate 32 bit long word(s) of storage with initialized values
ds ds.b rmb .blkb Allocate bytes of storage without initialization
ds.w .blkw Allocate bytes of storage without initialization
ds.l .blkl Allocate 32 bit words of storage without initialization
end end .end Signifies the end of the source code (TExaS ignores these)
--------------------------------------------------------------------------------------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 data + 1 ldaa data
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 6811/6812exampleno operand accumulator and inherent clra<expression> direct, extended, or relative ldaa 4#<expression> immediate ldaa #4<expression>,R indexed with address register ldaa 4,x<expr>,<expr> bit set or clear bset 4,#$01<expr>,<expr>,<expr> bit test and branch brset 4,#$01,there<expr>,R,<expr>,<expr> bit test and branch 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 32-bit twos-complement integer precision (on the IBM PC).
Precedence operationHighest parentheses2 unary + - ~3 binary * / % &lowest 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 multi-byte 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 INVALID REASON INVALID12 123456 more than 5 digits12345 12.3 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 INVALID REASON INVALID$12 ABCD no preceding "$"$ABCD $G2A invalid character$001F $2F018 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 INVALID REASON INVALID%00101 1010101 missing percent%1 %10011000101010111 too many digits%10100 %210101 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 REASON INVALID'*' 'VALID' 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 Set I=1 ldaa $1003 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 Signifies no key has been typed sei The following code will not be interrupted ldaa $1003 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 RegXFirst set 0 The first 16 bit local variableSecond set 2 The second 16 bit local variableResult set 4 The Maximum of first,secondNum1 set 12 Input parameter1Num2 set 10 Input parameter2Max 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=SecondMaxOK 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) cycles shows the number of cycles to execute this instruction
[100] total gives a running cycle total since last org pseudo-op{PPP} type 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 Types1) Undefined symbol: Program refers to a label that does not exist How to fix: check spelling of both the definition and access2) Undefined opcode or pseudo-op How to fix: check the spelling/availability of the instruction3) Addressing mode not available How to fix: look up the addressing modes available for the instruction4) Expression error How to fix: check parentheses, start with a simpler expression5) 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 references6) Address error How to fix: use org pseudo-op’s 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,xindex equ 100; ...loop ldaa #0
The listing shows the phasing errorCopyright 1999-2000 Test EXecute And Simulate$0000 A6E064 ldaa index,x$0064 index equ 100 ; ...$0003 8600 loop ldaa #0##### Phasing errorThis 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 ICC12Group B is supported by Motorola's DOS level AS05, AS08, AS11 and AS12Group C are some alternative definitions
Group A Group B Group C meaning
org org .org Specific absolute address to put subsequent object code
= equ Define a constant symbol
set Define or redefine a constant symbol
dc.b db fcb .byte Allocate byte(s) of storage with initialized values
fcc Create an ASCII string (no termination character)
dc.w dw fdb .word Allocate word(s) of storage with initialized values
dc.l dl .long Allocate 32 bit long word(s) of storage with initialized values
ds ds.b rmb .blkb Allocate bytes of storage without initialization
ds.w .blkw Allocate bytes of storage without initialization
ds.l .blkl Allocate 32 bit words of storage without initialization
end end .end Signifies the end of the source code (TExaS ignores these)
--------------------------------------------------------------------------------------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 -4PT equ -3Ans 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 outputsPORTB equ 0x01DDRB equ 0x03PORTC equ 0x04DDRC equ 0x06TCNT equ 0x84 ; 16 bit unsigned clock, incremented each cycleTSCR 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 RAMStatePt rmb 2 Pointer to the current state org $F000 Put in EEPROM so it can be changedOut equ 0 offset for output value * 2 bit pattern stored in the low part of an 8 bit byteWait equ 1 offset for time to waitNext equ 2 offset for 4 next states* Four 16 bit unsigned absolute addressesInitState fdb S1 Initial stateS1 fcb %01 Output fcb 5 Wait Time fdb S2,S1,S2,S3S2 fcb %10 Output fcb 10 Wait Time fdb S3,S1,S2,S3S3 fcb %11 Output fcb 20 Wait Time fdb S1,S1,S2,S1 org $F800 programs go in ROMMain 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 pointerFSM 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 delayWloop 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 -4PT set -3Ans 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 byteWait equ 1 offset for time to waitNext equ 2 offset for 4 next states* Four 16 bit unsigned absolute addressesInitState fdb S1 Initial stateS1 fcb %01 Output fcb 5 Wait Time fdb S2,S1,S2,S3S2 fcb %10 Output fcb 10 Wait Time fdb S3,S1,S2,S3S3 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 FCC 'ABC'LABEL2 fcc "Jon Valvano "LABEL4 fcc /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 #StrgStrg 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 100Con16 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 byteWait equ 1 offset for time to waitNext equ 2 offset for 4 next states* Four 16 bit unsigned absolute addressesInitState fdb S1 Initial stateS1 fcb %01 Output fcb 5 Wait Time fdb S2,S1,S2,S3S2 fcb %10 Output fcb 10 Wait Time fdb S3,S1,S2,S3S3 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 dl 100000,$12345678S2 .long 1,10,100,1000,10000,100000,1000000,10000000S3 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 EEPROMMain: 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 1 2 3 4 5 6 7
0 NUL DLE SP 0 @ P ` p
B 1 SOH DC1 : 1 A Q a qI 2 STX DC2 ! 2 B R b rT 3 ETX DC3 # 3 C S c sS 4 EOT DC4 $ 4 D T d t
5 ENQ NAK % 5 E U e u
0 6 ACK SYN & 6 F V f v
7 BEL ETB ' 7 G W g w
T 8 BS CAN ( 8 H X h xO 9 HT EM ) 9 I Y i y
A LF SUB * : J Z j z
3 B VT ESC + ; K [ k {
C FF FS , < L \ l ;
D CR GS - = M ] m }
E SO RS . > N ^ n ~
F S1 US / ? O _ o 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 - sumWhen 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 SEQUENCEdeveloping 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 PROGRAMMINGAsemblers 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, sub-routine, 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 constants or labelso immediate datao data contained in another accumulator or registero 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 valuesORG 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 hexCODE: LDAA STATUS ;The label called CODE is identified as a
;machine code instruction which loads the ;A accumulator with the contents of the ;memory location associated with the label ;STATUS, ie, the value 23
JMP CODE ;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 Byte value 0100 23 0101 B6 0102 01 0103 00 0104 7E 0105 01 0106 01
Location 0100 holds the value associated with the label STATUSLocations 0101 to 0103 perform the LDAA STATUS instructionLocations 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 reserving memory for data variables, arrays and structureso determining the start address of the programo determining the entry address of the programo 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 ASSEMBLERConsider 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 Label Operation Operand 1 Operand 21 COPY ZERO OLDER2 COPY ONE OLD3 READ LIMIT4 WRITE OLD5 FRONT: LOAD OLDER6 ADD OLD 7 STORE NEW8 SUB LIMIT9 BRPOS FINAL10 WRITE NEW11 COPY OLD OLDER12 COPY NEW OLD13 BR FRONT14 FINAL: WRITE LIMIT15 STOP16 ZERO: CONST 017 ONE CONST 118 OLDER SPACE19 OLD SPACE20 NEW SPACE21 LIMIT SPACE
The instruction set of the computer is as follows,
Operation Code Number ofSymbolic Machine Length Operands ActionADD 02 2 1 ACC <- ACC + OPD1BR 00 2 1 Branch to OPD1BRPOS 01 2 1 Branch to OPD1 if ACC> 0COPY 13 3 2 OPD2 <- OPD1LOAD 03 2 1 ACC <- OPD1READ 12 2 1 OPD1 <- input streamSTOP 11 1 0 Halt executionSTORE 07 2 1 OPD1 <- ACCSUB 06 2 1 ACC <- (ACC - OPD1)WRITE 08 2 1 output stream <- OPD1
The functions that the assembler will perform in translating the program are,
9. replace symbolic addresses by numeric addresses10. replace symbolic operation codes by machine operation codes11. reserve storage for instructions and data12. translate constants into machine representation
IMPLEMENTATIONThe 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 Address Label Operation OPD1 OPD2 1 0 COPY ZERO OLDER 2 3 COPY ONE OLD 3 6 READ LIMIT
and the contents of the symbol table will be
Symbol AddressZERO ---OLDER ---ONE ---OLD ---LIMIT ---Location Counter: 8Line 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 Address Label Operation OPD1 OPD21 0 COPY ZERO OLDER2 3 COPY ONE OLD3 6 READ LIMIT4 8 WRITE OLD5 10 FRONT LOAD OLDER6 12 ADD OLD
7 14 STORE NEW8 16 SUB LIMIT9 18 BRPOS FINAL
and the contents of the symbol table will be
Symbol AddressZERO ---OLDER ---ONE ---OLD ---LIMIT ---FRONT 10NEW ---FINAL ---Location Counter: 20Line 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 Address Label Operation OPD1 OPD21 0 COPY ZERO OLDER2 3 COPY ONE OLD3 6 READ LIMIT4 8 WRITE OLD5 10 FRONT LOAD OLDER6 12 ADD OLD7 14 STORE NEW8 16 SUB LIMIT9 18 BRPOS FINAL10 20 WRITE NEW11 22 COPY OLD OLDER12 25 COPY NEW OLD13 28 BR FRONT14 30 FINAL WRITE LIMIT15 32 STOP16 33 ZERO CONST 0
17 34 ONE CONST 118 35 OLDER SPACE19 36 OLD SPACE20 37 NEW SPACE21 38 LIMIT SPACE
and the contents of the symbol table will be
Symbol AddressZERO 33OLDER 35ONE 34OLD 36LIMIT 38FRONT 10NEW 37FINAL 30Location Counter: 39Line 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 OPD2 00 3 13 33 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 Length Opcode OPD1 OPD2 00 3 13 33 35 03 3 13 34 36 06 2 12 38 08 2 08 36 10 2 03 35 12 2 02 36 14 2 07 37 16 2 06 38
18 2 01 30 20 2 08 37 22 3 13 36 35 25 3 13 37 36 28 2 00 10 30 2 08 38 32 1 11 33 1 00 34 1 01 35 1 xx 36 1 xx 37 1 xx 38 1 xx
ASSEMBLER DIRECTIVESAs 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.
EQUATEis 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
ORIGINThis 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 120HLDAA #FFH
The statement LDAA #FFH begins at byte 120h.
ORG $ + 2start: 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 TYPESThis 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 FORMATSThis 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 formatsINT ; intel formatsTEK ; tektronix formats
This directive appears before any machine instructions.
BYTE STORAGEThe 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 16form: DFB 6*2text: 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 STORAGEThe directive used to allocate and initialize words (two bytes) of memory storage is,
DWM define word, most significant byte firstDWL define word, least significant byte first
Its format is,
name: DWM initialvalue,,,
The name portion is optional.
DWM 1687Hmess: 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 RESERVATIONThis 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 AddressThe 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 0100Hstart: LDAA #3FHJMP startEND 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 CRS8The following source file has been named MC6802.ASM
CPU 6802 ; 6802 processor HOF MOT ; Motorola Records
ORG 0100H ; Start of DataSource: DFB 'Hello and Welcome'Length: EQU $ - Source ;Length of SourceDestin: DFS Length ; Buffer which has same
; length as SourceORG 0120H ; Start of Code
Entry: LDX #Source ; Point Index Reg to ; Source string
LDAB #Length ; Number of characters to move
Loop: LDAA 0,XSTAA Length,XINXDECBBNE Loop
Fin: JMP FinEND 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 ; a list file showing the code generated MC6802.HEX ; the record file for downloading to the
; target system or Eprom programmer
The listing file MC6802.PRN looks like
C:6802.TBL CPU 6802 ; 6802 processorC:6802.HEX HOF MOT ; Motorola Records
0100 ORG 0100H ; Start of Data
0100 48656C6C6F Source: DFB 'Hello and Welcome'0011 = Length: EQU $ - Source ; Length of
Source0111 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,X0127 A711 STAA Length,X0129 08 INX012A 5A DECB012B 26F8 BNE Loop 012D 7E012D Fin: JMP Fin0130 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
S00D0000433A363830322E48455892S113010048656C6C6F20616E642057656C636F6D1DS10401106585S1130120CE0100C611A600A711085A26F87E012D9FS9030120DB
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
egS1 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 TYPESMost 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.
Charactersare 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 Stringsare 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).
Integersare 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 Arraysare 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 0120Hstart: LDAA #30H ; ASCII '0'
LDAB #11 ; ten digitsLDX #Digits
loop: STAA 0,XINX ; next elementINCA ; next digitDECBBNE loop
exit: ....
Typical Array OperationsThe following routines are typical of functions which are performed on character based arrays.
CopyThis 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 6802 HOF MOT ORG 100H str1: DFS 10 st1len: EQU $ - str1 str2: DFS 10 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 jsr copystr ; copy str1 to str2 exit: bra exit initstr1: LDAA #41H ; character 'A' LDAB #11 ; elements 1 - 30 LDX #str1 ; point to str1 lp1: STAA 0,X ; store character INX ; next element INCA ; next value DECB ; loop around BNE lp1 LDAA #00 ; null terminator STAA 0,X RTS copystr: LDX SRCINX ; pick up source
pointer cplp2: LDAA 0,X ; get source character CMPA #00H ; eostr? BEQ cpstrq ; yes, then exit INX ; else inc source pointer STX SRCINX ; store source ptr LDX DSTINX ; get destination pointer STAA 0,X ; store character
INX ; inc dest ptr STX DSTINX ; store dest ptr LDX SRCINX ; reload source pointer BRA cplp2 ; repeat cpstrq: LDX DSTINX ; Null terminate
dest str CLR 00,X RTS END start1
String LengthReturns 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 6802 HOF MOT EOFSTR: EQU 00H ORG 100H str1: DFB 'Hello and Welcome.', 00H ORG 120H start: LDX #str1 ; point to string jsr strlen ; find length of str1 exit: bra exit strlen: LDAB #00 ; character count strlp1: LDAA 0,X ; read character CMPA #EOFSTR ; is it end of string BEQ strexit ; yes, then exit INX ; no, inc str ptr INCB ; inc character count BRA strlp1 ; and repeatstr exit: RTS ; acc B has length END start2
Search for first occurrence of a characterThis 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 6802 HOF MOT EOFSTR: EQU 00H
ORG 100H str1: DFB 'Hello and Welcome.', 00H ORG 120H start: LDAA #6FH ; ASCII 'o' LDX #str1 ; point to src string jsr strpos ; find first 'o' in str1 exit: bra exit strpos: CMPA 0,X ; is char = search value BEQ strex2 ; yes then exit CMPA #EOFSTR ; is it end of string BEQ strex1 ; yes, then exit INX ; no, inc str ptr BRA strpos ; and repeat strex1: LDX #0000H ; not found strex2: RTS ; Index reg has address END start3
Search for SubstringThis 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/ReplacementThis 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 ReverseThis routine reverses the characters in a string. It accepts the address of the string. Hello becomes olleH
String to UppercaseThis routine converts all characters of a string to uppercase. It accepts the address of the string. Hello becomes HELLO
String to LowercaseThis routine converts all characters of a string to lowercase. It accepts the address of the string. Hello becomes hello
ARRAY INDEX CALCULATIONSThis 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 CONSTRUCTSIn 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 register2. Perform the operation3. Store the result back into the variables location
SIMPLE STATEMENT ASSIGNMENTSAssigning a constant value to a variable
1. Load the constant into a register2. Store the register to the variables memory location eg,3.4. X1 := 20;5.6. LDX #207. STX X1
Use eight bit registers for bytes/characters, and 16bit registers for integers. eg,
Letter := 'Y';
LDAA #'Y'STAA Letter
Assigning a variables value to another variable
8. Load the second variable into a register9. Store the register into the first variables memory location eg,10.11. X1 := Y;12.13. LDX Y14. STX X115.
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 Y LDAB Z ABA STAA 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 Y SUBA #7 STAA 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 Y LDAB Z SBA ; subtract bx from ax, Z from Y STAA 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 Z LDAB #7 ; mult A,B ; (Z * 7) LDAB Y ADDB #4 ; (Y + 4) ABA STAA X1 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 STATEMENTSThis 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 test2. Branch false to an endif label3. The if body statements preceed the endif label
if: ; comparison test ; jump false to endif ; if body statements endif:
Comparing a variable and a constant1. Load the variable into a register2. Compare the register against the constant3. Branch false to a label after the body of the if statement
IF X1 < 10 then Y := Z * 2; if: LDAA X CMPA #10 BCC endif LDAA Z ; if body, Y := Z * 2 LDAB 2 ; mult a, b STAA Y endif:
Comparing a Variable against another Variable1. 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 Z LDAA X CBA BLT endif LDAA X1 ; if body, Y := X1; STAA Y endif:
Comparing a Variable for Logic 1 or TRUE1. Load the variable into a register (t1)2. Compare the register against zero3. Branch equal to a label after the body of the if statement
IF X1 then Y := X / 2; if: LDAA X CMPA #0 BEQ endif LDAA X1 ; if body, Y:=X1 / 2; LDAB #2 ; div A, B STAA Y endif:
Comparing a Variable for Logic 0 or FALSE1. Load the variable into a register (t1)2. Compare the register against zero3. Branch above or greater to a label after the body of the if statement
IF X1 then Y := X + 2; if: LDAA X1 CMPA #0 BHI endif LDAA X1 ; if body, Y:=X1 + 2; ADDA #2 STAA Y 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 X1 CMPA #2 BNE endif LDAA #4 STAA Y endif:
IF NOT X1 = 2 then Y := 4; if: LDAA X CMPA #2 BEQ endif LDAA #4 STAA Y endif:
IF THEN ELSE STATEMENTSThis 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: ; else statements ; endif:
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 XCMPA #2BNE elseLDAA YADDA #4STAA YJMP endif
else: LDAA #0STAA Z
endif:
WHILE LOOPSThis 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 test2. Branch false to an endwhile label3. 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 X CMPA #10 BHI endwhile LDAB X ; Y := Y + X LDAA Y ABA STAA Y LDAA X ; X := X + 1 ADDA #1 STAA X JMP while endwhile: 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 LOOPS1. Initialise the loop variable2. Use a for label, Perform the comparison test with the final value3. Branch false to an endfor label4. Inside the for loop body, the last statement, should adjust the loop
variable, and use an unconditional branch back to the for label
initfor: ; initialise loop variable for: ; 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: LDAA #1 STAA X for: LDAA X CMPA #10 BHI endfor LDAB X ; Y := Y + X LDAA Y ABA STAA Y LDAA X ; NEXT X ADDA #1 STAA X JMP for 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 statementIn comparing the value of operands, consider the following example.
2.3. IF X = 2 THEN Y = X4.
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: DFB 10Y: DFB 00
....LDAA X ; load A acc with value of XCMPA #02 ; compare A acc with immediate
data BNE IF1 ; exit if false LDAA X ; get value of X STAA Y ; store value of X at variable Y
IF1: ..... ; next statement after if construct
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 compareCMP [X], [Y]register to memory compareCMPA [Y] register to register compareCMPAB
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 ; get variable X CMPA Y ; compare with variable Y BNE IF1 ; exit it not equal LDAA #00H ; set variable Y to zeroSTAA Y
IF1: ....
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: DFB 10HY: DFB 12H
IF: LDAA XCMPA Y BHI IF1LDAA #04H STAA Y
IF1: ....
5. The IF THEN ELSE statementIn 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: DFB 00Y: DFB 00 IF: LDAA X
CMPA #02D BNE ELSE1 LDAA X STAA Y JMP IF1
ELSE1: LDAA Y STAA X
IF1: ....
8. The WHILE WEND statementConsider the following example for unsigned values.
9.10. WHILE X < 10 DO11. Y = Y + 1 X = X + 1 12. WEND
This becomes coded as,
X: DFB 00H Y: DFB 00HDO1: LDAB X
CMPB #10D BCC EXIT1 ; for signed use BLTLDAA Y ; increment value of Y ADDA #01 STAA Y LDAA X ; increment value of X ADDA #01 STAA X JMP DO1
EXIT1: ...
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.asmCPU 6802 HOF MOT ORG 100h loop: dfb 0 val1: dfb 0 val2: dfb 0ORG 120h
Begin: LDAA #0 ; val1 := 0 STAA val1 LDAA #0 ; val2 := 0 STAA val2 LDAA #0 ; loop := 0 STAA loop
While: LDAA loop ; while loop <= 10 do CMPA #10 BGT if1 LDAA val2 ; val2 := val2 + loopLDAB loop ABASTAA val2 LDAA loop ; loop := loop + 1 ADDA #01STAA loop JMP While ; endwhile
if1: LDAA val2 ; if val1 < val2 then CMPA val1 BGE Else LDAA val2 ; val1 := val2 STAA val1 JMP endif
Else: LDAA val1 ; else val2 := val1 STAA Val2
Endif: NOPSWIEnd Begin
DATA CONVERSION ROUTINESComputer 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 CHARACTERSThis routine converts a byte to TWO ASCII characters. eg,
AFH becomes 41H 46H
The algorithm for this is
GET DIGITMASK OFF HIGH NIBBLE CONVERT TO ASCII MASK OFF LOW NIBBLE CONVERT TO ASCII
The following code shows an MC6802 implementation.
CPU 6802HOF MOT ORG 0100H
Val1: DFB 3FH Result: DFS 02H
ORG 120H Start:
LDAA Val1 ; get val1 PSHA ; save val1 ANDA #0F0H ; mask high byteLSRA ; shift to low nibble LSRA LSRA LSRA JSR Conv ; convert high nibble STAA Result ; store it PULA ANDA #0FH ; mask low nibbleJSR Conv ; convert low nibble STAA Result+1 ; store it
Exit: BRA Exit
Conv: CMPA #9H ; check for digit BLS ASCZ ADDA #07 ; adjust for letter
ASCZ: ADDA #30H ; adjust to ASCII RTS
END Start
ASCII STRING TO HEX BYTEThis 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 EndIfShift 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 6802 HOF MOTORG 0100H
ASC1: DFB 33H ASC2: DFB 46H HexB: DFB 00H
ORG 120H
Start:LDAA ASC1 ; get first digit SUBA #30H CMPA #09H BLS If1 SUBA #07h
If1: ASLAASLA ASLA ASLA
STAA HexB LDAA ASC2 ; get next digitSUBA #30H CMPA #09H BLS If2 SUBA #07h
If2: ORAA HexB STAA HexBEND Start
8BIT MULTIPLYThis 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 6802 HOF MOT ORG 0100H
Val1: DFB 10HVal2: DFB 20HResult: DFS 02H
ORG 0120H
Start: CLRA ; product MSB = zeroCLRB ; product LSB = zero LDX #0008H ; multiplier = 8
Shift: CPX #0000H BEQ Exit ASLB ; shift product left 1 bit ROLA
ASL Val1 ; shift multiplier left to BCC Decr ; examine next bit ADDB Val2 ; Add multiplicand to ADCA #00H ; product if carry is set
Decr: DEXBRA Shift ; loop till all 8bits are done
Exit: STAA ResultSTAB Result+1
Finish: BRA Finish END Start
8BIT DIVIDEThis 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 6802 HOF MOT ORG 0100H
Val1: DFB 10H ; DividendVal2: DFB 20H ; DivisorQuot: DFB 00HRem: DFB 00H
ORG 0120H
Start: LDX #0008H ; Number of bits in Divisor
CLRALDAB Val1 ; Get Dividend
Div: CPX #0000H BEQ Exit ASLB ; Shiftv Dividend and Quotient ROLACMPA Val2 ; is subtraction successfulBCS ChkCnt SUBA Val2 ; Yes, subtract and set bit in
quotient INCB
ChkCnt: DEX BRA DivExit: STAB Quot
STAA Rem END Start
ASCII TO INTEGERThis routine converts an ASCII character string into a 16bit signed integer value. To implement this routine, the following variables are used.
OFFSET DFB ;offset into ASCII string BUFFER DFS ;space for ASCII string BINV DFW ;integer result BASE DFW ;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 ASCIIThis routine converts a 16bit signed integer into an ASCII character string. To implement this routine, the following variables are used.
OFFSET DFB ;offset into ASCII string BUFFER DFS ;space for ASCII string BINV DFW ;integer value BASE DFW ;base 10 value
The algorithm for implementing the routine is,
Offset equals last position in Buffer Get BinVSave 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 BinVStore 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 DECIMALThis 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 6802 HOF MOT ORG 0100H
Val1: DFB 00HVal2: DFB 0AH ; multiply by 10 decimalResult: DFS 02H ; result of Val1 * Val2PackBCD:DFB 93HDecVal: DFB 00H
ORG 0110H
Start: LDAA PackBCDLSRA ; shift high nibble to low nibble LSRA LSRA LSRA STAA Val1 ; multiply high nibble by 10 JSR Multiply LDAA PackBCD ANDA #0FH ; mask high byte ADDA Result+1 ; add to high byte * 10 STAA DecVal ; store decimal value
Finish: BRA Finish
Multiply: CLRA ; product MSB = zero CLRB ; product LSB = zero LDX #0008H ; multiplier = 8
Shift: CPX #0000H BEQ Exit ASLB ; shift product left 1 bit ROLAASL Val1 ; shift multiplier left to BCC Decr ; examine next bitADDB Val2 ; Add multiplicand to ADCA #00H ; product if carry is
setDecr: DEXBRA Shift ; loop till all 8bits are done
Exit: STAA Result STAB Result+1 RTS END 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 = instructionsDS, 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 DIRECTIVESThe 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
.CODEMOV AX, @DATA ; initialize DSMOV DS, AX
DIFFERENT SIZED MEMORY MODELSThe 8088 processor supports several different memory models. We shall look at the most common types.
SMALL memory modelThe 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 modelThe 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 TYPESThe following directives are used to specify the processor type.
.186
.286
.386
.8087
.8086
RETURNING TO PCDOSWhen 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, 4c00hint 21h
ASSEMBLER DIRECTIVES FOR IBM-PC PROGRAMSThe 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.
EQUATESThe 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 3.14159clearax EQU 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 STORAGEThe 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 DB 16form DB 6*2text DB "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 STORAGEThe DW directive allocates a word (2bytes) of storage for each initialized value. Its format is,
name DW initialvalue,,,
The name portion is optional.
DW ?mess DW 'ab'
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.
TITLEThe 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.
NAMEThe 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 CONTROLThe 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 ; 66 lines per page ; 132 characters wide
PAGE ; go to new page in list file
PROCEDURESThese 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 TENThe 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.
OFFSETThe 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.
.DATA temp db 10 mess db 'Hi there','$' .CODE
start: mov ax, @data mov ds, ax mov ah, 9h mov dx, OFFSET mess ;1 byte in .DATA
segment int 21h ;print message mov ax, 4c00h ;return to PCDOS int 21h END start
SAMPLE PROGRAM FOR IBM-PC
TITLE Doscall ;Doscall.asm source file .MODEL SMALLCR equ 0ahLF equ 0dhEOSTR equ '$'
.stack 200h
.datamessage db 'Hello and welcome.' db CR, LF, EOSTR
.codeprint proc near
mov ah,9h ;PCDOS print function int 21h ret
print endp
start: mov ax, @datamov ds, ax mov dx, offset message call print mov ax, 4c00h int 21h end start
The program is assembled by typing
$ TASM DOSCALLTurbo Assembler V1.0 Copyright(c)1988 by Borland
International
Assembling file: DOSCALL.ASM Error messages: None Warning messages: NoneRemaining 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. $
MACROSThe 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
DisadvantagesIn 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 DEFINITIONDefining Macros is done as follows,
name MACRO [optional arguments]statementsstatements ENDM
Consider the following macro to return to PCDOS from an assembly language program.
exittodos MACRO mov ax,4C00hint 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 .MODEL smallexittodos MACRO mov ax,4C00h
int 21h ENDM
.STACK 100h
.DATAmessage DB 'Hello and Welcome', '$'
.CODE
start: 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 .MODEL smallexittodos MACRO mov ax,4C00h
int 21h ENDM
.STACK 100h
.DATAmessage DB 'Hello and Welcome', '$'
.CODEstart: mov ax, @data
mov ds, ax mov ah, 9h mov dx, OFFSET message int 21h mov ax,4C00h int 21h END start
Macros can also accept values (parameters).
addup MACRO ad1,ad2, ad3 mov ax, ad1mov 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, dspushall 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 NumbersThe following example shows the declaration of a single precision floating point decimal number (stored in IEEE 754 standard).
FPnum1 DD 1.32740
BCD stringsThe 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 ARRAYSArrays 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 variableThe 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 ax, 1 ; Loop := 1 mov Loop, ax mov bx, offset IntArrat ; setup base
register for: mov ax, Loop
cmp ax, 10 ja forexit mov ax, Loop ; calculate offset mul ax, 2 mov si, ax mov ax, [bx + si] mov cx, sum ; add sum and
intArray[Loop] add ax, cx mov sum, ax ; update sum jmp for forexit:
Integer ArraysInteger 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 Large .STACK 200h .DATAmess db 'The total is ','$'result dw ?IntArry dw 10, 34, 76, 25, 14, 9, 3, 22IntAlen dw ($ - IntArry) / 2buff db 6dup( 20h ) db '$'
.CODE
binasc proc far ; convert result to ascii string
mov ax, 0 mov ax, [result] ; get number to convert push ax ; save it mov si, offset buff[5] ; point to string area mov cx, 10 ; divide base factor shl ax, 1 ; clear sign bit shr ax, 1
do1: cmp ax, 10 ; compare with base fact
jb exit1 mov dx, 0 ; clear upper numerator
div cx ; divide by base factor add dl, 30h ; convert to ASCII mov [si], dl ; and store it dec si ; next character jmp do1
exit1: add al, 30h ; convert last character mov [si], al ; and store it pop ax ; recover or ax, ax ; and test for sign
bit jns exit2dec si ; store '-' signmov bl, 2dh mov [si], bl
exit2: retbinasc endp
start: mov ax, @datamov ds, ax mov [result], 0000h ; clear result mov cx, IntAlen ; count of elements mov bx, offset IntArry ; point to IntArrymov si, 0000h ; first element xor ax, ax ; clear total
lp1: add ax, [bx + si] ; add value to total inc si ; next element inc sidec cx jne lp1 mov [result], ax ; store total mov dx, offset mess ; print message mov ah, 9h int 21h call binasc ; convert result to
ASCII mov dx, offset buff mov ah, 9h int 21h mov ax, 4c00h ; exit to DOS int 21h END start
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 = RECORDint_number : integer; fp_number : real; letter : character; END;
The same record is implemented in assembly language by first defining its composition.
ex_rec STRUCint_num dwfp_num dd lett db
ex_rec ENDS
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 STRUCint_num dw
fp_num dd
mess db " "ex_rec ENDS
.STACK 200h
.DATAmyrec ex_rec <22,1.30, "Hello there.$">
.CODEstart: mov ax, @data
mov ds, ax mov dx, offset myrec.mess mov ah, 9h int 21h mov ax,4c00h int 21h END start
Jump TablesJump 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 .DATAhelp 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
ctl_tbl label worddw ctrl_null ; 0 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 ; d 15 dw ctrln ; e 16 dw ctrlo ; f 17 dw ctrlp ; 10 20dw 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 ; 1b 33dw ctrl_bslash ; 1c 34 dw ctrl_rbkt ; 1d 35 dw ctrl_carat ; 1e 36 dw ctrl_ul ; 1f 37
.CODEbumpcur 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 positionmov 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 ; normal attributectrl_exit: pop bx
retctrl_code endp
start: mov ax, @data mov ds, ax mov ah, 9h ;print help message mov dx, offset help int 21
hlp1: 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 exitje exit cmp al, 32 ; else if control code jae disp1 call ctrl_code ; then process control
code jmp lp1
disp1: push bx xor bx, bx ; page zero on video memory mov bl, [attrib] ; get character attributemov cx, 1 ; one character to write mov ah, 9 ; write char + attributeint 10h ; use BIOS call call bumpcur ; next cursor position jmp lp1 ; repeat
exit: mov ax, 4c00h int 21h
END start
PARAMETER PASSINGParameter 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 MEMORYThe 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 .DATAnum1 dw 22num2 dw 32result dw 0
.CODEaddnum 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 VARIABLESThis 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 .DATAnum1 dw 22num2 dw 32result dw 0
.CODEaddnum 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 VARIABLESParameters 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 ValueThis 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 ReferenceThis 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 100HNum1: DFB 10Num2: DFB 20Result: 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 MODULELets 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 MODULELocal 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 integeri, 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 TERMINATESWhen 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 MODULEConsider 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 MODULEIt 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 PARAMETERSThe 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 ROUTINESThere 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 AddupAddup 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 ENDPEND
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 OPTIONSVarious 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 FILESCommand 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 STATEMENTSThe following directives are used to specify to the assembler, whether or not to assemble the bracketed group of statements which follow.
IFELSEENDIFIFDEFIFNDEF
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 debugxor ax,ax
ELSExor 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 bufferbuf1 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 DEFINITIONSA 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 entryINCLUDE b:\include\c_stuff
LIST FILESList 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 SMALLCR equ 0ahLF equ 0dhEOSTR equ '$' .stack 200h .datamessage db 'Hello and welcome.' db CR, LF, EOSTR .codeprint 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 1DOSCALL.ASMDoscall 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 INFORMATIONSymbolic 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 2Symbol TableSymbol 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 #15CR Number 000A #3 13EOSTR Number 0024 #5 13LF Number 000D #4 13MESSAGE Byte DGROUP:0000 #10 24PRINT Near _TEXT:0000 #16 25START 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 TOOLSThese tools are designed to make the process of maintaining programs easier.
MAKEThis 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 SYSTEMSSource 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 MAINTENANCEThis 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 endpEND
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 .CODEEXTRN setcur:farstart: 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
LINKERSThe 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 OPTIONSThe 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 FacilityIf 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 ValueProgram entry point at 0000:0005
DEFINITION OF LINKING TERMS
Relocatable/RelativeThe 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/FixedIf 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.
CommonVariables, 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/PublicPublic 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 ;defines exit as being known to
other modules EXTERN print:near ;defines print as existing in
another module .STACK 100h .DATA .CODEstart: mov ax, @data ; Load segment location
mov ds, ax ; into DS register jmp print ; goto PRINT in other module
exit: mov ax, 4C00h ; call terminate function int 21h END start
Task Module NAME task .MODEL small PUBLIC print ;defines print as public so it
can ;be used by the calling module
EXTERN exit:near ;defines exit as existing in another module
; outside this one .DATAstring DB "Hello",13,10,"$" .CODEprint: 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 TableSymbol 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 2EXIT Near _TEXT:0008PRINT Near ----:---- ExternSTART Near _TEXT:0000
Groups & Segments Bit Size Align Combine ClassDGROUP Group STACK 16 0100 Para Stack
STACK _DATA 16 0000 Word Public
DATA_TEXT 16 000D Word Public
CODE
TASK.ASM Symbol TableSymbol 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 Text TASK@WORDSIZE Text 2EXIT Near ----:---- ExternPRINT Near _TEXT:0000STRING Byte DGROUP:0000
Groups & Segments Bit Size Align Combine ClassDGROUP Group _DATA 16 0008 Word Public
DATA_TEXT 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 Class 00000H 00017H 00018H _TEXT CODE 00018H 0001FH 00008H _DATA DATA 00020H 0011FH 00100H STACK STACK
Detailed map of segments0000:0000 000D C=CODE S=_TEXT G=(none) M=MAIN.ASM ACBP=480000:000E 000A C=CODE S=_TEXT G=(none) M=TASK.ASM ACBP=480001:0008 0000 C=DATA S=_DATA G=DGROUP M=MAIN.ASM ACBP=480001:0008 0008 C=DATA S=_DATA G=DGROUP M=TASK.ASM ACBP=480002:0000 0100 C=STACK S=STACK G=DGROUP M=MAIN.ASM ACBP=74
Address Publics by Name 0000:0008 EXIT 0000:000E PRINT
Address Publics by Value 0000:0008 EXIT 0000:000E PRINT
Program entry point at 0000:0000
SEGMENT DIRECTIVESSo 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 Segdemostck 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:stckstart: 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 endsend 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 LC-
3b 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 youfind 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 benefit 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 pseudo-ops,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.