Using 68000 assembly language in the laboratory

and at home

A guide to 68000 systems in the Microprocessor Applications Laboratory D226

Hong Kong Technical College (Tsing Yi)

Department of Electrical and Communications Engineering

Nick Urbanik

26 January 1996 Copyright \begin{matrix}c \\ ◯ \end{matrix} 1996 by Nick Urbanik Department of Electrical and Communications Engineering Hong Kong Technical College (Tsing Yi) nicku@iohk.com

For use by the students and staff of the Hong Kong Technical Colleges.

This booklet was typeset using L^{A}TEX 2ε.

Contents

1 Introduction 1

2 The programmer’s model of the 68000 microprocessor 3

2.1 The registers in the 68000 . . . . . . . . . . . . . . . . . . . . 3 2.1.1 Data registers . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.2 Accumulators . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.3 Address registers . . . . . . . . . . . . . . . . . . . . . 4 2.1.4 Special registers: the program counter and the stack

pointer . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.5 Flags in the status registers . . . . . . . . . . . . . . . 5

2.2 Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.1 The memory map of the 68000 system in the laboratory 8 2.2.2 The way data is stored in memory . . . . . . . . . . . 9

3 68000 Assembly language 11

3.1 The framework of an assembly language program . . . . . . . 11 3.1.1 Labels, opcodes, operands and comments . . . . . . . 11 3.1.2 Comments in programs . . . . . . . . . . . . . . . . . 12 3.1.3 Integer numbers in programs . . . . . . . . . . . . . . 13 3.1.4 Assembler directives: org, dc, ds and end . . . . . . . 13

iv  CONTENTS

3.2 Addressing modes . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2.1 Register direct addressing modes . . . . . . . . . . . . 16 3.2.2 Absolute addressing mode . . . . . . . . . . . . . . . . 16 3.2.3 Immediate addressing mode . . . . . . . . . . . . . . . 17 3.2.4 Address register indirect mode . . . . . . . . . . . . . 17 3.2.5 Address register indirect with postincrement mode . . 18 3.2.6 Address register indirect with predecrement mode . . 18 3.2.7 Address register indirect with displacement addressing

mode . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.2.8 Address register indirect with index addressing mode . 19 3.2.9 Tables in MC68000 assembly language . . . . . . . . . 20 3.2.10 Summary of 68000 addressing modes . . . . . . . . . . 21

3.3 Some assembly language instructions . . . . . . . . . . . . . . 21 3.3.1 Loops and decisions: the B_{CC} instructions . . . . . . . 21 3.3.2 Loops: the dbf instruction . . . . . . . . . . . . . . . 25 3.3.3 What if you find an instruction you do not know? . . 25

3.4 Subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.5 The stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

3.5.1 The push operation . . . . . . . . . . . . . . . . . . . 27 3.5.2 The pop operation . . . . . . . . . . . . . . . . . . . . 29

3.6 Preserving registers: transparency . . . . . . . . . . . . . . . 30 3.7 Subroutine calls, parameters and return values . . . . . . . . 31 3.8 Subroutine calls and the stack . . . . . . . . . . . . . . . . . . 32

3.8.1 The bsr and jsr instructions . . . . . . . . . . . . . . 32 3.8.2 The rts instruction . . . . . . . . . . . . . . . . . . . 33 3.8.3 The subroutine must leave the stack as it found it . . 33

3.9 An example showing a subroutine call . . . . . . . . . . . . . 33 3.10 The include file flight.i: using macros . . . . . . . . . . . . 35

ver 1.15, 26 January 1996

CONTENTS  v

3.10.1 The Init macro . . . . . . . . . . . . . . . . . . . . . 36 3.10.2 The Delay macro . . . . . . . . . . . . . . . . . . . . . 36 3.10.3 The WriteSs macro . . . . . . . . . . . . . . . . . . . 36 3.10.4 The Transl macro . . . . . . . . . . . . . . . . . . . . 37 3.10.5 The ShowNum macro . . . . . . . . . . . . . . . . . . . 37 3.10.6 The WNKeyPr macro . . . . . . . . . . . . . . . . . . . 37 3.10.7 The WKeyPr macro . . . . . . . . . . . . . . . . . . . . 38

3.11 The trap instruction and monitor routines . . . . . . . . . . 38

4 Input and output 39

4.1 I/O ports and the 68230 parallel interface/timer chip . . . . . 40 4.1.1 The interface card . . . . . . . . . . . . . . . . . . . . 41 4.1.2 The 68230 parallel interface/timer chip . . . . . . . . 41 4.1.3 Register addresses in the 68230 pi^{/}t on the flight-68k 42 4.1.4 Initialising the pi^{/}t for bit i^{/}o . . . . . . . . . . . . . 44 4.1.5 How the interface card is connected to the parallel chip 46

4.2 The keypad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.2.1 Scanning the keypad . . . . . . . . . . . . . . . . . . . 48 4.2.2 Debouncing the keys . . . . . . . . . . . . . . . . . . . 50 4.2.3 How to detect if any key has been pressed . . . . . . . 51

A Editing, assembling, downloading and running 52 A.1 The program design . . . . . . . . . . . . . . . . . . . . . . . 52 A.2 The general procedure for editing, assembling, downloading

and running an assembly language program . . . . . . . . . . 52 A.3 Editing the program . . . . . . . . . . . . . . . . . . . . . . . 53 A.4 Assembling the program . . . . . . . . . . . . . . . . . . . . . 54 A.5 Downloading your program . . . . . . . . . . . . . . . . . . . 54 A.6 Running the program . . . . . . . . . . . . . . . . . . . . . . 55

EE/27/LA/21/95

vi  CONTENTS

B Using the sim68k simulator and asm68k assembler at home 57 B.1 What is a simulator? . . . . . . . . . . . . . . . . . . . . . . . 57 B.2 The sim68k simulator . . . . . . . . . . . . . . . . . . . . . . . 57

B.2.1 The Simulator . . . . . . . . . . . . . . . . . . . . . . 58 B.2.2 The Memory Map of the ecb (and sim68k) . . . . . . 58 B.2.3 sim68k firmware commands . . . . . . . . . . . . . . . 58 B.2.4 Getting started . . . . . . . . . . . . . . . . . . . . . . 59 B.2.5 List of firmware commands . . . . . . . . . . . . . . . 59 B.2.6 Monitor software interrupts . . . . . . . . . . . . . . . 59 B.2.7 Use the correct simulator and assembler . . . . . . . . 61

B.3 Using asm68k . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 B.4 A Tutorial example using asm68k and sim68k . . . . . . . . . 62 B.5 How to obtain the simulator sim68k and assembler asm68k . . 63

References 65

Index 66

List of Figures

2.1 Registers in the 68000. PC is program counter. A7 is the stack pointer. All registers are 32 bits wide, except for the status register which is 16 bits wide. . . . . . . . . . . . . . . 4

2.2 A memory map of the flight-68k microprocessor board. M means megabytes; K means kilobytes. 1M = 2^{20}; 1K = 2^{10}. . 8

3.1 Executing a subroutine . . . . . . . . . . . . . . . . . . . . . . 26 3.2 Executing nested subroutines . . . . . . . . . . . . . . . . . . 26 3.3 Before and after pushing 12345678_{16} onto the stack. . . . . . 29 3.4 Before and after popping the stack into the D0 register. … 30 3.5 The stack before and after the call to subroutine sub1. . . . . 34

4.1 The two settings for the jumper on the interface board …. 46 4.2 The connection of the keypad to the 68230 parallel interface/

timer chip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

List of Tables

2.1 The five flags in the status register in the cpu. . . . . . . . . 6 2.2 A comparison of storing the number 6789abcd_{16} at the memory

address 8000 in a big-endian and a little-endian architecture.

All numbers are in hexadecimal. . . . . . . . . . . . . . . . . 10

3.1 Examples of each addressing mode. . . . . . . . . . . . . . . . 22 3.2 The simple tests of status register flags. . . . . . . . . . . . . 23 3.3 The number comparison tests. . . . . . . . . . . . . . . . . . . 24 3.4 All the B_{CC} instructions (all tests). . . . . . . . . . . . . . . . 24

4.1 The port addresses in the 68230 pi^{/}t and their names …. 42 4.2 The addresses of registers in the 68230 pi^{/}t on the flight-

68k. All addresses and offsets are in hexadecimal. . . . . . . 43 4.3 The values to be written into individual bits of the port A

control register (PACR) and the port B control register (PBDR)

to put these ports into submode 1X. . . . . . . . . . . . . . . 44

B.1 A list of commands recognised by sim68k. These are compatible with the Motorola Tutor firmware commands. . . . . . . 60

Chapter 1

Introduction

This lab primer introduces many new concepts that you will apply in the laboratory. Your lab sheets will refer to this primer. You need to read the parts referred to by your lab sheets before you come into the lab. I suggest that you use a highlighter as you read, highlighting the important points in different colours so that you can find them easily when you are in the lab.

There are five main topics in this little book:

1. The 68000 programmer’s model: the registers and memory organisation of the 68000

2. 68000 assembly language

3. i^{/}o using the 68230 pi^{/}t parallel chip

4. How to edit, assemble, download and run your programs in the laboratory

5. How to use the simulator and assembler at your home.

How to use this book:

1. Bring it to each lab session

2. (a) Bring a floppy disk to the laboratory (b) Collect the sim68k simulator and the asm68k assembler^{1} (c) take these home or to work

1See section B.5 on page 63 for details of how to obtain the sim68k simulator and asm68k assembler.

(d) install them on a pc

(e) use them:

(f) work through the example in section B.4 on page 62

(g) read the rest of appendix B for details of how to use the assembler

and the simulator.

3. Read the sections that your lab sheet refers to

4. Use the index and table of contents to locate any topic you need, then read that section.

5. It is not necessary for you to read this starting from page 1 and finishing with page 70!

The programmer’s model of the 68000 microprocessor

The programmer’s model of a microprocessor is the set of registers, the memory address space, the addressing modes and the instruction set. In this chapter we just look at the registers and memory space of the 68000.

The cpu is the heart of the microcomputer system. We also call the cpu a microprocessor. A microprocessor is a cpu that is made in one integrated circuit, or ic. We will look at how it works and how to program it.

2.1 The registers in the 68000

There are 18 special storage locations in the 68000 called registers. Figure 2.1 on the following page shows the arrangement of these registers and how they can be accessed by you, the programmer. Registers are very special storage locations: each has its own name. They are all located inside the microprocessor. The cpu can move data to and from the registers much faster than to or from memory. For that reason, we try to keep data in cpu registers while we can.

[figure: D0; D1; D2; D3; D4; D5; D6; D7; A0; A1; A2; A3; A4; A5; A6; A7 (SP); PC; status; Data Registers; Address Registers; 16 bits; 32 bits; 32 bits]

Figure 2.1: Registers in the 68000. PC is program counter. A7 is the stack pointer. All registers are 32 bits wide, except for the status register which is 16 bits wide.

2.1.1 Data registers

Inside the cpu there are eight temporary storage places that can each hold a 32-bit number. These are called the data registers. They are called D0, D1, D2, D3, D4, D5, D6 and D7.

2.1.2 Accumulators

An accumulator is a special cpu register which is used in nearly all arithmetic and logic instructions as one of the operands. In other microprocessor systems, there may be only one accumulator. The 68000 can use all of the data registers as accumulators.

2.1.3 Address registers

There are also eight temporary storage locations to hold addresses inside the cpu. These are called the address registers. They are called A0, A1, A2, …, A7. A7 is special purpose and is called the stack pointer. The others can be used to hold any address.

You will see in section 3.2.7 on page 19 and section 3.10.1 on page 36 that we are using A6 to hold the base address of the 68230 parallel chip, so it is better not to change A6.

2.1.4 Special registers: the program counter and the stack pointer

