Amiga Machine Language (Chapter 2)
A amigaspirit.hu - pegasos.hu Wiki wikiből
The following text is part of the Amiga Machine Language tutorial.
Chapter 2, The MC68000 processor
The Amiga`s MC68000 processor is a 16/32 bit processor, which means that while it can process data of 32 bits, it "only" has a 16 bit data bus and a 24 bit address bus. Thus, it can access 2^24=16777216 bytes (or 16 Mbytes) of memory directly.
The Amiga 68000 processor, running at 7.1 megahertz, is quite fast, which is required for a computor with a workload as heavy as the Amiga`s. The Amiga also processes a number of custom chips that greatly ease the workload of the processor. These custom chips manage sound in/output, graphics and animation, thus freeing the processor for calculations.
In addition to the standard RAM, the processor contains internal memory called registers. There are eight data registers (D0-D7), eight address registers(A0-A7), a status register(SR),two stack pointers, a user stack pointer, a system stack pointer (USP and SSP) and the program counter(PC).
- Register Sizes: The data registers,the address registers, and the program counter are all 32 bits, while the status register is 16 bits. These registers are located directly in the processor so they are not accessed the same way memory would be accessed. There are special instructions for accessing these registers.
- Data Registers: The data registers are used for all kinds of data. They can handle operations with bytes (8 bits), words (16 bits) and longwords (32bits).
- Address Registers: The address registers are used for storing and processing addresses. This way they can be used as pointers to tables, in which case only words and longwords operations are possible.
- Stack Pointer: The address register A7 plays a special role:this register is utilized as the Stack Pointer(SR) by the processor, and thus is not recommended for normal use by the programmer. Which of the two possible stacks is being pointed to depends on the present mode of the processor, but more about that later. The stack, to whose actual position the stack pointer is pointing, is used to store temporary internal data. The stack works similar to a stack of notes on your desk: the note that was added to the stack last is the first one to come off the stack. This type of stack is known as LIFO (Last in,First out). There is another type of stack, the FIFO (First in,First out) which is not used by the processor itself. How these registers and the SP can be manipulated, or how to work with the stack, is presented in the next chapter. Lets continue with the registers for now.
- Status Register: The status register plays an important role in machine language programming. This 16-bit quality (word) contains important information about the processor status in 10 of its bits. The word is divided into two bytes, the lower byte (the user byte) and the upper byte (the system byte). The bits that signify that certain conditions are refered to as flags. This means that when a certain condition is present, a particular bit is set. The user byte contains five flags, which have the following meaning:
Bit Name Meaning ----------------------------------------------- 0 (C,Carry) Carry bit,modified by math calculation,and shift instructions. 1 (V,Overflow) Similar to carry,indicates a change of sign,in other words,a carry from bit six to bit seven. 2 (Z,Zero) Bit is set when the result of an operation is zero. 3 (N,Negative) Is set when the result of an operation is negative. 4 (X,Extended) Like carry,is set for arithmetic operations. 5-7 Not used.
The system byte contains five significant bits:
Bit Nane Meaning ----------------------------------------------- 8 I0 Interupt mask.Activates interupt 9 I1 levels 0 to 7,where 0 is the lowest 10 I2 and 7 is the highest priority. 11 not used. 12 not used. 13 (S,Supervisor) This bit indicates the actual pocessor mode(0=User,1=Supervisor mode). 14 not used. 15 (T,Trace) If this bit is set,the processor is in single step mode.
Here's an overview of the status word;
bit : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 name : T - S - - I2 I1 I0 - - - X N Z V C
Don't let the new terms, like mode and interupt confuse you. We'll talk about these in greater detail in the chapter dealing with the operating conditions of the processor.
In the standard Amiga 500's and 1000's, the processor has over 512k of RAM available. The Amiga 2000 has one mega-byte of RAM that can be accessed by the processor. How does the processor access all this memory? If you're programming in BASIC you don't have to worry about memory management. You can simply enter MARKER%=1, and the value is stored in memory by the BASIC interpreter. In assembler, there are two ways of accomplishing this task:
- Store the value in one of the data or address registers,or
- Write it directly into a memory location.
To demonstrate these two methods let's get a little ahead and introduce a machine language instruction, which is probably the most common: MOVE. As its name states,this instruction moves values. Two parameters are required with the instruction: source and destination. Lets see what the example from above would look like if you utilize the MOVE instruction...
- MOVE #1,D0 - This instruction moves the value 1 into data register D0. As you can see,the source value is always entered before the destination. Thus, the instruction MOVE D0,#1 is not possible.
- MOVE #1,$1000 - deposits the value 1 in the memory location at $1000. This address was arbitrarily chosen. Usually addresses of this form won't be used at all in assembler programs, since labels that point to a certain address are used instead. Thus, the more common way of writing this would be:
... MOVE #1,MARKER ... MARKER:DC.W 1
These are actually two pieces of program: the first part executes the normal MOVE instruction whose destination is `MARKER`. This label is usually defined at the end of a program and specifies the address at which the value is stored. The parameter DC.W 1 is a pseudo op, a pseudo operation. This means that this isn`t an instruction for the processor, but an instruction for the assembler. The letters DC stand for `DeClare` and the suffix .W indicates that the data is a Word. The other two suffix alternatives would be .B for a byte(8 bits) and .L for a long word(32 bits). This suffix (.B.W or.L) is used with most machine language instructions. If the suffix is omitted,the assembler uses .W(word) as the default parameter. If you wanted a long word, you`d use an instruction that looks something like this: MOVE .L #$1234678,D0 where as an instruction like MOVE.B #$12,D0 would be used for a byte of data. However,with this instruction there`s one thing you must be aware of...
- CAUTION: If the memory is accessed by words or long words,the address must be even (end digit must be 0,2,4,6,8,A,C,E)!
Assemblers usually have a pseudo-op, `EVEN` or `ALIGN`, depending on the assembler, that aligns data to an even address. This becomes necessary in situations similar to this:
... VALUE1: DC.B 1 VALUE2: DC.W 1
If the VALUE1 is located at an even address, VALUE2 is automaticaly located at an odd one. If an ALIGN (EVEN) is inserted here, a fill byte(0) is inserted by the assembler, thus making the second address even.
... VALUE1: DC.B 1 ALIGN VALUE2: DC.W 1
Back to the different ways of addressing. The variations listed above are equivalent to the BASIC instruction MARKER%=1 where the % symbol indicates an integer value. Lets go a step further and translate the BASIC instruction MARKER%=VALUE% into assembler. You`ve probably already guessed the answer right?
MOVE VALUE,MARKER ... ... MARKER: DC.W 1 VALUE : DC.W 1
In this case, the contents in the address at VALUE are moved into the address at MARKER. With the help of these simple examples, you`ve already become familiar with four different ways of addressing, in other words, ways that the processor can access memory. The first is characterized by the number sign (#) and represents a direct value. Thus, this method is also known as direct addressing, and is legal only for the source parameter!
A further method in which a direct address (in our case, `MARKER` and `VALUE`) can be specified is known as absolute addressing. This method is legal for the source parameter as well as for the destination parameter. This method can be divided into two different types, between which the programmer usually does'nt notice a difference. Depending on whether the absolute address is smaller or larger than $FFFF, in other words if it requires a long word, it is called absolute addressing (for addresses above $FFFF) or otherwise absolute short addressing. The assembler generally makes the distinction between these two types, and thus, only general knowledge of absolute addressing is required.
The fourth method of addressing that you've encountered so far is known as DATA REGISTER DIRECT. It was the first one introduced(MOVE #1,D0) in conjunction with direct addressing, the only difference being that this type accesses a data register (such as D0). These four methods aren't the only ones available to the MC68000 processor, in fact there are a total of 12. One other variation called ADDRESS REGISTER DIRECT, is almost identical to data register direct, except that it accesses the address register instead of the data register. Thus, you can use MOVE.L #MARKER,A0 to access the address register A0 directly.
You now know five ways to address memory with which quite a bit can be accomplished. Now, lets tackle something more complicated and more interesting. Lets take another example from BASIC:
10 A=1000 20 POKE A,1
In this example the first line assigns the value 1000 to the variable A. This can be done in assembler as well: MOVE.L #1000,A0. In the assembler version the absolute value of 1000 is stored in the address register A0. Line 20 doesn't assign a value to the variable A itself, but rather to the memory location at the address stored in A. This is an indirect access, which is quite easy to duplicate in assembler:
MOVE.L #1000,A0 ;bring address in A0 MOVE #1,(A0) ;write 1 into this address
The parentheses indicates an addressing known as ADDRESS REGISTER INDIRECT. This method only works with address registers, since a 'data register indirect' does not exist. There are several variations of this method. For instance, a distance value can be specified,which is added to the address presently located in the address register before the register is actually accessed. The instruction MOVE #1,4(A0), if applied to the example above, writes the value 1 into the memory cell at 1000+4=1004. This distance value can be positive or negative. Thus, values from -32768 to +32768 are accepted. This specific variation of addressing is called ADDRESS REGISTER INDIRECT WITH A 16 BIT DISPLACEMENT value. There is another very similar variation: ADDRESS REGISTER INDIRECT WITH AN 8 BIT INDEX value. While this variation is limited to 8 bits, it also brings another register into play. This second register is also a distance value, except that it is a variable as well.
We'll try to clarify this with an example. Lets assume that a program includes a data list that is structured like this:
... RECORD: DC.W 2 ;number of entries -1 DC.W 1,2,3 ;elements of list
We'll use MOVE.L #RECORD,A0 to load the list into the address register A0. Then you can use MOVE (A0),D0 to pull the number of in the list into the data register. To access the last element of the list only one instruction is needed. The whole thing looks something like this:
CLR.L D0 ;erase D0 completely MOVE.L #RECORD,A0 ;address of list in A0 MOVE (A0),D0 ;number of elements -1 in D0 MOVE 1(A0,D0),D1 ;last element in D1 ... RECORD: DC.W 2 ;number of entries -1 DC.W 1,2,3 ;elements of list
This last instruction accesses the byte that is located at 1+A0+D0 in other words,the record +1 where the data begins plus the contents of D0 (in this case 2). This method of accessing is very useful. It works exquisitely for the processing of tables and lists, as the example demonstrates. If no distance value is needed, simply use a distance value of zero, which some assemblers automatically insert as the default value if for instance only MOVE (A0,D0) is entered. The latter two methods have a third variation, which has its own characteristic trait. It dosen't utilize an address register, but uses the Program Counter(PC) instead. The program counter with displacement method proves useful when a program must function without any changes in all address ranges. The following two statements (in the 15 bit limits) have the same effect:
- MOVE MARKER,D0
- MOVE MARKER(PC),D0
This method is actually rather imprecise, since the first instruction specifies the actual address of the marker with MARKER while the second line specifies the distance between the instruction and the marker. However since it would be quite cumbersome to constanly calculate the distance,the assembler takes this task off our hands and calculates the actual value automatic. Lets examine the difference between the two instructions. In a program they'll accomplish the same thing, although they are interpreted as two completely different things by the assembler. You'll assume a program being at the address $1000 and the marker is located at $1100. The generated program code looks something like this:
$001000 30 39 00 00 11 00 MOVE MARKER,D1
$001000 30 3A 00 FE MOVE MARKER(PC),D1
As you can see, the generated code of the second line is two bytes shorter than the first line. In addition, if you were to shift this code to the address $2000, the first version still accesses the memory at $1100, while the second line using the PC indirect addressing accesses memory at $2000 correctly. Thus, the program can be transferred to almost any location. This, then, is PROGRAM COUNTER WITH 16 BIT DISPACEMENT value. As we mentioned, there is also PROGRAM COUNTER WITH 8 BIT INDEX value, which permits a second register as a distance value, also known as offset.
There are two addressing modes left. These two are based on indirect addressing. They offer the capability of automatically raising or lowering the address register by one when memory is accessed with address register indirect. To automatically increase the register, you'd use ADDRESS REGISTER INDIRECT WITH POST-INCREMENT. The address register raises this by the number of bytes used AFTER accessing memory. Thus if you write:
MOVE.L #1000,A0 MOVE.B #1,(A0)+
the 1 is written in the address $1000 and then A0 is raised by one. Instructions like this are very helpful when a memory range is to be filled with a specific value (for instance when the screen is cleared). For such purposes the instruction can be placed in a loop ... which we'll get to later.
The counter part to post increment is ADDRESS REGISTER INDIRECT WITH PRE-DECREMENT. In this case the specified address register is lowered by one BEFORE the access to memory. The instructions:
MOVE.L #1000,A0 MOVE.B #1,-(A0)
writes 1 in the address 999, since the contents of A0 is first decremented and the 1 is written afterwards. These two methods of addressing are used to manage the stack pointer(SP). Since the stack is filled from top to bottom, the following is written to place a word (s.aD0) on the stack:
and to remove it from the stack, again in D0:
This way the stack pointer always points to the byte last deposited on the stack. Here, again, you'll have to be careful that an access to the stack with a word or a long word is always on an even address. Thus, if you're going to deposit a byte on the stack, either use a whole word or make sure the byte is not followed by a JSR or BSR. The JSR or BSR instructions deposit the addresses from which they stem on the stack in the form of a long word. In the code above,the SP is generally replaced by the address register A7 in the program code, since this register is always used as the SP. Thus,you can write A7 as well as S,the resulting program is the same. However, we recommend the use of SP, since this makes the code somewhat easier to read. After all, quite often you'll want to employ your own stacks, in which case the difference between the processor stack and your own stacks become very important.
These are the twelve ways of addressing the MC68000 processor, here is a summary:
No Name Format -- ---- ------ 1 data register direct Dn 2 address register direct An 3 address register indirect (An) 4 address register indirect with post-increment (An)+ 5 address register indirect with pre-decrement -(An) 6 address register indirect with 16 bit displacement d16(An) 7 address register indirect with 8 bit index value 8(An,Rn) 8 absolute short xxxx.W 9 absolute long xxxxxxxx.L 10 direct #'data' 11 program counter indirect with 16 bit displacement d16(PC) 12 program counter indirect with 8 bit index value d8(PC,Rn)
The abbreviations above have the following meanings:
An address registers A0-A7 Dn data registers D0-D7 d16 16 bit value d8 8 bit value Rn register D0-D7,A0-A7 'data' up to 32 bit value,(either .B .W .L)
These are the addressing modes used by the MC68000 processor. The bigger brother of this processor, the 32 bit MC68020, has six more methods which we won't discuss here. Next you're going to see under what conditions the processor can operate.
In the previous section about registers you encountered the Status Register (SR). The individual bits of this register reflect the present operating condition of the processor. You differentiated between the system byte (bits 8-15) and the user byte (bits 0-7). Now, lets take a closer look at the system byte and its effects upon the operation of the processor.
User and Supervisor Modes
Isn't it rather strange that the processor classifies you either as a 'user' or a 'supervisor'? Both of these operating modes are possible, the user mode being the more common mode. In this mode it is impossible to issue some instructions, and that in your own computor! Don't worry though,you can get around that,as well. The Amiga's operating system contains a function that allows us to switch the processor from one mode to the other.
The mode is determined by bit 13 of the Status Register. Usually this bit is cleared(0), indicating that the processor is in user mode. It is possible to write directly into the status register, although this is a privileged instruction that can only be executed from the supervisor mode. Thus, this instruction could only be used to switch from the supervisor mode into the user mode, by using AND #$DFFF,SR to clear the supervisor bit. However, it is quite preferable to let the operating system perform the switch between these two modes. Now what differentiates these two modes in terms of application?
Well, we already mentioned the first difference: some instructions, such as MOVE xx,SR, are privileged and can only be executed from the supervisor mode. An attempt to do this in user mode would result in an exception and interruption of the program. Exceptions are the only way of switching to the supervisor mode, but more about later. A further difference is in the stack range used. Although A7 is still used as the stack pointer, another memory range is used for the stack itself. Thus, the SP is changed rach time you switch from one mode to the other. Because of this you differentiate between the User(USP) and the Supervisor SP (SSP).
Accessing memory can also depend on these two modes. During such accessing, the processor sends signals to the peripheral components informing them of the current processor moce. This way a MC68000 computor can protect (privilege) certain memory ranges so they can't be accessed by the user. In the supervisor mode it is possible to execute all instructions and access all areas of memory. Because of this, operating systems usually run in the supervisor mode. This is accomplished through the use of Exceptions.
Exceptions are similar to interupts on 6500 computors. This allows stopping a program, running a sub-program,and then restarting the stopped program. When an exception occurs the following steps are taken:
1) The status register is saved. 2) The S bit in SR is set(supervisor mode)and the T bit is cleared(no trace). 3) The program counter and the user SP are saved. 4) The exception vector,which points to the needed exception routine,is retrieved. 5) The routine is executed.
The vectors mentioned, which contain the starting addresses for the various routines, are located at the very beginning of the memory. Here is an overview of the vectors and their respective addresses:
Number Address Used for ----- ------ -------- 0 $000 RESET:starting SSP 1 $004 RESET:starting PC 2 $008 bus error 3 $00C address error 4 $010 illegal instruction 5 $014 division by zero 6 $018 CHK instruction 7 $01C TRAPV instruction 8 $020 privilege violation 9 $024 trace 10 $028 Axxx-instruction emulation 11 $02C Fxxx-instruction emulation $030-$038 reserved 15 $03C uninitialed interrupt $040-$05F reserved 24 $060 unjustified interrupt 25-31 $064-$083 level 1-7 interrupt 32-47 $080-$0BF TRAP instructions $0C0-$0FF reserved 64-255 $100-$3FF user interrupt vectors
The individual entries in the table above need detailed explanation. So lets go through them one by one...
- RESET: staring SSP; At reset, the long word stored at this location is used as the stack pointer for the supervisor mode(SSP). This way you can specify the stack for the RESET routine.
- RESET: starting PC; Again at reset,the value at this location is used as the program counter. In other words, the RESET routine is started at the address stored here.
- Bus Error: This exception is activated by a co-processor when, for instance, a reserved or non-existant memory range is accessed.
- Address Error: This error occurs when a word or long word access is atempted at an odd address.
- Illegal Instruction: Since all MC68000 instructions consist of one word,a total 65536 different instructions are possible. However,since the processor doesn't that many instructions, there are a number of words that are invalid instructions. Should such a word occur, the exception is prompted.
- Division by Zero: Since the processor has a division function,and the division of anything by zero is mathematically undefined and thus illegal, this exception occurs when such an operation is attempted.
- CHK Instruction: This exception only occurs with the CHK instruction. This instruction tests that a data registers contents are within a certain limit. If this is not the case, the exception is activated.
- TRAPV Instruction: If the TRAPV instruction is executed and the V bit (bit 1) in the status word is set, this exception is prompted.
- Privilege Violation: If a privileged instruction is called from the user mode, this exception is activated.
- Trace: If the trace bit (bit 15) in the status word is set, this exception is activated after each instruction that is executed. This method allows you to employ a step by step execution of machine programs.
- Axxx-Instruction Emulation:
- Fxxx-Instruction Emulation: These two vectors can be used for a quite interesting trick. If an instruction beginning with $A or $F (such as $A010 or $F200) is called the, the routine to which the corresponding vector is pointing is accessed. In these routines you can create chains of other instructions, in effect expanding the processors instruction vocabulary!
- Reserved: These vectors are not used.
- Uninitialized Interrupt: This exception is activated when a peripheral component that was not initialized sends an interrupt.
- Unassigned Interrupt: Is activated when a BUS error occurs during the interrupt verification of the activating component. However,the interrupt is usually only by some type of disturbance.
- Level 1-7 Interrupt: These seven vectors point to the interrupt routines of the corresponding priority levels. If the level indicated in the status word is higher than the level of the occuring interrupt, the interrupt is simply ignored.
- TRAP Instructions: These 16 vectors are used when a corresponding TRAP instruction occurs. Thus, TRAP instructions from TRAP #0 to TRAP #15 are possible.
- User Interrupt Vectors: These vectors are used for interrupts which are activated by several peripheral components that generate their own vector number.
At this point you don't want to delve any deeper into the secrets of exceptions, since we'd be expanding this book beyond its frame work. However, there's one last thing to say about exceptions: theexception routines are ended with the RTE (ReTurn from Exception) instruction, with which the original status is restored and the user program is continued.
Interrupts are processed similarly to exceptions.They are breaks (or interruptions)in the program which are activated through hardware(such as a peripherical component or an external trigger). The interrupt level is stored in bits 8-10 of the status register. A value between 0 and 7 indicates the interrupt level.There are eight possible interrupts,each of which as a different priority.If the level of this interrupt happens to be higher than the value in the status register,the interrupt is executed,or else ignored. When a valid interrupt occurs,the computor branches to the corresponding routine whose address is indicated in the exception vector table above. The interrupts are very important if you're trying to synchronize a program with connected hardware.In this manner,a trigger(s.a the keyboard)which is to feed the computor data,can signal the request for a legal value using an interrupt.The interrupt routine then simply takes the value directly.This method is also employed for the operation of the serial interface(RS232). We'll talk about the use of interrupts at a later time.The last thing we want to mention about interrupts at this time is that, like exceptions,interrupt routines are terminated with the RTE instruction.
2.3.4.Condition Codes. --------------------- When you write a program in any language,the need for a conditional operation arises quite often.For instance,in a BASIC program
IF D1=2 THEN D2=0
represents a conditional operation.To write the equivalent in machine language,you first need to make the comparison:
CMP stands for compare and compares two operands,in this case D1 and D2.How is this operation evaluated? For this purpose you have condition codes(CC's),which exist in the branch instructions of machine language.Because of this,you must specify when and where the program is to branch. The simplest variation of the branch instruction is an unconditional branch.The corresponding instruction is 'BRA address ',although this won't help you here.After all,you need a conditional branch. To retain the result of the operation,in this case a comparrison (CMP),several bits are reserved in the status word.Lets look at bit 2 first,which is the zero flag.This flag is set when the result of the previous operation was zero. To explain the relationship between CMP and the Z flag,you must first clarify the function of the CMP instruction.Actually this instruction performs the subtraction of the source operand from the destination operand.In the example above,the number 2 is subtracted from the content of the memory cell at D1.Before the result of this subtraction is discarded,the corresponding flags are set. If the contents of D1 in our example above happened to be 2,the result of the subtraction would be 0.Thus,the Z flag would be set, which can then be evaluated through a corresponding branch instruction.Our example would then look like this:
... CMP #2,D1 ;comparison,or subtraction BNE UNEQUAL ;branch,if not equal(Z flag not set) MOVE #0,D2 ;otherwise execute D2=0 UNEQUAL:
... ;program branches to here
BNE stands for Branch if Not Equal.This means,that if the Z flag was cleared(=0)by the previous CMP,the program branches to the address specified by BNE(here represented by UNEQUAL).The counter part to the BNE instruction is the BEQ(Branch if EQual)instruction which is executed if Z=1. Here's a list of condition codes,which allow you to form conditional branches using the Bcc(cc=condition code)format:
cc Condition Bits --------------------------------------------------- T true,corresponds to BRA F false,never branches HI higher than C'* Z' LS lower or same C + Z CC,HS carry clear,higher or same C' CS,LO carry set,lower C NE not equal Z' EQ equal Z VC overflow clear V' VS overflow set V PL plus,positive MI minus,negative GE greater or equal N*V+N'*V' LT less than N*V'+N'*V GT greater than N*V*Z'+N'*V'*Z' LE less or equal Z + N*V' + N'*V
*=logic AND, +=logic OR, '=logic NOT
Here are a few examples to demonstrate how these numerous conditions can be utilized:
CMP #2,D1 BLS SMALLER_EQUAL
This branches if the contents of D1<=2,whether D1 is 0,1 or 2.In this example,the BLE instruction would allow the program to branch even if D1 is negative.You can tell this by the fact that the V bit is used in the evaluation of this expression(see chart above). When the sign is changed during the operation,this V bit is compared with the N bit.Should both bits be cleared(N bit=0 and V bit=0)after the CMP subtraction(D1-2),the result has remained positive:the condition as not been met. The conditions EQ and NE are quite important for other uses,as well.For instance,they can be used to determine if particular bits in a data word are set,by writing the following sequence...
... AND #%00001111,D1 ;masks bits out BEQ SMALLER ;branches when none of the four ; ;lower bits is set CMP #%00001111,D1 BEQ ALL ;branches when all four bits set
The AND instruction causes all bits of D1 to be compared with the bits of the parameter(in this case #%00001111).If the same bits are set in both bytes,the corresponding bits are also set in the result.If one bit of the pair is cleared,the resulting bit is zero as well.Thus,in the result,the only bits that are set are those bits of the lowest four that were set in D1. The technique is known as masking.In the example above,only the lowest four bits were masked out,which meansthat in the resulting byte,only the lowest four appear in their original condition.All other bits are cleared with the AND operand.Of course you can use any bit combination with this method. If no bit at all is set in the result,the zeroflag is set,thus fullfilling the BEQ condition and branching the program.Otherwise, the next instruction is processed,in which D1 is compared with %00001111.When both are equal,at leastall of the four lowest bits of the original byte have been set,in which case the following BEQ instruction branches. Aside from CMP,the CC and CS conditions can also be used to determine whether a HI bit was pushed out of the data word during data rotation with the ROL and ROR instructions. Before you move on the instruction vocabulary of the MC68000,we'd like to give you another tip: The AssemPro assembler makes it quite easy to try every command in posible situations.Take the CMP command which we've been talking about,for example.To test this command with various values and to receive the results of the comparisons directly via the flags,try the following. Type the following into the Editor.
run: cmp $10,d1 bra run end
Assemble it,save the resulting code and enter the debugger.After reloading the code you can then single step through the program observing the results the program has on the flags.Try changing the values in the register D1 and see how higher and lower values affect the flag. By the way,using the start command at this time causes it to run forever.Well,at least until reset is hit,which isn't exactly desirable,either... This procedure isn't limited to just the CMP instruction.You can use it to try any other instructions you're interested in.
2.4.The 68000 Instructions. -------------------------- Its about time to explain the MC68000 instructions.You don't have room for an in-depth discussion of each instruction in this book; for that purpose we recommend PROGRAMMING THE 68000 from Sybex by Steve Williams. The following tables show the required parameters and arguments for each instruction.AssemPro have access to built in help tables covering effective addressing modes and many of the Amiga Intuition calls.The following notation is used for arguments:
Label a label or address Reg register An address register n Dn data register n Source source operand Dest destination operand <ea> address or register #n direct value
Here is a short list of the instructions for the MC68000 processor AssemPro owners can simply place the cursor at the beginning of the instruction and press the help key to see the addressing modes allowed:
Mnemonic Meaning --------------------------------------------------- Bcc Label conditional branch,depends on condition BRA Label unconditional branch(similar to JMP) BSR Label branch to subprogram.Return address is deposited on stack,RTS causes return to that address. CHK <ea>,Dx check data register for limits,activate the CHK instruction exception. DBcc Reg,Label check condition,decrement and branch JMP Label jump to address(similar to BRA) JSR Label jump to subroutine.Return address is deposited on stack,RTS causes return to that address. NOP no operation RESET reset peripherals(caution!) RTE return from exception RTR return with loading of flags RTS return from subroutine(after BSR and JSR) Scc <ea> set a byte to -1 when condition is met STOP stop processing(caution!) TRAP #n jump to an exception TRAPV check overflow flag,the TRAPV exception
Here are a few important notes... When a program jumps(JSR)or branches(BSR)to subroutine,the return address to which the program is to return is placed on the stack. At the RTS instruction,the address is pulled back off the stack, and the program jumps to that point. Lets experiment a little with this procedure.Please enter the following short program:
run: pea subprogram ;address on the stack jsr subprogram ;subprogram called move.l (sp)+,d1 ;get a long word from stack ; illegal ;for assemblers without ;debuggers
subprogram: move.l (sp),d0 ;return address in D0 rts ;and return end
The first instruction,PEA,places the address of the subprogram on the stack.Next,the JSR instruction jumps to the subprogram.The return address,or the address at which the main program is to continue after the completion of the subprogram,is also deposited on the stack at this point. In the subprogram,the long word pointed to by the stack pointer is now loaded into the data register D0.After that,the RTS instruction pulls the return address from the stack,and the program jumps to that address. Back in the main program,the long word which is on the top of the stack,is pulled from the stack and written in D1.Assemblers that do not have the debugging features of AssemPro may need the ILLEGAL instruction so they can break the program and allow you to view the register contents. Assemble the program and load the resulting code into the debugger Single step thru the program and examine the register contents. Here you can see that D0 contains the address at which the program is to continue after the RTS command.Also,D1 contains the address of the subprogram which you can verify by comparing the debugger listing. The STOP and RESET instructions are so powerful that they can only be used in supervisor mode.Even if you do switch to the supervisor mode,you should NOT use these instructions if there is any data in memory that has not been saved and you wish to retain. The TRAP instruction receives a number between 0 and $F,which determines the particular TRAP vector(addresses $0080-$00BF)and thus the corresponding exception routine.Several operating systems for the 68000 utilize this instruction to call operating system functions.You'll deal more with this instruction later. In the short sample program that compared two numbers,the CMP instruction performed an arithmetic function,namely a subtraction. This subtraction could be performed with an actual result as well using the SUB instruction.The counterpart to this is in addition, for which the ADD instruction is used.In 8 bit processors,like the 6502,these arithmetic functions are the only mathematical operations.The MC68000 can also multiply,divide,and perform these operations with a variety of data sizes. Most of the functions require two parameters.For instance the ADD instruction...
where source and destination can be registers or memory addresses. Source can also be a direct value(#n).The result of the opoeration is placed in the destination register or the destination address. This is same for all operation of this type.These instructions can be tried out with the AssemPro assembler.In this case we recommend the use of a register as the destination. Heres an overview of the arithmetic operations with whole numbers:
Mnemonic Meaning -------------------------------------------------------------- ADD source,dest binary addition ADDA source,An binary addition to a address register ADDI #n,<ea> addition with a constant ADDQ #n,<ea> fast addition of a constant which can be only from 1-8 ADDX source,dest addition with transfer in X flag CLR <ea> clear an operand CMP source,dest comparison of two operands CMPA <ea>,An comparison with an address register CMPI #n,<ea> comparison with a constant CMPM source,dest comparison of two memory operands DIVS source,dest sign-true division of a 32 bit destination by a 16 bit source operand. The result of the division is stored in the LO word of the destination,the remainder in the HI word. DIVU source,dest division without regard to sign,similar to DIVS EXT Dn sign-true expansion to twice original size(width)data unit MULS source,dest sign-true multiplication of two words into one long word MULU source,dest multiplication without regard to sign, similar to MULS NEG <ea> negation of an operand(twos complement) NEGX <ea> negation of an operand with transfer SUB source,dest binary subtraction SUBA <ea>,An binary subtraction from an address register SUBI #n,<ea> subtraction of a constant SUBQ #n,<ea> fast subtraction of a 3 bit constant SUBX source,dest subtraction with transfer in X flag TST <ea> test an operand and set N and Z flag
For the processing of whole numbers,the processor can operate with BCD numbers.These are Binary Coded Decimal numbers,which means that the processor is working with decimals.In this method,each halfbyte contains only numbers from 0 to 9,so that these numbers can be easily processed.For this method,the following instructions are available:
Mnemonic Meaning ----------------------------------------------------------------- ABCD source,dest addition of two BCD numbers NBCD source,dest negation of a BCD number(nine complement) SBCD source,dest subtraction of two BCD numbers
Again,we recommend that you try this out yourself.Although handling the BCD numbers is relatively easy,it can be rather awkward at first.Be sure that you enter only BCD numbers for source and destination,since the results are not correct otherwise. Next are the logical operations,which you might know from BASIC. With these functions,you can operate on binary numbers bit for bit.
Mnemonic Meaning ----------------------------------------------------------------- AND source,dest logic AND ANDI #n,<ea> logic AND with a constant EOR source,dest exclusive OR EORI #n,<ea> exclusive OR with a constant NOT <ea> inversion of an operand OR source,dest logic OR ORI #n,<ea> logic OR wuth a constant TAS <ea> check a byte and set bit 7
Single bits can also be manipulated by the following set of instructions:
Mnemonic Meaning ---------------------------------------------------------------- BCHG #n,<ea> change bit n(0 is changed to 1 and vice versa) BCLR #n,<ea> clear bit n BSET #n,<ea> set bit n BTST #n,<ea> test bit n,result is displayed in Z flag
These instructions are particularly important from the manipulation and evaluation of data from peripherals.After all,in this type of data,single bits are often very significant.You'll come across this more in later chapters. The processor can also shift and rotate an operand within itself ('n'indicates a register,'#'indicates a direct value which specifies the number of shiftings)...
Mnemonic Meaning ---------------------------------------------------------------- AS n,<ea> arithmetic shift to the left(*2^n) ASR n,<ea> arithmetic shift to the right(/2^n) LSL n,<ea> logic shift to the left LSR n,<ea> logic shift to the right ROL n,<ea> rotation left ROR n,<ea> rotation right ROXL n,<ea> rotation left with transfer in X flag ROXR n,<ea> rotation right with transfer in X flag
All these instructions allow you to shift a byte,a word or a long word to the left or right.Its not too surprising that this is the equivalentof multipling(dividing)the number by a power of 2.Here's a little example to demonstrate why Lets take a byte containing the value 16 as an example.In binary, it looks like this:
%00010000 =16 Now,if you shift the byte to the left by inserting a 0 at the right,you'll get the following result...
%00010000 shifted to the left equals %00100000 =32,in effect 16*2
Repeated shifting results in repeated doubling of the number.Thus if you shift the number n times,the number is multiplied by 2^n. The same goes for shifting to the right.However,this operation as a slight quirk:here's a sample byte with the value 5:
%00000101 =5,shifted once to the right equals %00000010 =2
The answer in this case is not 2.5 as you might expect.The result such a division is always a whole number,since any decimal places are discarded.If you use the DIV instruction instead of shifting, you'll retain the digits to the right of the decimal point.However shifting is somewhat faster,and shifting can also receive long words as results. After explaining the principle of shifting,you still need to know why more than two instructions are required for the procedure.Well this is because there are several different types of shifting. First,you must differentiate between shifting and rotating.In shifting,the bit that is added to the left or the right side is always a zero.In rotating,it is always a specific value that is inserted.This means that with the ROR or the ROL instructions,the bit that is taken out on one side is the one that is inserted on the other.With the ROXR and the ROXL instructions this bit takes a slight detour to the X flag.Thus,the content of the flag is inserted into the new bit,while the old bit is loaded into the flag. Shifting as well,has two variations:arithmetic and logical shifting.You've already dealt with logical shifting.In this variation,the inserted bit is always a zero,and the extracted bit is deposited in the C flag and in the X flag. Although the highest bit,which always represents the sign,is shifted in arithmetic shifting,the sign is still retained by ASR. This has the advantage that when these instructions are used for division,the operation retains the correct sign(-10/2 equals-5). However,should an overflow or underflow cause the sign to change, this change is noted in the V flag,which always indicates a change in sign.With logical shifting this flag is always cleared. Now to the instructions that allow you to move data.These are actually the most important instructions for any processor,for how else could you process data?
Mnemonic Meaning ------------------------------------------------------------------ EXG Rn,Rn exchange of two register contents(don't confuse with swap) LEA <ea>,An load an effective address in address register An LINK An,#n build stack range MOVE source,dest carry value over from source to dest MOVE SR,<ea> transfer the status register contents MOVE <ea>,SR transfer the status register contents MOVE <ea>,CCR load flags MOVE USP,<ea> transfer the user stack point MOVE <ea>,USP transfer the user stack point MOVEA <ea>,An transfer a value to the address register An MOVEM Regs,<ea> transfer several registers at once MOVEM <ea>,Regs transfer several registers at once MOVEP source,dest transfer data to peripherals MOVEQ #n,Dn quickly transfer a 8 bit constant to the data register Dn PEA <ea> deposit an address on the stack SWAP Dn swap the halves of the register(the upper 16 bits with the lower) UNLK An unlink the stack
The LEA or PEA instructions are often used to deposit addresses in an address register or on the stack.The instruction
loads the address of the label'label' into the address register A0.In practice,this corresponds to
which is equivalent to
All these instructions deposit the address of 'label' on the stack The following instruction also does this:
The LEA instruction becomes much more interesting when the label is replaced by indirect addressing.Here's an example:
The address that's produced by the addition of 1(direct value offset)+A0+D0 is located in A1.To duplicate this instruction with MOVE would be quite cumbersome.Take a look:
MOVE.L A0,A1 ADD.L D0,A1 ADDQ.L #1,A1
As you can see,the LEA instruction offers you quite some interesting possibilities. Those are all the instructions of the MC68000.Through their combination using the diverse methods of addressing,you can create a great number of different instructions,in order to make a program as efficent as possible. The following table is an overview of all MC68000 instructions along with their possible addressing types and the influence of flags.The following abbreviations are used:
x=legal s=source only d=destination only -=not effected 0=cleared *=modified accordingly 1=set u=undermined P=privileged
Mnemonic 1 2 3 4 5 6 7 8 9 10 11 12 X N Z V C P ----------------------------------------------------------------- ABCD x ADD s s x x x x x x x s s s * * * * * ADDA x x x x x x x x x x x x - - - - - ADDI x x x x x x x x * * * * * ADDQ x x x x x x x x x * * * * * ADDX x x * * * * * AND s x x x x x x x s s s - * * 0 0 ANDI x x x x x x x x - * * 0 0 ASL, ASR x x x x x x x x * * * * * Bcc - - - - - BCHG x x x x x x x x - - * - - BCLR x x x x x x x x - - * - - BRA - - - - - BSET x x x x x x x x - - * - - BSR - - - - - BTST x x x x x x x x z x x - - * - - CHK x x x x x x x x x x x - * u u u CLR x x x x x x x x - 0 1 0 0 CMP x x x x x x x x x x x x - * * * * CMPA x x x x x x x x x x x x - * * * * CMPI x x x x x x x x - * * * * CMPM x x x x x x x - * * * cpGEN - - - - - DBcc - - - - - DIVS x x x x x x x x x x x - * * * 0 DIVU x x x x x x x x x x x - * * * 0 EOR x x x x x x x x - * * 0 0 EORI x x x x x x x x - * * 0 0 EORI CCR * * * * * EORI SR * * * * * EXG - - - - - EXT - * * 0 0 EXTB - * * 0 0 ILLEGAL - - - - - JMP x x x x x x x - - - - - JSR x x x x x x x - - - - - LEA x x x x x x x - - - - - LINK x - - - - - LSL, LSR x x x x x x x * * * 0 * MOVE x s x x x x x x x s s s - * * 0 0 MOVEA x x x x x x x x x x x x - - - - - MOVE to CCR x x x x x x x x x x x * * * * * MOVE from SR x x x x x x x x - - - - - P MOVE to SR x x x x x x x x x x x * * * * * P MOVE USP x - - - - - P MOVEM x s d x x x x s s - - - - - MOVEP s d - - - - - MOVEQ d - * * 0 0 MULS x x x x x x x x x x x - * * 0 0 MULU x x x x x x x x x x x - * * 0 0 NBCD x x x x x x x x * u * u * NEG x x x x x x x x * * * * * NEGX x x x x x x x x * * * * * NOP - - - - - NOT x x x x x x x x - * * 0 0 OR s x x x x x x x s s s - * * 0 0 ORI x x x x x x x x - * * 0 0 PEA x x x x x x x - - - - - RESET - - - - - P ROL, ROR x x x x x x x - * * 0 * ROXL, ROXR x x x x x x x - * * 0 * RTE - - - - - P RTR * * * * * RTS - - - - - SBCD x x * u * u * Scc x x x x x x x x - - - - - STOP x - - - - - SUB s s x x x x x x x s s s * * * * * SUBA x x x x x x x x x x x x - - - - - SUBI x x x x x x x x * * * * * SUBQ x x x x x x x x x * * * * * SUBX x x * * * * * SWAP x - * * 0 0 TAS x x x x x x x x - * * 0 0 TRAP x - - - - - TRAPV - - - - - TST x x x x x x x x - * * 0 0 UNLK x - - - - -