M 68000 Microprocessor
M 68000 Microprocessor
Address Registers:
There are seven general purpose address registers (A0-A6). These registers
can handle either 16-bit word or 32-bit long word operands.
Status Registers:
A 16-bit status register is divided into two 8-bit bytes : The system byte and
the user byte. Fig. 11.2 shows the bit assignments for the status register. The
system byte of the status register contains status information that is system-
related. The user byte, on the other hand, contains the condition code status
bits (X, N, Z, V and C) which are instruction or program related. Bits in the
system byte of the Status register can only be altered when the MC 68000 is
in the supervisor mode.
The Carry (C) bit is set if there is a carry out of the most significant bit
following an addition operation, or if a borrow is required from the most
significant bit during a subtraction. This status bit is also modified by certain
shift and rotate instructions.
The Overflow (V) bit is the exclusive-OR of the carries out of the most
significant and next higher-order bits of the operand following arithmetic
operations. The setting of the overflow bit signifies a magnitude overflow
since the result cannot be represented in the specified operand size.
The Zero (Z) bit is set whenever the result of an operation is zero; it is reset
otherwise.
The Negative (N) bit is the equivalent of the sign status bit provided in most
of the microprocessors. The negative bit is equal to the value of the most
significant result bit following arithmetic operations. If a signed binary
arithmetic operation is being performed, a negative status of 0 specifies a
positive or zero result, whereas a Negative status of 1 specifies a negative
result.
The Extend (X) bit is used in multiprecision arithmetic operations. When it is
affected by an instruction, it is set to the same state as the carry bit.
The three most significant bits (bits 5, 6 and 7) of the user byte of the status
register are not currently assigned and will always be zero.
The three least significant bits (bits 8, 9 and 10) of the Status register’s System
byte form the interrupt mask. The MC68000 provides seven levels of
interrupts. The level of any given interrupt is decoded from the three interrupt
pins. The interrupt priorities are numbered from 1 to 7, with level 7 having the
highest priority. The level 7 interrupt is nonmaskable and thus cannot be
disabled. Level 0 represents a “no interrupt request” condition. Levels 1
through 6 are the mask-enabled levels. For example, if you set the mask to
100 then only levels 5, 6 and 7 will be enabled; interrupt ‘levels 1 through 4
are disabled.
Bit 13 of the status register is the S-bit, which specifies whether the
MC68000 is in the supervisor or user mode of operation. When the bit is 1,
the MC68000 is in the supervisor mode, and when it is 0 the microprocessor
is in the user mode.
The most significant bit of the status register is the Trace (T) flag. If this bit is 0
then the MC68000 operates normally. However, if the bit is 1, the
microprocessor is in the trace mode of operation. After each instruction is
executed in the trace mode, a trap is forced so that a debugging program can
monitor the results of that instruction’s execution.
D0-D15 is the bi-directional 16-bit data bus. A1-A23 is the output 24-bit address
bus. The UDS (Upper Data Strobe) and LDS (Lower Data Strobe) signals
determine whether data is being transferred on either the upper (most
‘significant) byte, the lower (least significant) byte, or both bytes of the 16-bit
data bus. Table defines the significance of the UDS, LDS and Read/Write
(R/W) signals in relation to the data bus.
DTACK : This is an input signal to 68000 from an external device to notify
that the data transfer operation is completed (either read or write).
R/W : Determines whether a read or rite operation is being performed.
1 = read, 0 = write.
Function Code Signals:
FC0,FC1 and FC2 are the function code or processor cycle status outputs. These
outputs identify the type of the bus activity currently being performed by the
Motorola 68000 Pins and Signals. As summarized in. Table 11.2, five different
types of cycles are currently defined : access to either supervisor data
memory, supervisor program memory, user data memory or user program
memory, and interrupt acknowledge cycle.
System Control Signals:
BERR (BUS error) : The purpose of the BERR signal is to inform the 68000
when an external device has not responded (using the DTACK input) within
an expected amount of time during a read or write operation. When this
signal is active (low) the 68000 performs a sequence (exception processing
sequence), similar to that which it executes in response to an interrupt
request.
HALT : The HALT signal performs several functions.
1. It can be used in conjunction with the BERR signal to initiate rerunning
of bus
2. The HALT signal is also used in conjunction with the RESET signal to
initialize
The low on this signal forces 68000 to go into HALT state. In this state, at the
end of the cycle the Address Bus, Data Bus, and FC 0-FC2 signals are all placed
in the high impedance state. When 68000 is in the halted state, it does
nothing – it merely waits for the HALT signal to return high.
RESET : It is a bi-directional signal that allows processor or an external
device, to reset the system.. Processor can generate RESET signal by RESET
instruction which asserts RESET signal for 124 clock cycles. This gives
sufficient time to all the external devices to reset.
68000 Family Signals:
E, VPA, VMA : These are provided so that 6800 family devices can be easily
interfaced to the Motorola 68000 Pins and Signals (Ref. Fig. 11.4). The 6800
based systems use a synchronous method of data transfer.
E (Clock Signal) : To accomplish synchronous data transfer, a system clock
enable (E) must be distributed to all 6800 devices. The frequency of E is
1/10th of the clock input of 68000. E is low for 6 clock cycles and high for 4
clock cycles.
VPA (Valid Peripheral Address) : It is used by 6800 type devices to inform
68000 that 6800 type data transfer is required. When VPA is used, 68000
alters the data transfer timing so that it is synchronous with E.
VMA (Valid Memory Address) : If AS is still active when 68000 receives VPA,
the processor responds by VMA which is used by the addressed peripheral
device to complete the device selection.
Interrupt Pins:
IPL0, IPL1 and IPL2 are the interrupt request inputs. These three inputs are
decoded internally by the MC 68000 to determine the priority level of the
interrupt request.
Bus Arbitration Signals:
The bus arbitration is a technique used by the processor to allow bus access
to any requesting device when the processor is not currently using the bus
itself. The MC 68000 allows requesting devices to utilize the bus between
instructions and between bus cycles of a single instruction.
There are three signals associated with the bus arbitration logic as shown in
Fig. 11.6.Bus Request (BR), Bus Grant (BG) and Bus Grant Acknowledge
(BGACK). The flow chart shown in Fig. 11.5 explains how the external device
takes the control of bus.
These instructions can execute only when Motorola 68000 Instruction Set is
in the Supervisor Mode.
Some Peculiar Motorola 68000 Instruction Set:
5. LINK, UNLK : On various occasions, the exact state of the stack prior to
return from a subroutine might not be known to the programmer: Also
execution of subroutine will not store temporary data on the stack. in an
orderly manner. The LINK and UNLK instructions are used to take care of the
above problem. The link instruction dynamically allocates upto 32768 bytes
of storage on the stack and also assigns a pointer, to the top of the reserved
area. Furthermore, the LINK instruction saves the current value of the pointer.
The UNLK reverses the effects of the LINK instruction and therefore it
restores the stack and address registers.
6.PEA and LEA : Often it is desirable to pass a parameter from one procedure
to another. This may be accomplished by simply pushing the parameters on
the stack. Another technique involves pushing the address of the parameter
on the stack. 68000 allows effective address calculation with PEA (Push
Effective Address) and LEA (Load Effective Address) instructions. These
perform the address calculations automatically and place it either on the
stack or in an address register.
7.TAS : The Test and Set instructions, along with a hardware lock mechanism
can initialize a semaphore (Binary Flag). The 68000 provides the TAS
instruction to test and set a semaphore.
During execution of the TAS instruction, the 68000 activates AS signal on
68000 output pin, which can be used to lockout other processors from
accessing the semaphore.
8.STOP : After execution of the STOP instruction, the 68000 will enter into a
STOP State. STOP instruction can be executed only when 68000 is in the
Supervisor mode. In the STOP state processor does nothing. When an
exception condition is detected by the 68000, it leaves the Stop state and
will process the exception condition.
a)Absolute short
b)Absolute long.
3.Indirect Memory Addressing
a)Register Indirect
b)Post-increment Register Indirect
c)Pre-decrement Register Indirect
d)Register Indirect with Displacement
e)Register Indirect with Index and Displacement
4.Implied Register Addressing
a)Immediate
b)Quick Immediate
Any address register may be used for direct or indirect addressing, and any
register may be used as an index register.
1.Register Direct Addressing:
This addressing mode requires that the operand involved must be contained
in one of the eight Data registers (Dn) or one of the eight Address registers
(An).
a)Data Register Direct : Operand is in the data register Dn. EA (Effective
Address) = Dn
b)Address Register Direct : Operand is in the address register An. EA = An
c)Absolute Data Addressing : There are two forms of this addressing mode.
Addressing with short form is called absolute short addressing, while with the
longer form is called absolute long addressing.
d)Absolute Short : One extension word (16-bit) is necessary for this
addressing The address of the operand is the sign extended value of the
extension word.
e)Absolute Long : Two extension words (32-bit) are necessary for this
addressing Actually only 24-bits are used. The address of the operand is the
sign extended value’of the extension double word.
2.Register Indirect Addressing:
a)Address register indirect : In this mode, the address of the operand is the
contents of the specified address register.
b)Address register indirect with post increment : The post increment address
register indirect mode increments an address register by 1 for byte, 2 for
word, and 4 for long word after it is used.
c)Address register indirect with predecrement : Here the address register
contents are decremented before they are used to reference the operand.
d)Address register indirect with displacement : In this mode, the address of
the operand is the sum of the contents of the specified address register and
the sign-extended 16-bit number.
e)Address register indirect with index and displacement : In this mode, the
operand address is the sum of the specified address register, the sign
extended displacement integer in the least significant byte of the extension
word, and the contents of the index register.
3.Implied Register Addressing:
There are some instructions that implicitly refer to a specific register. These
registers are the Program Counter (PC), the Stack Pointer (SP) and the Status
Register (SR).
4.Program Counter Relative Addressing:
There are two immediate modes available with the 68000 Addressing
Modes.
1. Immediate
2. Quick Immediate
In the immediate mode, the operand data is constant data, which is part of
the instruction.
b)Instruction traps
c)Trace function
If the T-bit in the supervisor portion of the status register is set, exception
processing will be initiated after each instruction. The Trace function is used
to implement single stepping through programs.
2.Externally generated exception
Bus error : When BERR is pulled low by external logic (while HALT is high)
exception processing is initiated.
RESET : When RESET is asserted by external logic, exception processing is
initiated.
Interrupt request : This is initiated by external logic via the three interrupt
request line IPL0-IPL2.
Exception Priorities:
The different types of exceptions have different priorities. The following table
lists the types of exception according to their relative priorities, and also
defines when processing begins for different types of exception.
RESET, BERR, and Address Error are the highest priority exceptions. Any of
these exceptions will cause immediate termination of the current instruction,
even within a bus cycle. The next group of exceptions is trace, interrupt
requests, illegal/unimplemented instructions, and privilege violations. These
exceptions allow the completion of current instruction before initializing
exception processing. The lowest priority of exceptions are TRAP, TRAPV,
CHIC, and Divide by zero. These instructions can initiate exception
processing as part of their normal execution.
Fig. 11.9 shows Exception Vector Table. The table is organized as 256 double
word (32-bit) vectors. Each vector is a 32-bit address which will be loaded
into the program counter as part of the exception processing sequence.
Exception Processing Sequence:
Steps :
1. The Status Register contents are copied into an internal register.
2. The S-bit, in the Status Register is set, thus placing the MC68000 in
the Supervisor mode of operation.
3. 3.The T-bit in the Status Register is reset to disable trace operation.
4. The Program Counter contents are pushed onto the Supervisor stack.
The contents of SSP will be decremented by four since four bytes are
required to store the 32-bit contents of PC.
5. Status register contents. are pushed onto the Supervisor stack; SSP
contents are decremented by two, since the Status register is a 16-bit
register.
6. The new Program Counter contents are taken from the appropriate
location in the interrupt vector table.
7. Instruction execution then begins at the location indicated by the new
contents of the Program Counter; this will be the first instruction of the
exception processing program you have provided for that particular
type of exception.
B) Exception Processing Sequence for Bus Error and Address Error:
Steps :
1. The contents of the Status register are copied into an internal register.
2. The S-bit in the Status register is set, placing the MC68000 in the
Supervisor
3. The T-bit in the Status register is reset to disable trace operation.
4. The contents of the Program Counter are pushed onto the Supervisor
stack and the System Stack Pointer (SSP) is decremented by four.
5. The contents of the Status register are pushed onto the Supervisor
stack and the contents of SSP are decremented by two.
6. The contents of the MC68000’s instruction register, which constitute
the first word of the instruction that was in progress when the bus
error occurred, are pushed onto the Supervisor stack and SSP is
decremented by two.
7. The 32-bit address that was being used for the bus cycle which was
terminated is also pushed onto the Supervisor stack and SSP is
decremented by four.
8. A word which provides information as to the type of cycle that was in
progress at the time of the error is pushed onto the Supervisor stack
and SSP is decremented by two.
9. The Program Counter contents are taken from the appropriate
interrupt vector-either the bus error vector or address error vector of
the exception vector table.
10. Instruction execution resumes at the location indicated by the new
contents of the Program Counter.
C) Exception Processing Sequence for RESET:
Steps :
1. The S-bit in the Status register is set; placing the MC68000 in the
Supervisor
2. The T-bit in the Status register is reset to disable the trace function.
3. All three interrupt mask bits in the Status register are set, thus
specifying the interrupt priority mask at level seven,
4. Supervisor Stack Pointer (SSP) is loaded with the contents of the first
four bytes of memory (addresses 000000-000003).
5. The Program Counter (PC) is loaded from the next four bytes of
memory (addresses 000004-000007).
6. Instruction execution commences at the address indicated by the new
contents of the Program Counter, which should reference your power-
on reset initialization program.
D) Exception Processing for Interrupt Request:
The requesting device places a byte of exception vector data on the lower
byte of the data bus (D0-D7). The Data Transfer Acknowledge (DTACK) signal
is used to effect this transfer of data just as with a normal read cycle.
Throughout the interrupt acknowledge cycle, the Function Code Outputs
(FC0-FC2) will be set high since these represent the interrupt acknowledge
function code.
Address lines A1-A3 reflect the status of IPL0-IPL2.
1. The contents of the program counter are pushed onto the supervisor
stack and SSP is decremented by four.
2. The contents of the status register are pushed onto the supervisor
stack and SSP is decremented by two.
3. The program counter is loaded with four bytes from the appropriate
location in the exception vector table.
4. Instruction execution resumes at the location indicated by the new
contents of the program counter.
Spurious Interrupt:
Seven vector locations are used in the exception vector table for autovectors,
corresponding to seven interrupt priority levels. These vectors will be used if
the device requesting an interrupt responds to the interrupt
acknowledge bus cycle by asserting the Valid Peripheral Address (VPA),
signal instead of supplying a byte of vector data.
TABLE OF CONTENTS
The Motorola 68000 processor has 18 registers that are directly accessible by the
user. Those are:
Figure 9.1
The 68000 User Registers
Here are the important points you should know about the 68000 user registers:
● All the data registers, all the address registers and the Program Counter are
32-bits (4 bytes) wide.
● The Status Register, or SR, is 16 bits (2 bytes) wide. Only the low-order
byte of the SR, which is called the CCR, or Condition Code Register, can
be accessed by the user. The high-order byte of the SR, the so-called System
Byte, can be seen and accessed only by the Operating System during special
emergency cases called interrupts and exceptions which will be discussed in
the second part of the course. Until then, we can forget about the System
Byte and work only with the CCR.
● The data registers are used to store any data. They are general-
purpose registers, because they haven't been reserved for any specific task
by the 68000 chip designers, and they are interchangeable, in the sense that
whatever you can do with register Di you can also do with register Dj. There
are some rare instructions that require a specific register as an operand, but
those are really special cases and we won't see many of them.
● The address registers are used to store addresses of locations in main
memory. In other words, address registers are pointers to locations in
memory. Registers A0 to A6 are general-purpose and interchangeable, just
like their cousins the data registers. Register A7, also referred to as SP, is
more special: it is the processor's stack pointer. It is used by the system to
maintain a stack of subroutine return addresses (to be discussed later). You
are free to access and modify the contents of register of A7, just like any
other address register, but you always have to bear in mind what A7 is used
for. Note that any address register can be used to maintain a stack. Thus, it
is possible to handle several stacks at the same time. Note also that although
you can store an address of a memory location in a data register, you cannot
use a data register as a pointer to that location. The differences between
address and data registers will become more clear when we start looking at
the instructions that use those registers.
As we said, most of the 68000 user registers are 32 bits wide, i.e. they can
accommodate 32 bit numbers. In order to be able to reference a particular bit in the
register, each bit is given a number. The convention is to number the bits from 0 to
31, bit 0 being the least significant, i.e. the rightmost bit, and bit 31 being the most
significant, i.e. the leftmost bit. In other words, the bit numbering starts at 0 at the
right end and goes from right to left in increasing order (see figure 8.2).
Figure 9.2
The CCR
Let's now look in more detail at the CCR. The CCR is a very important piece of
hardware because it allows conditional behavior (i.e. high level constructs of the
kind if X then Y; else Z;) to be implemented, and the Control Unit often bases its
decisions on the contents of the CCR.
Figure 9.3
The CCR
The CCR is an 8 bit register, but only bits 0 to 4 are actually used. Bits 5, 6 and 7
are always ignored, and their value doesn't matter. It may be assumed that they are
always set to 0. Bits 0 to 4 are flags, each of whom is affected in specific ways by
the various operations performed by the CPU. Almost every instruction that is
executed by the CPU forces an update on the value of one or more CCR bits.
● Bit 0, known as the C bit, is the carry bit. It is set (to 1) whenever the result
of an operation generates a carry coming from the most significant bit of the
result, and cleared (set to 0) otherwise.
● Bit 1, known as the V bit, is the overflow bit. It is set if an operation results
in arithmetic overflow in terms of two's complement arithmetic, i.e. when
the result of an operation is too large or too small to be handled by the
destination operand.
● Bit 2, known as the Z bit, is the zero bit. It is set when the result of an
operation is zero (i.e. all the bits of the result are 0), and is cleared
otherwise.
● Bit 3, known as the N bit, is the negative bit. It is set when the result of an
operation is negative, and cleared when the result is positive. In other words,
the N bit holds the value of the most significant bit of the result of the
operation (remember two's complement numbers).
● Bit 4, known as the X bit, is the extend bit. It often has the same value as the
C bit. It is used in multiple-precision arithmetic, i.e. arithmetic involving
numbers greater than 32 bits in size, to hold the carry.
We'll come back to the CCR when we start examining the 68000 assembler
instructions. But before attacking the actual 68000 language, let's examine the
organization of the 68000 memory space.
Back to top of page
The memory space of the 68000 processor is one big linear array of memory
locations, each of them being able to store one byte. The memory is said to
be byte-addressable, i.e. each byte within the memory has its own unique address
and can be accessed directly. Note that the memory of the 68000 is not bit-
addressable, which means that you cannot access data in memory bit by bit. That
also means that you cannot start reading memory in the middle of a memory
location, only at the beginning. Later versions of the 68000 processor, like the
68020, allow you to overcome those access restrictions.
By convention, the memory locations are numbered in a big-endian order. If the
memory space is represented vertically, then the memory locations with a small
address number are found at the top, and those with a big address number are
found at the bottom.
Figure 9.4
The linear memory of the 68000.
Each memory location is one byte in size. The 32 bit addresses are given as 8
hexadecimal digits.
As you know, the 68000 has a 32 bit Program Counter and 32 bit address registers.
This is so because addresses of locations in memory are 32 bit numbers, and
consequently you can address up to 232 locations, i.e. 232 bytes, or 4 gigabytes
(each memory location is one byte). In order to do so, you need a 32 bit address
bus to carry a 32 bit address number from the CPU to memory and vice versa. The
68000 and its successors the 68020, the 68030, etc. have indeed a 32 bit address
bus. However, the 68000 processor as such is a special case, because due to space
restrictions only the first 24 lines of the address bus (address lines 0 to 23) actually
leave the chip and connect it to memory. Address lines 24 to 31 are simply not
brought off-chip. Since one address line transports one bit, that means that only
bits 0 to 23 within a 32 bit address are used to specify a memory location. For
example, if you write a program to access locations 8012345616 and 4512345616,
you will access the same physical location. The state of bits 24 to 31, represented
by the two leftmost hexadecimal digits, simply doesn't matter. In other words, the
68000 behaves as if its addresses are 24 bit quantities, not 32 bit quantities. That
means that the addressable memory space of the 68000 is in practice only 224 bytes,
or 16 megabytes. Note that addresses in the 68000 are still represented as and
stored as 32 bit numbers, even if only the first 24 bits of those numbers are actually
used.
This limitation does not exist with the newer members of the 68000 family. The
68020, 68030 and 68040 have a fully connected 32 bit address bus and a true
address space of 4 gigabytes.
Figure 9.5
The address bus of the 68000
The 68000 assembly language, like any other assembly language, is composed of
two types of statements: the assembler directive and the executable instruction. An
executable instruction is one of the processor's valid instructions which is
translated by the Assembler into machine language and actually executed by the
CPU. An assembler directive, on the other hand, is just an indication to the
Assembler about the program and its environment. Assembler directives
are not translated into machine language.
The most common 68000 instructions and assembler directives, as well as the most
common addressing modes are described in detail in the NeXT textbook written by
David Cloutier. We will therefore simply introduce the most important topics
covered in the NeXT textbook, and we'll concentrate on giving some examples of
programs, Motorola syntax, and interfacing assembly language with C.
Let's introduce some instructions, and then we'll put them to use in a simple
program.
Here is how some of the above instructions can be used in an assembly language
program. Consider the following C code fragment:
x = 0;
y = Q;
if (y == 5)
x = x + y;
y = y - 6;
x = y;
MOTOROLA SYNTAX
MOVE #0,D0 x = 0; loads D0 with the value 0;
* we use D0 to represent x
MOVE Q,D1 y = Q; loads D1 with Q;
* we use D1 to represent y; Q is a reference
* to a memory location
CMP #5,D1 Compare the number 5 with D1 (y)
BNE EXIT_IF If not equal, then branch to(go to)label
EXIT_IF
ADD D1,D0 x = x + y; this statement is executed
* only if y == 5
EXIT_IF SUB #6,D1 y = y - 6; subtracts 6 from D1
MOVE D1,D0 x = y; moves the value of y (D1) into x (D0)
MILO SYNTAX
move #0,d0 |x = 0; loads D0 with the value 0;
# we use D0 to represent x
move q,d1 |y = Q; loads D1 with q;
# we use D1 to represent y; q is a reference
# to a memory location
cmp #5,d1 |Compare the number 5 with D1 (y)
bne exit_if |If not equal, then branch to(go to)label
exit_if
add d1,d0 |x = x + y; this statement is executed
# only if y == 5
exit_if: sub #6,d1 |y = y - 6; subtracts 6 from D1
move d1,d0 |x = y; moves the value of y (D1) into x (D0)
You certainly recognize the 4 fields in the layout of this program: the label field,
the instruction field, the operands field, and the comments field. Here are some
points to note about the above example:
● You are maybe starting to appreciate the presence of 8 general purpose data
registers which provide you with a lot of on-chip working space. Note that
the above operations could have been implemented by using memory
locations instead of data registers, but the program would have been much
slower.
There is nothing wrong with the algorithm of the above program; however, the
program written as such would probably not run on a real machine. This is because
it suffers from one major oversimplification: the size of the operands is not
indicated anywhere.
The 68000 allows you to work with operands of 3 different sizes: bytes, words, and
longwords; registers can be used to store bytes, words, or longwords. For example,
when you work with character data, you may want to work with bytes; if you work
with integers you'll probably work with words, or longwords. The Assembler who
translates your program into machine code has no way to know when you want to
perform a longword operation and when a word operation unless you explicitly
specify its size.
Operations on 32 bit numbers are specified by appending the suffix .L to the end
of the instruction mnemonic, operations on 16 bit numbers are specified by
appending a .W suffix to the end of the instruction mnemonic, and operations on 8
bit numbers are specified by appending a .B suffix to the end of the instruction
mnemonic.
Assembly language
RTL form Picture
instruction
[D1(0:7)] <-
MOVE.B D0,D1
[D0(0:7)]
[D1(0:15)] <-
MOVE.W D0,D1
[D0(0:15)]
[D1(0:31)] <-
MOVE.L D0,D1
[D0(0:31)]
It is very important to keep in mind that only the bits specified by the size of the
operation are affected by the operation. For example, if register D4 contains the
number FFFFFFFF16 and you perform the operation
ADD.W #1,D4
the result (which will be stored in D4 and overwrite the previously held value)
will not be 0000000016, as it may be expected at first sight, but rather FFFF000016.
Furthermore, the value of the CCR bits calculated after an operation is also
determined by the size of the operation. Thus, in the above case:
● the Z bit will be set, because the operation yielded a zero result, even if D4
as a whole does not contain 0.
● the C bit will be set since a carry was generated from bit 15.
● the N bit will be cleared since bit 15 is 0.
● the V bit will be set since the operation resulted in arithmetic overflow:
FFFF16 + 000116 = 1000016, which cannot be stored within a 16 bit word. We
started with FFFF16 as operand, we end up with 0000 as result. The sign of
the result has changed, that is enough to set the V bit.
● Note that the CPU doesn't know whether you perform signed or unsigned
arithmetic, or any other kind of operation; it updates its CCR bits blindly,
and it's up to you to decide what use to make of the CCR bits.
The size of an operation is specified in a slightly different way in the Milo syntax.
The Motorola syntax uses a dot to separate the instruction from the size, while the
Milo syntax does not use a dot, it simply appends the letter of the size to the end of
the instruction.
Motorola
Milo syntax
syntax
MOVE.W movew
Let's now look at another simple program which specifies its operand sizes.
Consider the following fragment of C code:
/*
We assume we have the following declarations:
char C = 'A';
int X = 0x100;
long int Y = 0x2000A111;
X++ ;
if (C != 'B')
X -= 0x5;
Y += 0x9001;
MOTOROLA SYNTAX
* First, fetch the data from memory
*
MOVE.W X,D1 Fetch X and place it in D1. Note: X is 2
bytes!
MOVE.L Y,D2 Fetch Y and place it in D2. Note: Y is 4
bytes!
MOVE.B C,D3 Fetch C and place it in D3. Note: C is 1
byte!
*
ADD.W #1,D1 Executes X++
CMP.B #$42,D3 Compares the ASCII code for 'B' (0x42) to
C
BEQ EXIT_IF Go to label EXIT_IF,thus skipping the next
instruction,
* if C == 'B'
SUB.W #$5,D1 Executes X -= 0x5
EXIT_IF ADD.L #$9001,D2 Executes Y += 0x9001
MILO SYNTAX
# First, fetch the data from memory
#
movew X,d1 |Fetch X and place it in d1. Note: X is 2
bytes!
movel Y,d2 |Fetch Y and place it in d2. Note: Y is 4
bytes!
moveb C,d3 |Fetch C and place it in d3. Note: C is 1
byte!
#
addw #1,d1 |Executes X++
cmpb #0x42,d3 |Compares the ASCII code for 'B' (0x42) to
C
beq exit_if |Go to label EXIT_IF,thus skipping the
next
# instruction, if C == 'B'
subw #0x5,d1 |Executes X -= 0x5
exit_if: addl #0x9001,d2 |Executes Y += 0x9001. Note Milo indicates
# hex numbers with 0x, just like in C
This program is intended to show you the importance of operand sizes. Let's
assume that C, X, and Y are originally stored in memory as shown in the following
diagram:
Figure 9.6
The above diagram represents an area of memory where our 3 variables are stored.
Note that all numbers are in hexadecimal. Note also the big endian ordering of
memory. Variable C, which holds the ASCII code for A (41 hexadecimal), is
stored in location 1001. C takes up only one location, because char type variables
are one byte in size. Variable X is an int, it is 2 bytes in size, therefore it takes the
next two locations 1002 and 1003. Y is a long int, it is stored in 4 consecutive
locations from 1004 to 1007.
Note very carefully the order in which bytes are transferred from memory to
registers and vice versa: the most significant byte is stored at the smallest
address in memory, and the least significant byte is stored at the greatest
address. A word at location N occupies byte addresses N and N+1. A longword at
location N occupies byte addresses N, N+1, N+2 and N+3.
What would have happened if we had done, let's say, MOVE.W Y,D2? There is
nothing illegal with such an instruction, but it would be incorrect in our case. The
least significant word of D2, i.e. bits 0 to 15, would hold 200016 and bits 16 to 31
would stay untouched.
It is important to remain consistent with the operand sizes throughout the entire
program. When you know that your D2 holds a 32 bit number, keep on performing
32 bit operations on that register until you start using the register for something
else. What would have happened if you had performed ADD.W #$9001,D2 instead
of ADD.L #$9001,D2? After the ADD.W #$9001,D2 instruction, register D2 will hold
the value 2000311216. Your program will not crash because of that, but it will give
you an incorrect result. The correct result should be 2001311216, but you get
2000311216 because a word operation affects only bits 0 to 15 and leaves bits 16 to
31 unaffected. Thus, the carry from bit 15 which should normally be added to bit
16 goes instead to the C bit of the CCR.
In the above example, D3 and D1 are not used to their full capacity. In fact, it is
possible to use bits 8 to 31 in D3 and bits 16 to 31 in D1 to store other useful
information not related to this program. Such "multiple-purpose" use of data
registers is perfectly legal, but highly discouraged because it is confusing and may
lead to errors.
Back to top of page
1) How does the computer know exactly where in memory variables C, X, and Y
are actually stored?
2) And how do you declare variables in assembly language? What is the assembly
language equivalent of char C = 'A'; ?
3) How do you declare constants?
4) How can the programmer participate in the management of memory space? How
do you indicate the start and the end of a program?
Question 1:
The letters C, X, and Y are identifiers that refer to address of locations in memory,
in the above example C refers to 1001, X refers to 1002 and Y refers to 1004. In
general, programmers don't have to worry about the actual numerical address, as
the computer automatically takes care of that, as we'll see later. However, if for
some reason you want to store one byte of information in a specific location, e.g.
100116, then you can use the assembler directive EQU to equate the name C to
the value 100116. The syntax of the EQU directive:
This way, you give a name to the number 100116, and you can use the name and the
actual numerical value interchangeably in calculations and other expressions.
In the above example, we use EQU to equate the name C to the absolute address
$1001. The EQU directive can be used in other circumstances as well. For example
you can have
LENGTH EQU 35
WIDTH EQU 15
AREA EQU LENGTH*WIDTH
Later in your program, you can use the identifiers instead of the actual numerical
values they represent, and the assembler will take care of replacing the identifiers
with the numerical values. The use of EQU is very similar to the use
of #define in C. It makes the program more clear and readable. Just beware of
illegal forward references:
LENGTH EQU 35 This code won't work
AREA EQU LENGTH*WIDTH because at this stage
WIDTH EQU 15 WIDTH hasn't been declared yet
Note that identifiers equated to a value through the EQU directive are still to be
treated as literals, and prefixed by the # sign, like in MOVE.W #LENGTH,D4.
The equivalent of EQU in the Milo syntax is the .set directive, its syntax is:
.set identifier,expression
Question 2:
The use of the EQU directive is not the method of choice for declaring variables
and constants. There is a specific assembler directive used for purposes equivalent
to variable declarations in high-level languages. This directive is DS and is
qualified by .B, .W or .L. Its syntax:
NAME DS.S <amount of storage space> where S stands for size, i.e. B, W, or L.
The Milo syntax for the DS directive is somewhat quite different. In Milo, if you
want to reserve one word for a variable called milovar and initialize it to the value
16 you will write
milovar: .word 16
Don't be confused by the number 16: here you don't reserve 16 words for the
variable milovar, you reserve one word and give it the initial value 16. For more
details about Milo directives, consult the NeXT textbook.
Question 3:
Constants are declared by the directive DC, which means "define constant". DC is
qualified by .B, .W or .L, depending on the storage space that the constant is meant
to occupy . Its syntax:
NAME DC.S constant where S stands for size, i.e. B, W, or L. All the occurences
of NAME will be replaced by the corresponding constant.
The constant that is being stored with the DC directive can be a decimal number, a
hex number prefixed with $, a binary number prefixed with %, or an ASCII string
enclosed in single quotes. You can store constants in consecutive locations by
separating them with a comma. We'll show you shortly how they are actually
stored in memory.
You can combine the DC directive with other directives, for example
To declare constants with Milo, you could use the same directives used for variable
declaration, i.e. .byte, .word and .long. Consult with the NeXT textbook for
details.
Question 4:
There is a directive called ORG, the origin directive, whose operand specifies
the absolute address of the beginning of the area of memory where a program and
its associated data are located. Its syntax:
ORG <address>
An ORG directive can be located at any point in the program, it simply resets the
value of the location counter that keeps track of where the next item is to be
located in the processor's memory.
ORG $001200 Sets the origin of the data area at address $001200
DC.B 12 The value $0C is stored in one byte of memory
DC.W 3,$3B2 The values $0003 and $03B2 are stored
in consecutive locations, each of them taking up 2
bytes
DC.L $DEADBEEF The value $DEADBEEF (4 bytes) is stored in memory
DC.L $3B2 The value $000003B2 is stored in 4 bytes of memory
DC.B 'Arnie' The ASCII characters are stored as 5 bytes
Here is how the memory of the processor would look like (note: all numbers are in
base 16):
Addre Conten
ss ts
00120
0C
0
00120
00
1
00120
03
2
00120
03
3
00120
B2
4
00120
DE
5
00120
AD
6
00120
BE
7
00120
EF
8
00120
00
9
00120
00
A
00120
03
B
00120
B2
C
00120
41
D
00120
72
E
00120
6E
F
00121
69
0
00121
65
1
A similar directive exists in Milo, its syntax is: .org <address>. Its use is
essentially the same.
The ORG directive is thus used to specify the beginning of a program in memory.
One program can have multiple origins, e.g. one for the data region and one for the
instructions region. Many assemblers, however, don't require the use of the ORG
directive, and they locate the program in memory wherever there is space for it. In
fact, the use of the ORG directive to specify an absolute location for the program in
memory can be very dangerous, since it can overwrite another program or data
region already present at that address. It is therefore recommended not to use ORG
unless you are certain there is no other program running on your computer at the
same time, which happens virtually never on modern systems.
In order to know where the source code of a program ends, some Assemblers
require the presence of the END directive at the last line of the assembly language
program. This directive simply tells the Assembler that there are no more
instructions or directives to be assembled. Most of the Assemblers who employ
this directive use it without parameters, but if you use the University of Teesside
cross-assembler then you need to supply a single parameter: the address in memory
where the code is located, i.e. the point where it is to start executing. This address
is in general the same as the one supplied by the ORG directive.
Back to top of page
Now we return to the example we saw earlier in section 8.3.2 and we'll give the
full assembly language program that corresponds to the following C code:
/*
We assume chars are 1 byte in size,
ints are 2 bytes in size,
and long ints are 4 bytes in size;
*/
char C = 'A';
int X = 0x100;
long int Y = 0x2000A111;
X++ ;
if (C != 'B')
X -= 0x5;
Y += 0x9001;
Finally, here is the Milo version of the same program. You have probably noted
that the most important difference between the Motorola and the Milo syntax is
found in the way assembler directives are written and used. Once again, you are
encouraged to refer to the NeXT textbook for information about the Milo syntax.
.data
#Reserve one byte for variable C and initialize it to the ascii value of
A
C: .ascii "A"
.even
#Reserve one word for variable X and initialize it to 0x0100
X: .word 0x100
.text