There are three other special registers. There is the program counter, labelled PC. The program counter always holds the address of the next instruction to be executed. As soon as the cpu fetches a word of an instruction, the program counter is automatically incremented to point to the next word of the instruction, or the next instruction itself once the fetching of the current instruction is complete.

See section 3.5 on page 27 for more about the stack pointer register, A7, and the stack.

The next section discusses the status register.

2.1.5 Flags in the status registers

The status register contains five flags. A flag is a single bit that can have the value 1 or 0. The flags are in the lowest five bits. They each have a single letter name: X = extend flag, N = negative flag, Z = zero flag, V = overflow flag and C = carry flag. These five flags are also sometimes called the condition codes. The bottom eight bits of the status register is sometimes called the condition code register, CCR.

The status register is quite different from the other registers, as the cpu treats it more as a collection of individual bits than as a single number. These individual flags are central to the way that the cpu decides to do one action rather than another action.

These five flags are set or cleared as the result of many instructions. We are particularly interested in the effect of arithmetic instructions on the flags. See the flight-68k manual or your text book to see what effect each instruction has on the flags. Table 2.1 on the following page shows the five condition code flags.

name  letter bit #  when set
carry  C  0  The current instruction had a carry out of MSB

(most significant bit), or if an unsigned subtraction, a borrow. This is equivalent to: the result of the current instruction, considered as an unsigned operation, did not fit in the destination.

overflow  V  1  The result of the current instruction, con-

sidered as a signed operation, did not fit in the destination. With addition, overflow can only happen when adding two numbers of the same sign. In this case, answer is of the opposite sign if and only if there was overflow.

zero  Z  2  The result of the last instruction was zero
negative  N  3  The result of the last operation was negative
extend  X  4  Used in arithmetic with data bigger than 32

bits to take carries from one long word to the next.

Table 2.1: The five flags in the status register in the cpu.

Overflow: Overflow indicates that a signed arithmetic operation produced the wrong answer. A wrong answer results when the destination operand is too small to hold the signed result.

Overflow from addition may only happen when two numbers of the same sign are added together. It will never happen when two numbers of opposite sign are added together. You can tell if there is overflow yourself, even without looking at the V flag in the 68000. If you add two positive numbers and the result is negative, then you have overflow. If you add two negative numbers and the result is positive, then you have overflow.

Example 2.1 Is there overflow from the 8-bit addition 79_{16} + 81_{16}?

\begin{matrix}& 79_{16} & > 0 \\ + & 81_{16} & < 0 \\ & FA_{16} & < 0 \end{matrix} \} ∴ there is no overflow.

\underline{2.2 Memory 7}

Example 2.2 Is there overflow from the 8-bit addition 83_{16} + 81_{16}?

\begin{matrix}& 83_{16} & < 0 \\ + & 81_{16} & < 0 \\ & 04_{16} & > 0 \end{matrix} \} ∴ here we have overflow.

The result is of opposite sign (it is positive). This answer is obviously wrong, because when we add two negative numbers together, we should always get a negative result.

Overflow is only of interest when you consider the numbers you are doing arithmetic with to be signed numbers. It indicates that the destination for the instruction was too small to hold the result of a signed operation.

Note that for signed arithmetic operations, carry is totally irrelevant.

Carry: Carry is more interesting when you are doing unsigned arithmetic— you can completely ignore it when you are doing signed arithmetic. For unsigned arithmetic, the carry flag indicates that the destination was too small for the result of an unsigned operation. In other words, the carry flag is set when an unsigned arithmetic operation gives the wrong answer.

With subtraction, we have ⟨dest⟩ = ⟨dest⟩ − ⟨src⟩. The carry flag will be set if the source is greater than the destination, considering both the source and destination as unsigned numbers.

Note that carry and overflow are independent; you can have one without the other, or both at the same time, or you can have no carry and no overflow.

See section 3.3.1 on page 23 for more about how the status register flags are used by the microprocessor for making decisions.

2.2 Memory

The 68000 is a 16-bit microprocessor with a 16-bit arithmetic logic unit (alu) and a 16-bit address bus. It can transfer 16 bits at a time to or from memory.

2.2.1 The memory map of the 68000 system in the laboratory

Memory is organised in byte-sized chunks;^{1} each chunk is called a memory location. Each memory location has its own special number, called its address. Addresses are usually specified in hexadecimal. In the 68000 system, addresses start from 0 and finish at 2^{24} − 1 = FFFFFF_{16}. This gives a maximum of more than 16 million possible memory locations. This range of possible addresses is called the address space. Not all of these addresses are used; in fact, only about 48 × 2^{10} bytes = 48 K bytes are actually used. The way that the systems in the laboratory use the address space is shown in figure 2.2.

monitor eprom firmware ram pi^{/}t (68230) duart (68681) 000000 007FFF 400000 403FFF 800000 80003F A00000 A0001F FFFFFF  4M 8M 10M 16M 32K 4M + 16K 8M + 64 bytes 10M + 32 bytes

Figure 2.2: A memory map of the flight-68k microprocessor board. M means megabytes; K means kilobytes. 1M = 2^{20}; 1K = 2^{10}.

Figure 2.2 shows that there are 16 K of ram (read-write memory), and 32 K of rom containing monitor firmware. It also shows the addresses in which i^{/}o ports in the 68230 pi^{/}t and in the 68681 duart can be found. The i^{/}o ports in the 68230 pi^{/}t are discussed in section 4.1 on page 40.

1However I have rarely found these chunks to be either juicy or meaty, and it seems Rover still prefers his byte-sized chunks straight from the tin.

ver 1.15, 26 January 1996

2.2.2 The way data is stored in memory

• Each memory location has a unique number called an address.

• Each memory location holds an 8-bit (1 byte) value

• Some data items are bigger than one byte, so they must occupy more than one memory location

• The lsb (least significant byte) is the right-most 2 hexadecimal digits. Informally, we call this the ‘little end’ of the data.

• The msb (most significant byte) is the left-most 2 hexadecimal digits. Informally, we call this the ‘big end’ of the data.

Examples: the word (i.e., 16-bit) value 379_{16} has msb 03_{16}, and the lsb is 79_{16}. The long word (i.e., 32-bit) value 379_{16} consists of the four bytes 00, 00, 03 and 79_{16}. The lsb is 79_{16}, the msb is 00.

• The address of a data item is always the lowest address of all the memory locations occupied by the data item. For instance, if a long word (4-byte) value is stored at address 8000_{16}, then the four bytes will occupy the four memory locations 8000_{16}, 8001_{16}, 8002_{16} and 8003_{16}.

A 68000 microprocessor always stores data in memory with the most significant byte of the data (the “big end”) at the address of the data. We say that the 68000 is big-endian.

If we have a 32-bit hexadecimal number 6789abcd_{16}, and this number is stored at the address 8000_{16}, then the arrangement of the data in memory is as shown in table 2.2 on the next page.

Let’s see another example using a Motorola microprocessor, which is bigendian. Suppose we put the word (i.e., a 2-byte value) with value C269_{16} at the address 400404_{16}. Then at what address is the byte C2_{16}? Where is 69_{16}?^{2}

Here is a third example using an Intel microprocessor, which is little-endian. Again we put the word (i.e., two bytes) with value C269_{16} at the absolute address 400404_{16}. Then at what addresses are the bytes C2_{16} and 69_{16}?^{3}

2The byte C2_{16} is at the address 400404_{16}. The byte 69_{16} is at the address 400405_{16}.

3The byte C2_{16} is at the address 400405_{16}. The byte 69_{16} is at the address 400404_{16}.

address big-endian little-endian
8000  67  cd
8001  89  ab
8002  ab  89
8003  cd  67

Table 2.2: A comparison of storing the number 6789abcd_{16} at the memory address 8000 in a big-endian and a little-endian architecture. All numbers are in hexadecimal.

68000 Assembly language

Here we look at some topics of assembly language relevant to the work you will do in the laboratory. This chapter does not attempt to replace a text book on 68000 assembly language; it can provide some help with using conditional and branch instructions, as well as some help with addressing modes. I suggest consulting the text book by Horvath [Hor92]. A good book that specifically helps with assembly language (but not much else) is the book by Leventhal [LHKC86]. There are many books in the library that you may consult. The flight-68k manual [Fli88] provides information about each instruction. There is also some on-line help in the laboratory: type asmhelp ⟨Enter⟩ at a dos prompt.

3.1 The framework of an assembly language program

3.1.1 Labels, opcodes, operands and comments

A line in a 68000 assembly language program may be either entirely a comment (see next section) or is divided into four parts: labels, opcodes, operands and comments. One or more of these may not be present. The format is:

⟨label⟩  ⟨opcode⟩  ⟨operands⟩  ⟨comments⟩
↑  ↑  ↑  ↑
No space or tab At least one space or tab  At least one space or tab  At least one space or tab

Optional Required May be required; depends on opcode

Optional

The assembler divides a line into these fields by considering spaces to separate the fields. For that reason, there must be no space between two operands, otherwise the assembler will consider the second operand to be a comment, and will give a syntax error message complaining that one operand is missing.

I suggest you use the ⟨Tab⟩ key to separate each column in your assembly language programs.

Size of operands and data

Many opcodes (and operands too) can have a suffix, which may be one of .B, .W or .L. These are shown in the following table.

Suffix Means: number of bytes number of bits
.B  byte  1  8
.W  word  2  16
.L  long word  4  32

If an instruction does not have a suffix, then it is the same as using the suffix “.W ”, e.g., the instructions move d0,d1 and move.w d0,d1 both produce exactly the same machine language.

3.1.2 Comments in programs

In the 68000 assembly language, comments can be written in two ways. A whole line comment begins at the left side of the screen with a symbol ‘*’.

Comments can also begin in the fourth field as mentioned in the previous section.

A comment is written for the human reader of a program. A comment has absolutely no effect on what the assembler sees; comments are ignored by the assembler.

You do not need to type the comments from these lab sheets into the computer. They are there to help you only.

When you write bigger programs of your own, then you should write comments, especially at the beginning of each subroutine explaining what the subroutine does. This saves a lot of time when you or someone else wants to change the program a year later.

3.1.3 Integer numbers in programs

Integer numbers can be expressed in decimal (the default), hexadecimal, octal or binary. There is a choice of suffixes and prefixes to show which representation is being used.

Type  Format  Examples
decimal  ⟨number⟩, &⟨number⟩  297, &297
hexadecimal  $⟨number⟩, ⟨number⟩H       $FF, 0FFH
octal  @⟨number⟩, ⟨number⟩O, ⟨number⟩Q  @457, 457O, 457Q
binary  %⟨number⟩, ⟨number⟩B       %11011011, 11011011B

Notice that if you use H, the first digit must be 0–9, otherwise the assembler will think the value is a label.

3.1.4 Assembler directives: ���, ��, �� and ���

An assembler directive is sometimes called a pseudo-opcode, since it appears in the opcode (or mnemonic) column of an assembly language program, but it is not a real assembly language instruction—it does not generate any machine code.

An assembler directive tells the assembler what to do. Each assembler has its own set of assembler directives; here we look at the most important: org,

dc, ds and end.

The ��� directive

The org assembler directive is used with the syntax:

org ⟨address⟩

The org assembler directive tells the assembler: “whatever comes next, put it starting at address ⟨address⟩”. For example, the org statement in this part of an assembly language program causes the move instruction to be assembled at address 400400_{16}; in other words, the machine language instruction for move d0,d1 will be located in memory at the absolute address 400400_{16}.

      org      $400400
      move.w   d0,d1

The ��, �� directives

The dc (“define constant data”) and ds (“define storage”) directives both reserve space in the computer’s memory, usually to store data for variables. The difference is that dc provides an initial value, while ds does not. The syntax is:

dc.⟨size⟩ ⟨initial value⟩

ds.⟨size⟩ ⟨number of data items⟩

The “.⟨size⟩” can be any of .B, .W or .L or can be omitted altogether, which is the same as using “.W”. dc.w. Here is an example of each:

      dc.L     1234     Reserve one long word; initialise with 1234_{10}
      ds.b     10       Reserve 10 bytes of memory.

See section 3.2.9 on page 20 for more examples of the use of dc and ds.

The ��� directive

Every assembly language program needs to be terminated with the end directive.

Like all assembler directives, this does not tell the microprocessor what to do, only the assembler. We use the trap #11 instruction followed by dc.w 0 to tell the microprocessor to terminate our assembly language programs. See section 3.11 on page 38 for more about the trap instruction.

Equates

Equates give a particular constant value to a label. The values of leds, sevSeg and switches are set using equates. If you are familiar with Pascal, an equate is like a Pascal constant defined with const. For those familiar with the C programming language, an equate is similar to a constant defined with #define.

Here is an example of some equates, showing the values given to these names:

keys     equ     $10             Port A data register
switches equ     $18             Port C data register
sevSeg   equ     $12             Port B data register
leds     equ     $12             Port B data register

So keys has the value $10, leds and sevSeg both have the value $12, and the name switches has the value $10. These values are the offsets from the base address of the 68230 parallel chip. See section 4.1.2 on page 41 for more about the 68230 parallel interface/timer chip. Also, see page 11-2 of the flight-68k manual for a list of all the registers in the pi^{/}t and their addresses on the flight-68k computer.

3.2 Addressing modes

The MC68000 has 14 addressing modes. In this section we look at these addressing modes and see some examples of their use.

An addressing mode is a way of specifying the effective address of an operand, either in a register or in memory. The effective address tells us where the operand is. There are many addressing modes because there are many different ways of storing data, and addressing modes are designed for accessing data that is stored using these different methods. Chapter 7 of the flight-68k manual, [Fli88], discusses addressing modes. Also see chapter 5, section 5.4 of Horvath the text book [Hor92], or pages 39–48 of Antonakos [Ant93]. Particularly useful as an assembly language reference for the 68000 is [Mot92].

3.2.1 Register direct addressing modes

This mode is the fastest and simplest addressing mode: the data is in a data register or in an address register. If the effective address is a data register, Motorola call this “Data register direct mode” in [Mot92]. If the data is in an address register, Motorola call this “address register direct mode.”

Here is an example of both data register direct and address register direct addressing modes:

      move.w   a0,d0    Each operand uses a register direct mode

Exercise 3.1 If this addressing mode is the fastest and simplest, why do we not use it all the time?

3.2.2 Absolute addressing mode

The first lab used the absolute addressing mode often. It is also one of the simplest addressing modes: the assembly language instruction gives the address of the operand. However, it is quite slow, as the effective address is contained in the machine language instruction, and so it takes longer for the cpu to fetch the instruction. Two examples are shown here:

        org    $400500
value   dc.w
        org    prog
        move   value,d0   value is accessed using absolute addressing
        move   $400500,d0 value is accessed using absolute addressing

Note that the two move instructions produce exactly the same machine code. The label value is equivalent to its address, 400500_{16}.

See pages 118 and 119, section 5.4.1 of the text book, [Hor92], or see page 44 of [Ant93], under “absolute short address” and “absolute long address.” See also the flight-68k manual on page 7–9 under the same headings. Note that the assembler decides itself whether to use absolute long or absolute short direct addressing; there is no need to specify a “.L” or “.W ” as shown in the manual, [Fli88].

Another disadvantage of the absolute addressing modes is that the effective address is fixed when the program is assembled or compiled; the program cannot change the effective address after the program starts running. This is a disadvantage when working with data in tables. Section 3.2.9 on page 20 discusses tables.

3.2.3 Immediate addressing mode

The first lab also used the immediate addressing mode. The value to be loaded is included with the instruction. The mode is indicated with a # in front of the number:

        move    #1,d0           Put the actual value 1 into D0.

See page 7–12 of the flight-68k manual [Fli88], or page 119 of the text book [Hor92] for some more information.

3.2.4 Address register indirect mode

The Address register indirect addressing mode overcomes two of the problems with the absolute addressing mode: the instruction executes faster and the effective address can be changed after the program starts to run. An address register holds the effective address. We say that the address register points to the data. The address register must be loaded with the effective address first, of course. This is particularly useful for processing data in a loop: the loop can use one instruction, but the effective address contained in the address register can be changed at each step, so a whole table of data can be processed with one add instruction!

This example adds a table of numbers and puts the result into the D0 register. It loads the address of the start of the table into the A0 address register. Then we have a loop, in which we add the longwords together into D0, then increment the effective address to point to the next longword in the table, and then check whether we are at the end of the table yet. If we are not at the end, we continue the loop. At the end, D0 contains the sum of all the numbers in the table. The label tableEnd is made equal to the address of whatever comes immediately after the end of the table.

table dc.L     1234,5678,91011,1213,1415,161718,22
tableEnd
*     ...
      move.L   #table,a0    load the effective address into A0
loop  add.L    (a0),d0      The source uses address register indirect
      addq     #4,a0        Increment effective address

      cmpa.L   #tableEnd,a0 Are we at the end of the table?
      bne      loop         If not, keep adding more numbers.

3.2.5 Address register indirect with postincrement mode

Notice that in the example above, the address register is incremented by 4 with an addq instruction. It would be nice if the cpu could do this incrementing for us automatically. Well, it can. This is the same example using the address register with postincrement mode.

table dc.L     1234,5678,91011,1213,1415,161718,22
tableEnd
*     ...
      move.L   #table,a0    load the effective address into A0
loop  add.L    (a0)+,d0     The source uses postincrementing mode
      cmpa.L   #tableEnd,a0 Are we at the end of the table?
      bne      loop         If not, keep adding more numbers.

This addressing mode uses the effective address from the address register first, then increments the address register by the size of the data. If the add instruction above were:

loop  add.w    (a0)+,d0     The source uses postincrementing mode

then the A0 register would be incremented by two, the size of a word (specified by the “.W ”).

The address register indirect with postincrement is very useful for the pop stack operation. Section 3.5.2 on page 29 describes the pop operation and the stack.

3.2.6 Address register indirect with predecrement mode

The predecrement mode does a subtraction from the effective address before the effective address is used. The subtraction is the size of the data, as for the postincrement mode. This addressing mode is very useful for the push operation. Examples are given of its use in section 3.5.1 on page 27.

3.2.7 Address register indirect with displacement addressing mode

The address register indirect with displacement mode may be new to you. We use this mode to access the registers of the parallel chip. First, the address register A6 is loaded with the base address of the parallel chip. The offset of the register from the base address is then added to the base address to give the address of the actual register.

Here is an example:

        move    $18(a6),d0      Load data from address given by
*                               adding 18_{16} to the contents of A6.

Note that in your lab work, you will use the constants leds, switches and sevSeg with this addressing mode. These constants have been defined in the file flight.i. See section 3.10 on page 35 for more about the file flight.i.

Here is another example, using the name leds defined in the file flight.i:

        move    d0,leds(a6)     Copy data from D0 to address given by
*                               adding leds to the contents of A6.

The value of leds is $12 — see section 3.1.4 on page 15 and section 4.1.2 on page 41. The Init macro loaded the base address of the parallel chip ($800 001) into A6. The effective address of “leds(a6)” is then $800 001 + $12 = $800 013, which is the address of the port B data register in the 68230 parallel chip. See table 4.1 on page 42 and also table 4.2 on page 43 for lists of the registers and their addresses in the pi^{/}t.

See pages 7–7 and 7–8 of the flight-68k manual [Fli88], pages 112 and 113 of the text book [Hor92] or pages 42–43 of [Ant93] for some more information about this addressing mode.

3.2.8 Address register indirect with index addressing mode

The register indirect with index addressing mode may also be new to you. In lab 3 we use it for looking up a pattern in a table to copy to the seven segment display. See page 7–8, section 7.6.2.5 of the flight-68k manual for more information.

Here is an example of its use:

        move    $18(a0,d1),d0

Here, the hexadecimal value $18 and the contents of D1 are added to the contents of the register A0 to give the memory address from which the data

is moved.

How would we use such an addressing mode? An example is looking up to the contents of the register A0 to give the memory address from which the data is moved.

How would we use such an addressing mode? An example is looking up a number in a table. Suppose we have a table of bytes, which we could declare as:

table   dc.b    1,2,4,8,$10,$20,$40,$80

Now we could load the address of this table, which is given by the label table, into the address register A0:

        movea.L #table,a0       Immediate addressing mode

Note that that suffix is an .L, not a 1! See section 3.1.1 on page 12. Now suppose we want the fourth entry in this table. We could put the value 3 into D1 and then use the instruction:

        move.b  0(a0,d1),d0     Load value 8 from table into D0

Why did we put 3 into D0 to access the fourth entry in the table? Suppose #table has the value $400500. This means that the first element of the table has the address $400500, the second has address $400501, the third $400502 and the fourth has address $400503. Now the way the addressing mode works is that the number 0 is added to what is in A0, $400500, and also to what is in D1, 3, to get the address $400503, which is the correct address. So to access the first item in the table, we would put 0 into D1, and to access the last of the eight items in the table, we would load 7 into D1.

See page 7–8 of the flight-68k manual, pages 122 and 123 of the text book [Hor92], or page 43 of [Ant93] for some more information.

3.2.9 Tables in MC68000 assembly language

As we use the word here, a table is a collection of numbers that are put one after the other in the computer’s memory. We use an index to access individual numbers in the table. Another word for such a table is array. If you have used Pascal or C, you may have seen an array before.

In MC68000 assembly language, an array is declared with the dc, declare constant data command, or with the ds, declare storage command. These assembler directives were introduced in section 3.1.4 on page 14. Here are examples of each:

        org     $400500         data starts at this address
pattn   dc.b    $ff,$7f,$3f,$1f,$f,7,3,1  An array of 8 patterns
values  ds.w    5               An array of 5 words, not initialised.

If there are initial values the entries in the table should have, then we use dc. If there are no special values the entries should be initialised to, or if the table is very big, we can use ds.

We can access the entries in the table using the register indirect with index addressing mode. See section 3.2.8 on page 19.

3.2.10 Summary of 68000 addressing modes

See table 3.1 for examples of the use of each addressing mode.

3.3 Some assembly language instructions

3.3.1 Loops and decisions: the �_{CC} instructions

Computers are good at doing repetitive jobs: the loop construction is ideal for implementing such repetitive work. We use two basic kinds of looping operations: conditional and unconditional.

Unconditional loops: the ��� instruction

An unconditional loop just keeps going forever—or at least, till we press the reset button or turn the computer off. We may implement such loops using the bra instruction (branch always). Every time we execute the bra instruction, the program counter register is loaded with the address given by the label. The program will then execute the instruction with that label. Your lab sheets have many examples of the bra instruction.

Addressing mode  Example
implied  rts
data register direct  move d0,d1
address register direct  move a0,d1

immediate  move #$1234,d1

absolute short  move $1234,d1

absolute long  move $400500,d1

address register indirect (ari)  move (a0),d1
ari with postincrement  move (a0)+,d1
ari with predecrement  move -(a0),d1
ari with index and displacement move $12(a0,d0),d1
ari with displacement  move $1234(a0),d1
PC with displacement  move $1234(pc),d1
PC with index and displacement move $12(pc,d0),d1
short relative  bra.s  alpha

Table 3.1: Examples of each addressing mode.

Conditional loops: the ��� instruction

A conditional loop can be more clever. It can stop looping when a condition is met. We often use a cmp (compare) instruction to set the flags in the status register, then we use a B_{CC} instruction such as beq to check those flags. The B_{CC} instruction will choose whether to jump (or ‘branch’) depending on the value of the status flags. These flags are the Zero, Negative, Carry, oVerflow and eXtend flags. See section 2.1.5 on page 5 for more about the status flags.

The ��� instruction

The cmp instruction works the same as the sub instruction, except that the result is thrown away.

The syntax of the cmp instruction is

             flag  N    Z    V    C

branch if flag set  BMI  BEQ  BVS  BCS
branch if flag clear BPL  BNE  BVC  BCC

Table 3.2: The simple tests of status register flags.

cmp.s ⟨ea⟩,Dn

.s is one of .B, .W or .L, ⟨ea⟩ is an effective address given by any addressing mode, specifying the source ⟨src⟩, and Dn is any data register, specifying the destination ⟨dest⟩.

The cpu does the subtraction ⟨dest⟩ − ⟨src⟩, throws the result away, but sets the flags in the status register. See section 2.1.5 on page 5 for more about the flags.

Simple tests of the flags

The B_{CC} instructions in table 3.2 are simple tests for a single flag in the status register being set (1) or clear (0).

Testing the result of comparisons

The B_{CC} instructions in table 3.3 on the next page are often applied after a subtraction. This subtraction is usually done with a CMP instruction. A subtract or compare instruction subtracts the source s from the destination d. Here d means ‘destination’ while s means ‘source’, so the subtraction is d − s.

The TST instruction is a special comparison that subtracts zero from d.

Notice that some of the instructions in table 3.3 are repeated from table 3.2, because it is only necessary to test one status bit for some comparisons.

The branch is taken if the result of the comparison is true.

Table 3.4 on the following page gives the full name for each mnemonic.

24  68000 Assembly language
comparison d < s d ≤ s d = s d ≠ s d ≥ s d > s

  signed  BLT    BLE    BEQ    BNE    BGE    BGT
  unsigned  BCS    BLS    BEQ    BNE    BCC    BHI

Table 3.3: The number comparison tests.

Mnemonic  Full name  flags
BCC  branch if carry clear  C
BCS  branch if carry set  \overline{C}
BEQ  branch if equal  Z
BNE  branch if not equal  \overline{Z}
BMI  branch if minus  N
BPL  branch if plus  \overline{Z}
BVS  branch if overflow set  V
BVC  branch if overflow clear  \overline{V}
BGT  branch if greater than (signed)  N · V · Z + \overline{N} · \overline{V} · \overline{Z}
BHI  branch if higher than (unsigned)  \overline{C} · \overline{Z}
BGE  branch if greater than or equal to (signed)  N · V + \overline{N} · \overline{V}
BLE  branch if less than or equal to (signed)  Z + N · \overline{V} + \overline{N} · V
BLS  branch if less than or same as (unsigned)  C + V
BLT  branch if less than (signed)  N · \overline{V} + \overline{N} · V

Table 3.4: All the B_{CC} instructions (all tests).

ver 1.15, 26 January 1996

3.3.2 Loops: the ��� instruction

Another useful kind of looping instruction is the DB_{CC} family of instructions. A very useful member of this family is the dbf or Decrement and Branch while False instruction. The dbf instruction takes two operands: a data register, and a label. This instruction does the following:

• The value in the data register is decremented by 1.

• If the data register is not −1, then the program will branch to the label.

• If the data register = −1, then the program will execute the instruction following the dbf instruction.

The dbf instruction can be used like a for loop in Pascal or C, with a fixed number of times through a loop.

3.3.3 What if you find an instruction you do not know?

There are some other instructions introduced in your lab work that you may not have seen before. There are several places to look. The back of the text book (Horvath, [Hor92]), and also Antonakos, [Ant93], has details of each 68000 instruction.

The flight-68k manual [Fli88] explains each instruction in chapter 8.

There is some online help with the assembler: to use this, at a dos prompt, type

asmhelp ⟨Enter⟩.

3.4 Subroutines

A subroutine is a specialised program module that may be called upon from anywhere else in the program outside of the normal sequence of execution. Most programs contain subroutines. Subroutines are often called functions or procedures, especially in higher level languages. We will talk more about functions later.

Figure 3.1 on the next page shows how a subroutine fits into the normal flow of execution of a program. At point a there is a call to the subroutine. This

[figure: subroutine; main program sequence; a; b; x; y]

Figure 3.1: Executing a subroutine

causes the next instruction in the execution of the program to be at point x. At y—the end of the subroutine—there is a return instruction, which causes execution to continue at the next instruction after point a. Later, at point b, there is another call to the same subroutine, so again the processor executes instructions between x and y. But this time, execution returns to the instruction immediately after point b.

[figure: first subroutine; main program sequence; a; b; x; y; nested subroutine; c; u; v]

Figure 3.2: Executing nested subroutines

Subroutines can also call other subroutines—see figure 3.2. Here, the first subroutine is executed as in figure 3.1, but when execution reaches instruction c, a call is made to a nested subroutine. Execution then starts at instruction u, the first instruction in the nested subroutine. At the end of the nested subroutine, a return instruction is executed at v, which causes execution to continue at the instruction immediately after instruction c. When the first subroutine finishes, the return instruction at y will cause execution to continue at the correct location—the instruction immediately after a or b, depending on where the call was made from. You will see that in this particular example, the nested subroutine is executed twice, as is the first subroutine.

Notice that when the subroutine finishes, it must return to just after where it was called from. This is different from the way a normal branch instruction works. A special data structure is required to hold the address of the instruction immediately after the calling instruction. A data structure is a software method for storing data. A data structure has storage areas and software to perform operations on the data.

3.5 The stack

We could hold the return address of the subroutine in a single fixed location, but this would not allow one subroutine to call another, since each call would overwrite the previous value.

A stack data structure is generally chosen to hold these return addresses. The stack is usually in ram. As mentioned in lectures, the stack is a last in, first out (lifo) structure. A stack has two operations:

push The push operation adds a data item to the stack.

pop The pop operation removes the most recent value pushed (that has not already been popped). Some texts call this a pull operation.

���� means that the value that is removed with a pop operation is the value that was most recently pushed onto the stack. The memory address that holds the last value that was pushed is called the top of the stack.

Each memory location, as we have discussed earlier, holds one byte and has a unique number called an address. A special register called the stack pointer—A7 in the 68000—holds the address of the top of the stack.

On some other microprocessors the stack pointer points to the location where the next data item will be pushed, but on both the 68000 and 8086 microprocessor families, the stack pointer points to the address of the last data item already pushed onto the stack.

3.5.1 The push operation

The push operation has two steps. When a data item is pushed onto the stack, 1. the stack pointer is decremented by the size of the data item (in bytes)

2. the data value is copied into the location whose address is now in the stack pointer register.

Note that you can only push and pop data values with an even number of bytes.

An example of a push operation

In the 68000, there are a few instructions to push data onto the stack. When using the move instruction, the push operation uses the address register indirect with predecrement addressing mode, described briefly in section 3.2.6 on page 18. As an example, the assembly language instruction to push a 32-bit longword value from the D0 register onto the stack is:

        move.L  d0,-(a7)        ; Remember A7 is the stack pointer.

Suppose before this instruction was executed, D0 contained 12345678_{16} and A7 contained 4000_{16}. Then the before and after pictures of this are shown in figure 3.3 on the facing page. Note that D0 is not changed by the operation, but four memory locations (in the computer’s ram) and the stack pointer, A7 in the cpu, do change.

Using the ����� instruction to push many registers onto the stack

Many registers can be pushed at once with the movem instruction. This example shows the 32-bit long word values in the registers D1, D2, D3, D4 and A0 all being pushed onto the stack:

        movem.L d1-d4/a0,-(a7)   Push all five registers onto the stack.

Note here that the dash ‘-’ between two registers means a range of registers: d1-d4 means registers D1, D2, D3 and D4. To specify registers that are not part of a range, separate them with slashes, as shown above. There is an example program in section B.4 on page 62 using a movem instruction.

Some of the programs you use in the lab sessions use the movem instruction at the beginning and at the end. This is to make them transparent. See section 3.6 on page 30 for more about transparency.

[figure: 4000; A7; 4000; 3FFF; 3FFE; 3FFD; 3FFC; ⋮; ⋮; High memory addresses; Low memory addresses; 3FFC; A7; 4000; 3FFF; 3FFE; 3FFD; 3FFC; ⋮; ⋮; High memory addresses; Low memory addresses; lsb  78; 56; 34; msb  12; STACK; STACK; 12345678; D0; 12345678; D0; Before executing move.L d0,-(a7); After executing move.L d0,-(a7)]

Figure 3.3: Before and after pushing 12345678_{16} onto the stack.

3.5.2 The pop operation

The pop operation is the reverse of the push operation. When a value is popped from the stack:

1. the data value is copied from the top of the stack into another location, either a register or a memory location.

2. the stack pointer is incremented—by the size of the data item that was removed, in bytes—to point to the new top of stack.

An example of a pop operation

Several instructions in the 68000 pop data from the stack. When using the move instruction, the pop operation uses the address register indirect with postincrement addressing mode, described in section 3.2.5 on page 18. As an example, the assembly language instruction to pop a 32-bit long word from the stack into the D0 register is:

        move.L  (a7)+,d0        ; Pop operation.

[figure: 4000; A7; 4000; 3FFF; 3FFE; 3FFD; 3FFC; ⋮; ⋮; High memory addresses; Low memory addresses; 3FFC; A7; 4000; 3FFF; 3FFE; 3FFD; 3FFC; ⋮; ⋮; High memory addresses; Low memory addresses; lsb  78; 56; 34; msb  12; STACK; STACK; 12345678; D0; ????????; D0; After executing move.L (a7)+,d0; Before executing move.L (a7)+,d0; lsb  78; 56; 34; msb  12]

Figure 3.4: Before and after popping the stack into the D0 register.

Suppose that before this instruction executes that A7 contains the value 3FFC_{16} . In this example, the “.L” means “long word”, (4 bytes) so the operation first copies 4 bytes from memory locations 3FFC_{16}, 3FFD_{16}, 3FFE_{16} and 3FFF_{16} into the 32-bit D0 register. Then the A7 register is incremented by 4 so that it finally has the value 4000_{16}. See figure 3.4. In this way, a push operation followed by a pop operation will leave the stack as it was before the push.

Using the ����� instruction to pop the stack into many registers

Many registers can be popped at once with the movem instruction. This example shows 5 32-bit long word values being popped from the stack into registers D1, D2, D3, D4 and A0:

        movem.L (a7)+,d1-d4/a0   Pop all five registers from the stack.

3.6 Preserving registers: transparency

Very often we use registers to hold variables in programs. There are many advantages to this: registers are quickly accessible—they are already inside the cpu. A program that keeps its variables all in registers will run faster than if it keeps many variables in memory.

There is one problem here—there is a limited number of registers. The 68000 has more registers than many microprocessor, but we can still run out. We may need to program other microprocessors with fewer registers. We may want to use a register for more than one variable.

Another problem is local variables in subroutines. The problem is that one subroutine may need five registers, and may call other subroutines that also need a few registers. Suppose the second subroutine changes a register that the first subroutine was using?

Another problem is with interrupts. A hardware interrupt can take place at unpredictable times. The program may be interrupted between any two instructions. When the cpu handles the interrupt, it executes a special type of subroutine called an interrupt service routine, or isr. The isr must not change any registers used by the program, or the program will stop working properly.

A solution to these problems is to make the subroutines “transparent”. We make a subroutine transparent by saving all the registers that it uses. Where do we save them? The system stack is a very convenient place. The subroutine can push all the registers it uses onto the stack as the very first action it does, then, at the very end, just before the rts instruction, it pops the the same registers in exactly the opposite order.

A convenient instruction to do this is the movem instruction, which will push or pop as many registers as you want, always taking care of doing these operations in the correct order. Please see the sections 3.5.1 and 3.5.2 on page 29 for more information about these operations.

Every isr must be transparent to the program that it interrupts, or the cpu may “hang,” or the program may give the wrong results.

Most of the macros used in the lab also begin with push and end with pop operations.

3.7 Subroutine calls, parameters and return values

Sometimes a subroutine does not need any information to do its job; if Init were a subroutine, it would need no extra information—it would just initialise the parallel chip. (The Init macro is described in section 3.10.1 on page 36.) However, some other subroutines do need to pass some information in or out to be useful. For example, the ReadKey subroutine needs to let us know what the key was that it read. It does this by putting the key index into the D0 register. We say that it returns the key index in D0, or that the key index is its return value.

Similarly, the GetKey subroutine returns a key index in register D0.

Information may be passed to a macro or subroutine through registers. An example is the Transl macro, described in section 3.10.4 on page 37. Transl takes a key index in register D0 and returns a number, also in D0, that corresponds to the value on the top of the key.

3.8 Subroutine calls and the stack

When a subroutine call is executed, the processor saves the return address on the top of the stack. When the most recent subroutine call finishes, the last stored return address is retrieved from the top of the stack. In this way, subroutines that call other subroutines (nested subroutines, as in figure 3.2 on page 26) will also return to the correct location, since the first subroutine call will be the last one to return.

3.8.1 The ��� and ��� instructions

In the 68000, the call operation is implemented by either the bsr instruction or the jsr instruction

Both do the same thing, except that bsr can only use relative addressing modes, whereas jsr can use many addressing modes. To see the difference, refer to the flight-68k manual, chapter 8, or the text book. Here we are interested in what these instructions do with the stack.

Suppose a subroutine is called sub1. Then a call to this subroutine with

either jsr sub1 or bsr sub1 will:

1. push the address of the instruction immediately following the call instruction onto the stack.

2. load the address of the first instruction in the subroutine into PC.

The first step saves the return address of the subroutine on the stack. Because the return address is a long word (4 bytes), the push operation will decrement the stack pointer by 4 before copying the return address to the stack. The second step causes execution of the instructions in the subroutine to begin.

3.8.2 The ��� instruction

Every subroutine must end with the rts (return from subroutine) instruction.

When the rts instruction executes, it simply pops the long word (32-bit) value from the top of the stack into PC (the program counter register). The effect is to cause program execution to continue from the instruction immediately after the instruction that called the subroutine.

That is,

1. the four bytes at the top of the stack are copied into the program counter

2. the stack pointer is incremented by 4

Hence, the rts instruction is simply a pop operation.

3.8.3 The subroutine must leave the stack as it found it

An important requirement is that when execution reaches the rts instruction, the stack must be exactly the same as it was immediately after the subroutine was called. If it is not, then something else will be popped into the PC, and the computer is likely to ‘hang’.

3.9 An example showing a subroutine call

A subroutine sub1 at address 400450_{16} is called. The instruction

        jsr     sub1

[figure: 4003F0; A7; 4003F0; 4003EF; 4003EE; 4003ED; 4003EC; ⋮; ⋮; High memory addresses; Low memory addresses; 4003EC; A7; 4003F0; 4003EF; 4003EE; 4003ED; 4003EC; ⋮; ⋮; High memory addresses; Low memory addresses; lsb  16; 04; 40; msb  00; STACK; STACK; 400410; PC; 400450; PC; Before call to sub1; After call to sub1]

Figure 3.5: The stack before and after the call to subroutine sub1.

is at location 400410_{16}. Figure 3.5 shows the arrangement of the stack before and after the call.

• Before the call, the stack pointer A7 is pointing to the top of the stack, which just happens to be at address 4003F0_{16}.

Here are the three steps performed by the cpu when it executes the call instruction:

• The stack pointer is decremented by four, since an address occupies 4 bytes

• The return address, 400416_{16} is copied from the program counter onto the top of the stack

• The address of the subroutine, 400450_{16} is copied into the program counter

• After the call, the value 400416_{16} has been pushed onto the stack, since the jsr sub1 instruction at address 400410_{16} is 6 bytes long. 400416_{16} = 400410_{16} + 6. 400416_{16} is the address of the instruction immediately after the jsr instruction. When the cpu fetched the jsr sub1 instruction, it automatically incremented the program counter to point to the next instruction. The cpu pushed this incremented value of the program counter. The address of the top of the stack is always held in the stack pointer, A7.

• Because the program counter holds the address of the first instruction of the subroutine, the cpu executes instructions in the subroutine.

3.10 The include file ��������: using macros

You will notice that the programs in labs 3 and 4 all have the line

        include flight.i

Also, the first instruction in each program is

        Init            Initialise the parallel chip

The first instruction opens a file, flight.i, which contains the definition of the names leds, sevSeg and switches. The file also contains the definition

of several macros called Init, Delay, WriteSs, Transl, ShowNum, WNKeyPr

and WKeyPr. You may look at the file flight.i if you like.

A macro is a sequence of instructions that has a name and that has a single idea or algorithm. A macro can be ‘called’ from anywhere in the program. A macro is rather like a subroutine. The difference from a subroutine is that each time you ‘call’ a macro, the instructions are inserted directly into that location by the assembler. If you call a macro ten times, the code for the macro will be inserted ten times in your program. On the other hand, if you call a subroutine ten times, the code for that subroutine will still only appear once in the program. A subroutine must finish with a rts instruction, while a macro does not.

A macro may take one or more parameters, such as the Delay macro, described in section 3.10.2 on the following page. The other macros here take no parameters, but some may use information provided in register D0, and some provide information in register D0.

These macros have been written so that they disturb the rest of your program as little as possible. They do this by pushing all registers that they use (except D0 in most cases, and A6) onto the stack, and then popping the registers back from the stack afterwards.

I have included these macros to make the lab work quicker for you to do.

To call a macro, simply type the name of the macro in the opcode field of your program, as shown in the programs in your lab sheet. It is necessary to type the same case as shown, as this assembler is case sensitive with the names of macros. Hence it is necessary to type Init and not

init, WriteSs and not writess, and so on.

3.10.1 The ���� macro

The macro Init initialises the 68230 parallel chip, and also loads its base address into the A6 register for use with the address register indirect with displacement addressing mode—see pages 122 to 124 of the text book, Horvath [Hor92], or p. 42–43 of Antonakos [Ant93], or p. 7-7 and 7-8 of the ���������� user manual for more about this addressing mode. Init sets the port connected to the leds as outputs, the port connected to the dipswitches as inputs, and also sets the keypad port up correctly, with port bits 0, 1 and 2 as outputs, and the other five bits as inputs. This is discussed in some detail in section 4.1 on page 40, and particularly in section 4.1.4 on page 44.

The Init macro contains six instructions. If you single-step through your program, you need to step over these first six instructions.

3.10.2 The ����� macro

The Delay macro takes one parameter, as mentioned above. The parameter is a counting number, and is provided after the name Delay, like this:

        Delay   20      Delay 20 milliseconds.

Delay preserves all the registers it uses. In other words, all registers will have the same value before and after calling Delay.

Delay will simply keep the cpu busy counting until the number of milliseconds specified by the parameter has passed.

3.10.3 The ������� macro

The WriteSs macro (write to seven segment display) takes a number in register D0 and uses a lookup table to display the value on the seven segment display, as in the last lab session.

Only the four right-most bits are used; other bits in D0 are cleared.

All other registers are not affected.

3.10.4 The ������ macro

The Transl macro (translate from a key index to key value) is used to interpret the result of ReadKey. ReadKey gives a number in the range 0 to 11, with the following values:

Key index:  0 1 2 3 4 5 6 7 8 9 10 11
Value printed on the key: 1 4 7 ∗ 2 5 8 0 3 6 9 #

Here, the “Key index” is the index returned by ReadKey. The “Value printed on the key” is what you see when you look at the telephone keypad yourself.

Transl uses a lookup table to take the key index as input (in D0) and provides the actual key value as output (also in D0). For ‘∗’ it returns $A, and for ‘#’ it returns $B, since the idea is to display the result on the seven segment display, which cannot display a ‘∗’ or a ‘#’.

3.10.5 The ������� macro

The ShowNum macro takes a value in register D0 and displays the value as a four-digit decimal number on the terminal screen. It does this by serial transfer, using monitor firmware routines, in the ROM on the flight-68k board.

All other registers are preserved besides D0.

3.10.6 The ������� macro

The WNKeyPr macro simply waits till a key is not pressed on the keypad. All registers are preserved. It is very similar to the macro WKeyPr.

3.10.7 The ������ macro

The WKeyPr macro simply waits till a key is pressed on the keypad. All registers are preserved. It uses the algorithm described in section 4.2.3 on page 51.

3.11 The ���� instruction and monitor routines

Almost every program here ends with a pair of lines that look like this:

        trap    #11
        dc.w    0

The trap instruction is used here to call a monitor routine. The monitor is a collection of programs that are in the EPROM on the flight-68k boards. These are mentioned briefly on page 55. The number on the line with the dc.w directive indicates which of these monitor programs you want to call. There are 28 of these routines, and they are listed on page 5–3 of the flight-68k manual. Three of them are used for the ShowNum macro—see section 3.10.5 on the preceding page for more about this macro.

This method of calling a monitor firmware routine is called a software interrupt.

Each program here terminates with a call to the renter routine, which terminates the program and enters the monitor routine that listens for your keystroke commands, such as when you type ⟨l⟩⟨t⟩ to begin a download, or ⟨g⟩⟨o⟩ to start a program running.

Input and output

Input is the transfer of data from outside the computer to inside the computer. Output is the transfer of data from the computer to the outside world.

• All input and output takes place through I/O ports.

• An i^{/}o port is a special “memory” location

• When the microprocessor reads from an input port, data is transferred from the outside world to the microprocessor

• When the microprocessor writes to an output port, data is transferred from the microprocessor to the outside world.

• An example of an output port is the output port connected to the leds and the seven segment display. This is port B data register on the pi^{/}t; we called this leds and sevSeg in our programs.

• An example of an input port is the input port connected to the dipswitches. This is the port A data register on the pi^{/}t. We called it switches in our programs.

• In our assembly language programs, to do output, we move the data to the port, i.e., the port is the destination, e.g.,

    move.b  #$c8,leds(a6)

• To do input in our programs, we put the input port as the source of an instruction, e.g.,

       add.b  switches(a6),d0

4.1 I/O ports and the 68230 parallel interface/timer chip

An �^{�}� port is a special memory location in an input or output hardware interface. An i^{/}o port is a connection between a computer and the outside world. For instance, the keyboard, printer and mouse on a desktop computer all are connected to the pc through i^{/}o ports.

When we read from an input port, we cause input from something outside the computer. Data is transferred from outside to inside the computer through the input port. An input is a special sort of read operation.

When we write to an output port, we cause data to be transferred from the computer to hardware that is outside the computer. An output is a special sort of write operation.

In a memory-mapped i^{/}o computer, such as the flight-68k in our labs, we use ordinary data movement instructions to do input and output, such as move or clr. An input or output port has an ordinary address, like memory. The only difference is that an address decoder selects an i^{/}o chip such as the 68230 pi^{/}t instead of a memory chip. The i^{/}o chip is connected to external hardware so that it may perform input and output. The special memory locations in the 68230 are called registers—they are all listed in table 4.2 on page 43. You may read some more about them in the flight-68k manual, in chapter 11. You may wish to look at the memory map of the flight-68k board, in figure 2.2 on page 8.

Here are some examples of input and output operations, as in our lab work:

        move.b  #$f,leds(a6)    OUTPUT: Turn on the four leds on the right.
        move.b  switches(a6),d0 INPUT from the dip switches into D0.
        clr.b   keys(a6)        OUTPUT zero to the keypad (port A)

The first operation outputs the bit pattern 0000 1111 to port B, which is connected to the leds. This output operation is performed by writing to the port B data register, which is at address $800013_{16}—see tables 4.1 on page 42 and 4.2 on page 43.

The second operation inputs from port C, which is connected to the dip switches. This input operation is performed by reading the port C data register, which is at address $800019_{16}—see tables 4.1 on the next page and 4.2 on page 43.

The third operation sends the data value 0 out of the port A data register, which is at the address 800011_{16}.

The names leds, switches and keys are named constants, defined in the file flight.i. See section 3.1.4 on page 15 for more about these names..

4.1.1 The interface card

On the 68000 microprocessor i^{/}o interface card^{1} there are four devices, two for input and two for output. The input devices are a bank of eight dip switches and a telephone keypad. The output devices are a set of eight leds (Light Emitting Diodes) and a seven segment display.

4.1.2 The 68230 parallel interface/timer chip

All these devices are connected to the 68230 parallel interface and timer (pi^{/}t) chip via a 40 wire ribbon cable.

The pi^{/}t has three ports. On this chip, a port is a set of eight pins. Each of these pins can be individually set up as an input or as an output. This is more flexible than the Intel 8255A parallel chip, in which the pins in one port cannot be assigned as inputs and outputs in any order.

The ports are called port A, port B and port C. Some of the pins in port C can be used also as inputs and outputs for the timer, and some others as control signals for transfers to and from ports A and B. We use port C as an input port since it is connected to the dip switches.

The 68230 pi^{/}t on the flight-68k has been given a base address of $800 001. The base address is determined by the design of the address decoder for the pi^{/}t. The port A data register has offset $10 from the base address of the pi^{/}t, the port B data register has offset $12 from the base address, while port C has offset $18 from the base address. This means that port A is at address $800 001 + $10 = $800 011, while the port B data register is at address $800 001 + $12 = $800 013, while the port C data register is at address $800 001 + $18 = $800 019. See section 3.1.4 on page 15 for how these constants are defined. See table 4.1 for a list of the port addresses.

1I will call the 68000 microprocessor i^{/}o interface card the interface card from here on.

register  address offset name
Port A data register 800011_{16} 10_{16}  keys
Port C data register 800019_{16} 18_{16}  switches
Port B data register 800013_{16} 12_{16}  sevSeg or leds

Table 4.1: The port addresses in the 68230 pi^{/}t and their names

4.1.3 Register addresses in the 68230 ��^{�}� on the ������� ���

A 68230 pi^{/}t has 23 separate registers. These occupy 64 memory locations. Individual bits in these registers can be set to control the behaviour of the IC. Three of them are the data registers used to communicate with the keyboard, dip-switches and leds in our lab work. Table 4.2 on the facing page gives the addresses of the registers in the 68230 on the flight-68k.

The ��^{�}� has many modes: The pi^{/}t is a general purpose parallel and timer chip, and can be set up to work in many different ways. Each of these methods of operation is called a mode. There are diagrams showing these modes on pages 1-4 and 1-5 of the data book, [Mot83]. Some ways that the parallel ports can be set up include support for handshaking and interrupts. We will study these topics later in the course.

There are many options within each mode. See pages 3-7 and 3-8 of the data book for a list of all the options.

Submode 1X We have used the pi^{/}t in its simplest mode, where each pin can work as an input or as an output. The Motorola data sheets call this submode 1X .^{2} Submode 1X is described on page 3-2 of the data book as “Bit i^{/}o (Pin-Definable Single-Buffered Output or Non-Latched Input).” On page 3-6 Motorola says:

2The name ‘submode 1X’ comes from the fact that there are two bits used to determine the submode. The ‘1X’ means that the first bit is a “don’t care” while the second is a ‘1’.

ver 1.15, 26 January 1996

register  abbreviation address offset  name
Port general control register  PGCR  800001  0
Port service request register  PSRR  800003  2
Port A data direction register  PADDR  800005  4
Port B data direction register  PBDDR  800007  6
Port C data direction register  PCDDR  800009  8
Port interrupt vector register  PIVR  80000B  A
Port A control register  PACR  80000D C
Port B control register  PBCR  80000F  E
Port A data register  PADR  800011  10  keys
Port B data register  PBDR  800013  12  sevSeg or leds
Port A alternate register  PAAR  800015  14
Port B alternate register  PBAR  800017  16
Port C data register  PCDR  800019  18 switches
Port status register  PSR  80001B 1A
Timer control register  TCR  800021  20
Timer interrupt vector register TIVR  800023  22
Counter preload register, high  CPRH  800027  26
Counter preload register, middle CPRM  800029  28
Counter preload register, low  CPRL  80002B 2A
Count register, high  CNTRH  80002F 2E
Count register, middle  CNTRM  800031  30
Count register, low  CNTRL  800033  32
Timer status register  TSR  800035  34

Table 4.2: The addresses of registers in the 68230 pi^{/}t on the flight-68k. All addresses and offsets are in hexadecimal.

This submode is intended for applications in which several independent devices must be controlled or monitored. [ …] Data read from the data register is the instantaneous value of the pin or what was written to the data register, depending on the contents of the data direction register.

In section 4.1.4 on the next page we will examine the function of the data direction registers.

4.1.4 Initialising the ��^{�}� for bit �^{�}�

Before we use the pi^{/}t, it needs to be set up in the right mode. We do this by writing particular values to some of the registers in the pi^{/}t. How do we know what values to write there? We look in the data book, [Mot83].

Examine pages 3-7 and 3-8 of the data book. I will not explain all the options: we will learn about them later. I will just give you a table (see table 4.3) showing what we want to use in our lab work.

register  bits  option
PACR  7 6 Port A submode
1 X Submode 1X
PACR  5 4 3 H2 control

0 X X Input pin—edge sensitive status input, H2S is set on an asserted edge

PACR  2 H2 SVCRQ enable
0 The H2 interrupt is disabled
PACR  1 H1 SVCRQ enable
0 The H1 interrupt is disabled
PACR  0 H1 Status control

X H1 is an edge-sensitive status input, H1S is set by an asserted edge of H1

Table 4.3: The values to be written into individual bits of the port A control register (PACR) and the port B control register (PBDR) to put these ports into submode 1X.

The arrangement for port B is the same as for port A, except that instead of PACR we are writing to the register PBCR, and instead of the handshaking lines H1 and H2, port B works with the handshaking lines H3 and H4.

What does it all mean? Table 4.3 on the preceding page above gives values required to be written to various bits of the PACR register to make the pi^{/}t behave the way we want. The ‘X’s are “don’t cares”. If the constant submode 1X contains the correct value, we can initialise port A with the instruction

  move.b   #submode_1X,PACR(a6)

Exercise 4.1 What value should we write to the port A control register to put port A into submode 1X? Give your value in binary and in hexadecimal.

We also want to put port B into submode 1X.

Exercise 4.2 What value do we need to write to port B data register to put port B into submode 1X?

What about port C? Port C is also multi-purpose, but it shares its function with the timer (the ‘T’ in pi^{/}t stands for timer), and also with interrupt functions. When port C is used just as an ordinary parallel port, it behaves like port A and port B do when they are in submode 1X. There is no need to do anything special to put port C into submode 1X.

Choosing inputs and outputs: the data direction registers

The 68230 pi^{/}t is flexible: you can choose whether individual pins are inputs or outputs. How can you select which pins are inputs and which pins are outputs?

The answer is that you write values to the data direction registers in the pi^{/}t. There are three data direction registers, one for port A (PADDR), one for port B (PBDDR) and one for port C (PCDDR). If you write a ‘zero’ to bit 0 of the port A data direction register (PADDR), then bit 0 of port A will be an input. If you put a ‘one’ there instead, it will be an output. Individual pins can be assigned as inputs or outputs in any order you like.

Exercise 4.3 If you want bits 0, 1, 2, and 3 to be inputs and bits 4, 5, 6 and 7 to be outputs of port B, what values should you write to what register?

Initialising the ��^{�}� for bit �^{�}�: a summary

The steps in initialising the pi^{/}t are as follows:

1. Put port A and port B into submode 1X by writing the values shown in table 4.3 on page 44 to the registers PACR and PBCR.

2. Select which pins you want to be inputs and which pins you want to be outputs

3. For each pin that you want to be an output, put a one into the corresponding bit-position in the data direction register. Similarly, put a zero for each pin you want to be an input.

4. That’s it! You can now read from the input pins and write to the outputs.

4.1.5 How the interface card is connected to the parallel chip

[figure: 1; 2; 3; JP1; 1; 2; 3; JP1; leds selected; Seven segment; display selected]

Figure 4.1: The two settings for the jumper on the interface board

The keypad is connected to port A while the leds and seven segment display are both connected to port B. The leds and seven-segment display are selected by jumper; please see figure 4.1. When the jumper is in the upper position across pins 1 and 2, the leds are selected. When the jumper is across pins 2 and 3, the seven segment display is selected. The dip switches are connected to port C.

When you read from the port C data register, you read an 8-bit number with one bit for each of the 8 switches in the dip switch. When you write to the port B data register, the bottom eight bits cause the corresponding led to turn on and off, or cause the corresponding segment on the seven-segment display to turn on and off. In both cases, a bit value of ‘1’ means “on”, a ‘0’ means “off”.

4.2 The keypad

On the interface board, the keypad is connected to port A of the 68230 parallel interface/timer IC.

[figure: 1; 2; 3; 4; 5; 6; 7; 8; 9; ∗; 0; #; PA_{0}; PA_{1}; PA_{2}; PA_{3}; PA_{4}; PA_{5}; PA_{6}; PI/T; Telephone keypad; V_{CC}; PA_{7}; Not connected; O/P; O/P; O/P; I/P; I/P; I/P; I/P; I/P]

Figure 4.2: The connection of the keypad to the 68230 parallel interface/ timer chip

The keypad is connected rather like figure 4.2. There is a key in the twelve positions where the vertical wires are shown crossing the horizontal ones. Each key connects the crossing wires when pressed. The columns are set up as outputs while the rows are set up as inputs. When no key is pressed, the row inputs are high.

4.2.1 Scanning the keypad

There are twelve keys on the keypad but we only have eight pins in one port. If we connected the key switches directly to twelve port pins, we could read the keys directly. But this will not work with a larger keypad or keyboard. Also port pins are too precious to waste—if possible, we would prefer to save them for another purpose.

If we have fewer port pins available, it is still possible to read the keypad by scanning it. To scan a 3 × 4 keypad, we use 3 + 4 port pins. We can use n + m port pins to scan a n × m keypad^{3}.

The method

The idea is that we output a ‘1’ to two of the column outputs to the keypad while outputting a ‘0’ to each column in turn. After the left-most column is set low, we can read each row in turn. Say the key ‘4’ on the keypad has been pressed (see figure 4.2 on the preceding page). If we read the rows (i.e., port inputs PA_{3} to PA_{6}), then bits PA_{3}, PA_{5} and PA_{6} are all ‘1’s, but PA_{4} is a zero, because the key has connected the line PA_{4} to PA_{0} on which we have output a ‘0’. The resistance between PA_{0} and ground is much lower than the value of the resistor between PA_{4} and V_{CC}, so PA_{4} will have a voltage close to zero volts. Let’s look at the steps in scanning the keypad, from a high level point of view.

• For each column:

∘ drive that column low and the others high.

∘ Read the rows.

∘ For each row

– If that row bit is ‘0’, then have found the key—exit.

– increment the key index

• If no key was found, then set key index to noKey

• The key index now has a number from 0 to 11 if a key was pressed, or a value of noKey if no key was pressed.

3The method shown here is not the only way to scan a keypad—it is possible to scan \frac{1}{2} n(n − 1) keys with n port pins. For instance, it is possible to read 28 keys with 8 port pins—if you are interested, ask the author.

ver 1.15, 26 January 1996

Let’s now look at the steps in greater detail.

step 1 Start by initialising the key index number that indicates the current key, the column counter and the column pattern that we will output to the keypad columns

step 2 for each column, starting from PA_{0}, drive that column low while driving the other columns high

step 3 Read the pattern of bits from the rows, i.e., PA_{3} to PA_{6}. To do this, we read the 8 bits from the keypad port into a register, then rotate the 8 bits three times, so that the bit from the top row is in bit position 0, i.e., in the LSB (least significant bit) position.

step 4 for each row:

step 4a Rotate the LSB into the carry flag in the cpu status register. Look up how the ror and rol instructions work in the flight-68k manual, your text book or any 68000 reference book.

step 4b check whether the carry flag is low. If it is, then the current key has been pressed.

step 4c if the current key has not been pressed, then increment the key index number and try the next row—go to step 4a.

step 5 Roll the bit pattern for the columns one position left so that we are ready to drive the next column low and the other columns high.

step 6 If we have read all the columns, then no key was pressed, so put a special value into the key index register to show that no key has been pressed.

step 7 We now have the key number. Use this as an index to look up the value of the key in a lookup table.

This list of steps is called a task list. We can also express the algorithm in pseudocode. Pseudocode is a mixture of English (or you can use Cantonese if you like!) and some programming language. Here I have combined English with Pascal, since you may have studied Pascal:

Algorithm ReadKey:
key = 0, col = 2, columnPattern = $FE

while col <> -1 do
   row = 3
   keypad = columnPattern
   rowPattern = keypad
   roll port A bit 3 into rightmost bit position of rowPattern
   while row <> -1 do
      roll LSB of rowPattern into CPU carry flag
      if carry flag is clear goto found
      key := key + 1
      row := row - 1
   roll columnPattern left one position
   col := col - 1
key := noKey
found:

I hope that these two ways of showing the algorithm will help you to understand the subroutine ReadKey.

4.2.2 Debouncing the keys

However, there is more to it than this. We need to deal with a problem called key bounce. When any switch opens or closes, it does not do so at only one moment. A closing switch will bounce open again for a very short time, close again, bounce apart again, and so on. This lasts about 10 milliseconds, depending on the type of switch. We need to be able to tell when a key has been pressed once and is bouncing or if it has been pressed more than once.

Small amounts of noise can also be interpreted as keystrokes when there are none.

There are many solutions to this problem, some using hardware such as a flip-flop or Schmidt trigger, others using software delay loops. We will take the software approach.

If a key has been pressed, we can wait a while and see if it is still pressed some ten or twenty milliseconds later. If it is still seen as pressed, we shall say that this is a genuine keystroke.

We can use the Delay macro to create a delay of 20 milliseconds:

        Delay   20      Delay 20 milliseconds.

Please read section 3.10.2 on page 36 for more about the Delay macro.

4.2.3 How to detect if any key has been pressed

To see how to scan a keypad we look here at the first part of the problem: detecting a keystroke. How can we do this?

One simple way is to output a low to all the columns (i.e., to bits 0, 1 and 2 of the keypad port, port A).

How do we write the pattern which has bits 0, 1 and 2 low? This is 0. To achieve this step, we can write:

        clr.b  keys(a6)     output 0 to keypad columns

Remember, to write to a port register, we put it as the destination operand, i.e., on the right side of the move instruction.

We can then read the keypad port. If any of the row bits (i.e., bits 3 to 6) are low, then a key has probably been pressed.

How can we do this? We can first input from the keypad into a register:

        move.b  keys(a6),d1     Register D1 contains the column bits

Now how can we find out if any of the rows are low? Well, first we need to clear our copy of the bits that are not in the rows, i.e., that are not bit 3, 4, 5 or 6. We use the and instruction to clear these bits.

We can write:

        andi.b  #$78,d1         Clear all the bits that are not row bits

Finally, we can compare this value read from the rows with $78. We can tell if a key has been pressed by seeing if these values are the same or different.

Exercise 4.4 What we can say about the number of keys pressed if the value read from the rows is equal to $78?

Appendix A

Editing, assembling, downloading and running

A.1 The program design

The starting point is your program design. I recommend writing the design in pseudocode. There is an example of the use of pseudocode on page 49. Flow charts are also sometimes useful in working out complicated loops and decisions. Once you have the design, the rest is easy. This appendix is about how to assemble, download and run a program after you have designed it.

A.2 The general procedure for editing, assembling, downloading and running an assembly language program

Remember: for each program, you will always use the same five steps:

• edit ; • assemble ; • download  • run ; • observe

You have a number of choices for what programs to use for running an assembly language program on the flight-68k microprocessor board. The method here uses ms-dos. An alternative is to use Microsoft Windows, and use Terminal for the data transfer instead of the comm.exe program.

1. Before the pc is turned on, be sure that you have an interface card plugged into a flight-68k microprocessor board, and that power is supplied to the microprocessor board and that the serial connection is made to the pc.

2. If you switch on the pc, from the ms-dos Startup Menu, select option

     3, “Standalone Workstation”

3. If you want to output to the seven segment display, set the jumper on the interface board to the seven-segment display position, as in figure 4.1 on page 46. If you are using the LEDs, set the jumper to the other position.

4. Change to the c:\68x directory by typing:

cd \68x ⟨Enter⟩

A.3 Editing the program

If your program uses any macros, please read about them in section 3.10 on page 35. Note that you must type the macro names in the same case as shown in that section, or you will get an “illegal opcode” assembler error message.

Here I assume that the program you are working with is called ⟨program⟩.asm. Just put the name of your own program instead of ⟨program⟩.

I describe here how to use the same editor—Q-edit—as we did in previous labs, but you can also use Emacs or edit.

1. Edit a new program using Q-edit by typing

q ⟨program⟩.asm ⟨Enter⟩

2. Type in your program.

3. Save this program and exit Q-edit by typing

⟨Alt-x⟩

A.4 Assembling the program

If the name of your assembly language program is ⟨program⟩.asm then you can assemble it by:

1. At the C:\68X> prompt, type xasm ⟨program⟩ ⟨Enter⟩

2. In response to the

Object file:  [⟨program⟩.BIN]

prompt, press the ⟨Enter⟩ key.

3. In response to the prompt for a list file, you can either

• press ⟨Enter⟩, in which case the listing file will be displayed on the screen, or

• type

⟨program⟩ ⟨Enter⟩

in which case a file ⟨program⟩.lst will be created containing the assembler listing output. You can examine this file with an editor if you like.

4. If the assembler reports 0 errors and 0 warnings then assembly was successful. Otherwise, write down the error messages. These show both the line number and the position in the line where the error was found. Edit the program again. The first error or warning is usually the best place to start. Examine that line and correct the error. Then assemble your program again until there are no errors or warnings.

A.5 Downloading your program

Downloading a program is sending the assembler output from the pc to the flight-68k board by a serial transfer. I assume here that the output from the assembler was the file ⟨program⟩.bin. If you are working with a different filename, use that name instead of ⟨program⟩.

1. Start the serial communications program at 9600 bits per second. We run the program at a baud rate of 9600 bits per second by typing:

comm /b9600

A.6 Running the program  55

This program is also called a terminal program, since the pc behaves as a terminal for the flight-68k board.

2. Press the ⟨Enter⟩ key three times until an F> prompt appears.

If no F> prompt appears, press the RESET button on the flight-68k board (not the one on the pc!) then press ⟨Enter⟩ three times. Call the lecturer or technician if you still get no F> prompt.

3. Type

⟨l⟩⟨t⟩

There are some programs in the flight-68k eprom . These programs are called the monitor programs. They are also called firmware because they remain after the power is turned off. The command ⟨l⟩⟨t⟩ is a command to the monitor firmware to be ready to receive a serial stream of characters from the pc. See also section 3.11 on page 38 for more information about the firmware.

4. When prompted with:

ENTER OFFSET (<CR> IF NONE) :

press the ⟨Enter⟩ key.

Many people forget this little step! If you do, the transfer will not work, but all sorts of strange characters may appear on your screen, and perhaps the terminal will beep.

5. Now press the ⟨F2 ⟩ key, and when prompted, enter the name of the program:

⟨program⟩ ⟨Enter⟩

This is a command to the terminal program to start sending characters from the file ⟨program⟩.bin down the serial cable.

6. The firmware will give a message LOAD COMPLETED indicating that the transfer was successful.

A.6 Running the program

1. Type

⟨g⟩⟨o⟩

2. In response to the request for a starting address, type the number that is the address of the first instruction in your program. This is given by the org statement in your program that is put immediately before the first instruction. In labs 3 and 4, we will stay with 400400_{16}.

3. Observe what happens when the program runs.

4. To exit from the comm terminal program press the ⟨Esc⟩ key.

Appendix B

Using the sim68k simulator and asm68k assembler at home

B.1 What is a simulator?

A simulator is software that can run on a computer like a pc and which can run programs designed for another computer. The sim68k simulator can run on any pc and will behave as if you have a 68000 microprocessor board attached to the pc! There is also an assembler that you can use, asm68k. This software is freely distributable—you can take a copy home or to work, and edit, assemble and run 68000 assembly language programs without requiring a 68000 microprocessor system! This means that you can—and should—practice writing and running assembly language programs outside of your laboratory class.

B.2 The sim68k simulator

Here are the commands that the sim68k simulator can use. For a full description of the simulator and assembler, see the book [LW93], Computer Organisation and the MC68000, by Panos E. Livadas and Christopher Ward, Prentice-Hall, 1993. The authors of the book wrote the simulator and assembler and are making them freely available.

B.2.1 The Simulator

The simulator is able to simulate the Motorola “Educational Computer Board”, or ecb. The ecb has a rather different address map from the flight-68k. Also the ecb has similar firmware commands, but they have different letters. These are listed below. The name of the simulator is sim68k. The firmware in the ecb is called Tutor.

B.2.2 The Memory Map of the ��� (and sim68k)

Address range Device Use
000000–000007  rom Reset vector
000008–0003FF  ram Vector table
000400–0008FF  ram Tutor scratch pad
000900–007FFF ram USER MEMORY ⟸ ⟸ ⟸
008000–00BFFF rom Tutor firmware
00C000–00FFFF  Unused

A 68230 pi^{/}t and two 6850 uarts are mapped above 00FFFF_{16}, but the address space there is otherwise empty.

Note that your program must be loaded into addresses lower than $7FFF, but higher than $8FF. The stack is usually at the top of ram.

B.2.3 sim68k firmware commands

The sim68k simulator is just software that runs on the pc and of course contains no real firmware, but these are Tutor firmware commands on the ecb.

ALL COMMANDS MUST BE ENTERED IN UPPER CASE LIKE THIS!

B.2.4 Getting started

1. Assemble your program with the assembler, asm68k.

asm68k ⟨filename⟩.⟨ext⟩

2. Load your program into the simulator:  LO ⟨filename⟩.s
3. Set the status register to $0700:  .SR 0700
4. Set the stack pointer to $7800:  .US 7800
5. Single step (“Trace”) your program:  TR [⟨address⟩]

OR

Execute your program:  GO [⟨address⟩]

B.2.5 List of firmware commands

A summary of the commands recognised by sim68k is shown in table B.1 on the next page. In that table, [...] means that ... is optional.

The firmware remembers the last command you entered. If you just type a carriage return at the prompt, the last command will be processed again.

Disassembly works on one instruction the first time. If you press ⟨Enter⟩ straight after, you get 16 instructions disassembled.

B.2.6 Monitor software interrupts

Like the flight-68k, the ecb supports software interrupts. There is a difference in the way they are used: load a function number as a byte into the D7 register then execute the instruction trap #14.

move.b #⟨function number⟩,d7

        trap    #14

Here is a list of these i^{/}o functions. The function numbers are given in decimal.

60  Using the sim68k simulator and asm68k assembler at home

Command  Action

DF “Display Formatted”—display the registers, like the flight-68k command ⟨r⟩⟨d⟩.

MD ⟨address⟩  Memory dump
MD ⟨address⟩ ;DI  Disassemble
EX  Exit the simulator
.⟨register⟩  Display ⟨register⟩, e.g., .D0 will display D0 .A6 will display A6 .A7 will display A7 .SR will display SR .PC will display PC.

.⟨register⟩ ⟨data⟩ Modify ⟨register⟩, e.g.,

.D0 ⟨data⟩ will modify D0
.A6 ⟨data⟩ will modify A6
.A7 ⟨data⟩ will modify A7
.SR ⟨data⟩ will modify SR
.PC ⟨data⟩ will modify PC.
GO [⟨address⟩]  Run program non-stop
TR [⟨address⟩]  Single step

BR [⟨address⟩] Set/display breakpoints—up to a maximum of eight.

NOBR [⟨address⟩]  Clear breakpoints

LO ⟨filename⟩ Load ⟨filename⟩ into sim68k. File contains Motorola S records.

ST ⟨filename⟩ ⟨address⟩ ⟨count⟩ load ⟨count⟩ bytes starting at ⟨address⟩ and store into ⟨filename⟩.

BF ⟨address1 ⟩ ⟨address2 ⟩ ⟨data⟩ Copy the hexadecimal word in ⟨data⟩ to all addresses from ⟨address1 ⟩ to ⟨address2 ⟩.

MM ⟨address⟩ Modify one byte of memory. Type . to terminate.

MM ⟨address⟩;W  Modify one word of memory at a time.

Table B.1: A list of commands recognised by sim68k. These are compatible with the Motorola Tutor firmware commands.

Function

number name  purpose

241 PORTIN1 Read string from keyboard into A6, terminate with cr lf.

247  INCHE  As for PORTIN1 but terminated with CR with no LF
243  OUTPUT Output string A6 points to to the screen
227  OUT1CR As OUTPUT but also send cr lf.
248  OUTCH  Byte in D0 is output to screen
228  EXIT  Give termination message if Supervisor bit clear.

For more information see Appendix C of Livadas.

B.2.7 Use the correct simulator and assembler

Note that there are three simulators in the f:\sim68k directory. Use sim68k.exe. There is one, xsim68k, that will run only under unix. The other, sim68.exe, is a simulator for a simplified 68000.

Similarly there are two assemblers: use asm68k. The other, asm68, is an assembler for the simplified simulator.

B.3 Using asm68k

Syntax:

asm68k ⟨filename⟩ [-l[x]] [-r]

-l causes the assembler to produce a listing file.
-lx produces a listing file containing a cross-reference table

-r produces a relocatable object file that can be linked; output file will have the extension “.o”.

B.4 A Tutorial example using asm68k and sim68k

Follow the procedure:

1. At the dos prompt, type

edit ex1.asm ⟨Enter⟩

2. Type the following program: (note that it is the same as ex1.asm in Workshop 2 for part-time classes but the addresses data and prog were changed to match sim68k).

data    equ     $1000         (note the addresses are different from the
prog    equ     $1100         flight-68k board using in the laboratory)
        org     data
value1  dc.L    $12345678     first value
value2  dc.L    $87654321     second value
result  ds.L    1             reserve long word storage
        org     prog
        movem.L value1,d0/d1  load values to be compared
        cmp.L   d0,d1         compare 32 bit values
        bhi     store         if value2 > value1 then goto store
        move.L  d0,d1         else d1 = value1
store   move.L  d1,result     store larger value
        nop                   (note: no trap #11 instruction)
        end

3. Select File→Exit from the editor menu and press ⟨Enter⟩.

4. At the dos prompt, type

asm68k ex1.asm ⟨Enter⟩

5. type

sim68k ⟨Enter⟩

6. Press ⟨CapsLock⟩ to switch to capital letters since the simulator only accepts CAPITAL LETTERS!

7. Type

LO ex1.s ⟨Enter⟩
MD 1100 13;DI ⟨Enter⟩

The screen will display the instructions of your program.

B.5 How to obtain the simulator sim68k and assembler asm68k 63

8. Type

MD 1000 10 ⟨Enter⟩

Record the contents of the memory.

9. Type BR 1112 ⟨Enter⟩ GO 1100 ⟨Enter⟩ The program will execute from address 1100_{16} and stop at 1112_{16}.

10. Type

MD 1000 10 ⟨Enter⟩

Record the contents of the memory again and compare the result with (8).

11. Type EX ⟨Enter⟩ The program will exit and return to dos.

B.5 How to obtain the simulator sim68k and assembler asm68k

To obtain the simulator, go to the Microprocessor Applications Laboratory, room D226, and:

• Turn on one of the computers

• Select the “Novell network” option from the boot menu

• type:

f: ⟨Enter⟩

• Type:

cd \sim68k ⟨Enter⟩

• Put a formatted floppy disk into the 3^{1}/_{4}” drive

• Run the batch file copyit.bat by typing:

copyit ⟨Enter⟩

EE/27/LA/21/95

64  Using the sim68k simulator and asm68k assembler at home

• When disk activity stops, remove your floppy disk

• Take the floppy disk home

• Make a directory on your computer by typing

  md \sim68k ⟨Enter⟩

• Change to that directory with

  cd \sim68k ⟨Enter⟩

• Copy the programs from your floppy disk to your computer’s hard disk by typing:

  xcopy a:\sim68k\*.* . ⟨Enter⟩

References

[Ant93] James L. Antonakos. The 68000 microprocessor: hardware and software principles and applications. Macmillan, New York, 1993. ISBN 0-02-303603-6. QA76.8.M6895A57 1993.

[Fli88] Flight Electronics. The ���������� users manual. Flight Electronics, Southampton, second edition, 1988.

[Hor92] Ralph Horvath. Introduction to Microprocessors using the MC6809 or the MC68000. McGraw-Hill, New York, 1992. ISBN 0-07-112645-7.

[LHKC86] Lance Leventhal, Doug Hawkins, Gerry Kane, and William D. Cramer. 68000 Assembly Language Programming. Osborne/McGraw-Hill, Berkley, second edition, 1986. ISBN 0-07-099131-6.

[LW93] Panos E. Livadas and Christopher Ward. Computer Organization and the MC68000. Prentice-Hall, Englewood Cliffs, 1993. ISBN 0-13-175712-1. This book describes the asm68k assembler and sim68k simulator.

[Mot83] Motorola. The MC68230 Parallel Interface/Timer (��^{�}�). Motorola Inc, Austin, Texas, December 1983.

[Mot92] Motorola. The M68000 Family Programmer’s Reference Manual. Motorola Inc, Austin, Texas, 1992.

Index

absolute addressing mode, 16–17

disadvantage, 16 accumulator, 4 addition

overflow, 6 address, 8, 9

of data item, 9

range in 68000 system, 8 address decoder, 41 address map, 58 address register, 4 address register indirect address-

ing mode, 17–18 address register indirect address-

ing mode with postincrement, 18

address register indirect addressing mode with predecrement, 18

address register indirect with displacement addressing mode, 19

address register indirect with index addressing mode, 19– 20

address space, 8 addresses

used in flight-68k, 8 addressing mode, 15

absolute, 16–17 disadvantage, 16 address register indirect (ari),

17–18 address register indirect with displacement, 19 address register indirect with

index, 19–20 address register indirect with postincrement, 18 address register indirect with predecrement, 18

immediate, 17 register direct, 16

addressing modes, 21

arrays, 20–21 asm68k, 61

                                   asmhelp, 11, 25

assembler, 12 asm68k

obtaining, 63–64 tutorial, 62–63 directive, 13–15 free, 61 on line help, 11, 25

assembling, 54 assembly language

comments, 12–13 assembly language instruction

format, 11–12

.B, 12

base address, 41

                                   B_{CC}, 23

\underline{INDEX 67}

big end, 9 big-endian, 9 binary numbers, 13 borrow, 6 bsr instruction, 32

carry flag, 7 chunks

as preferred by Rover, 8n byte-sized, 8 juicy, 8n meaty, 8n

comments, 11–13 conditional branch, 23 conditional loop, 22

data direction register, 45–46 data registers, 4 data structure, 27

stack, 27 dc, 14 debouncing a keypad, 50–51 Delay macro, 36 dip-switches, 36, 41 downloading, 54–55

doesn’t work, 55 ds, 14 duart, 8

ecb, 58 editing, 53 Educational Computer Board, 58 effective address, 15 extend flag, 6

F> prompt

how to get, 55 firmware

monitor, 8

monitor routine, 38 firmware commands, 58

Tutor, 58 flag carry, 7 extend, 6 negative, 6 overflow, 5–7 zero, 6 flow charts, 52

hexadecimal numbers, 13

immediate addressing mode, 17 Init macro, 36 input, 39 instruction

                                      B_{CC}, 23
                                      bsr, 32
                                      dbf, 25
                                      jsr, 32

movem, 28, 30

                                      rts, 33
                                      trap, 38

conditional branch, 23 interface card, 41, 53 interrupt, 31 interrupts

software, 38, 59–61 i^{/}o ports, 39–41

jsr instruction, 32 jumper, 46, 53

selecting leds or seven-segment, 46

keypad, 36, 46–47 debouncing, 50–51 detecting key press, 51 scanning, 48–50

kilobyte, 8

.L, 12 label, 11 least significant byte, 9 leds, 41, 53 \underline{68 INDEX}

little end, 9 little-endian, 10 lookup tables, 20–21 lsb, 9

macro

    Delay, 36
    Init, 36
    ShowNum, 37
    Transl, 32, 37
    WKeyPr, 38
    WNKeyPr, 37
    WriteSs, 36–37

and transparency, 30–31 macros, 35–36 megabyte, 8 memory location, 8, 9 memory map, 8, 58 microprocessor

definition, 3 Microsoft Windows, 53 monitor firmware, 8

prompt, 55 monitor routine, 38 most significant byte, 9 movem, 28, 30 msb, 9

negative flag, 6 numbers

binary, 13

hexadecimal, 13

octal, 13

octal numbers, 13 opcode, 11

suffix, 12 operand

size, 12 operands, 11 output, 39 overflow flag, 5–7 parallel chip, see parallel interface/

timer initialising, 36, 44–46

parallel interface/timer base address, 41 initialising for bit i^{/}o, 42–45 registers, 42 submode 1x, 42–45 pi^{/}t, see parallel interface/timer pop, 29–30 port

i^{/}o, 39–41 A, 41, 46 B, 46 C, 41, 45, 46

ports, 41 program

running, 55–56 program counter, 4, 5, 21, 33–35 program design, 52 pseudo-operand, 13–15 pseudocode, 49, 52 push, 27–28

register direct addressing mode, 16 registers

accumulator, 4 address, 4 data, 4 data direction in pi^{/}t, 45 limited number in cpu, 31 registers pi^{/}t, 42

program counter, 4, 5, 21, 33–

35 stack pointer, 4, 5, 27–29, 33–

35, 59 status, 5–7

                                   rts, 33

running a program, 55–56 selecting leds or seven-segment,

46 serial connection, 53 seven segment display, 53 seven-segment display, 41, 46 ShowNum macro, 37 simulator

sim68k freely distributable, 57 obtaining, 63–64 tutorial, 62–63 use right one, 61 what it is, 57

16-bit microprocessor, 7 68230

initialising, 36, 44–46 software interrupts, 38, 59–61 stack, 18, 27

and subroutines, 33 data structure, 27 definition, 27 operations, 27 pop, 18, 29–30

into many registers, 30 push, 27–28

many registers, 28 top of, 27

stack pointer, 4, 5, 27–29, 33–35,

59 status register, 5–7 submode 1x, 42–45 subroutine, 25–27

and the stack, 33

and transparency, 30–31 call, 32–35 parameters register, 32

tables, 20–21 Terminal program, 53 top of the stack, 27 Transl macro, 32, 37 transparency, 30–31 trap instruction, 38 Tutor, 58 Tutor firmware commands, 60

unconditional loop, 21 unsigned subtraction, 6

variables defining, 14

.W, 12

WKeyPr macro, 38 WNKeyPr macro, 37 WriteSs macro, 36–37

zero flag, 6

Reference: EE/27/LA/21/95

Compiled by  Issue  Date  Remark
Nick Urbanik  1  26 January 1996  This is a major reworking of workshop 0 for 2540 from

1994/95. I have turned it into a book format and have improved the organisation, and added more useful information, such as the appendix on the simulator and an index and list of references. This is now for 2540, 2541, 2543, but is also useful to full-time students.