Learning and using

in embedded systems _{Nick Urbanik}

example programs by Vincent Hui

27 February 1996

Hong Kong Technical College (Tsing Yi)

Department of Electrical and Communications Engineering 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

1.1 How should you learn the C language? . . . . . . . . . . . . . 1 1.1.1 Practice! . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.2 The Quincy interpreter . . . . . . . . . . . . . . . . . . 2 1.1.3 Use your laboratory! . . . . . . . . . . . . . . . . . . . 2 1.1.4 Books about C programming . . . . . . . . . . . . . . 2

1.2 The convention used here for C programs . . . . . . . . . . . 3 1.3 Embedded systems . . . . . . . . . . . . . . . . . . . . . . . . 4 1.4 How should I use this book? . . . . . . . . . . . . . . . . . . . 4

2 The C programming language 6

2.1 Constant values in C . . . . . . . . . . . . . . . . . . . . . . . 6 2.1.1 Constant integer values . . . . . . . . . . . . . . . . . 6 2.1.2 Constant character values . . . . . . . . . . . . . . . . 7 2.1.3 Constant strings . . . . . . . . . . . . . . . . . . . . . 7 2.1.4 Named constants in C . . . . . . . . . . . . . . . . . . 8

2.2 Integral types . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2.1 Definitions and declarations of integer-type variables . 9 2.3 The meaning of #include . . . . . . . . . . . . . . . . . . . . 10

iv  CONTENTS

2.4 Comments in the C language . . . . . . . . . . . . . . . . . . 11 2.5 C is case sensitive . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.6 The use of printf () . . . . . . . . . . . . . . . . . . . . . . . . 11

2.6.1 Displaying variables . . . . . . . . . . . . . . . . . . . 13 2.6.2 Displaying a string . . . . . . . . . . . . . . . . . . . . 16 2.6.3 A warning about the format characters and printf () . 16 2.6.4 Example programs showing the use of printf () …. 16

2.7 Expressions and operators . . . . . . . . . . . . . . . . . . . . 17 2.7.1 Expressions and operators—C compared with Pascal . 18 2.7.2 The assignment operator = . . . . . . . . . . . . . . . 20 2.7.3 The compound assignment operators . . . . . . . . . . 21 2.7.4 The relational operators . . . . . . . . . . . . . . . . . 21 2.7.5 The increment and decrement operators, ++ and −− 23 2.7.6 The logical operators . . . . . . . . . . . . . . . . . . . 23 2.7.7 The bitwise operators and bit patterns . . . . . . . . . 24

2.8 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.8.1 The null statement . . . . . . . . . . . . . . . . . . . . 31 2.8.2 Compound statements . . . . . . . . . . . . . . . . . . 31 2.8.3 if statement . . . . . . . . . . . . . . . . . . . . . . . . 32 2.8.4 The while statement . . . . . . . . . . . . . . . . . . . 34 2.8.5 The for statement . . . . . . . . . . . . . . . . . . . . 34 2.8.6 The do statement . . . . . . . . . . . . . . . . . . . . 37 2.8.7 Nested loops . . . . . . . . . . . . . . . . . . . . . . . 38 2.8.8 The break statement . . . . . . . . . . . . . . . . . . . 38 2.8.9 The continue statement . . . . . . . . . . . . . . . . . 39

2.9 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

2.9.1 Array indexing errors . . . . . . . . . . . . . . . . . . 43 2.10 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

ver 1.7, 27 February 1996

CONTENTS  v

2.10.1 Function return values . . . . . . . . . . . . . . . . . . 50 2.10.2 The function parameter list . . . . . . . . . . . . . . . 52

2.11 Indentation of your programs . . . . . . . . . . . . . . . . . . 54

3 Input and output 55

3.1 I/O ports and the 68230 parallel interface/timer chip . . . . . 56

3.1.1 The interface card . . . . . . . . . . . . . . . . . . . . 57 3.2 The flight-68k microprocessor board . . . . . . . . . . . . . 58

3.2.1 Octal tristate buffers on the interface board . . . . . . 59 3.2.2 Address decoder for the 68230 pi^{/}t . . . . . . . . . . . 59 3.2.3 The other connections between the 68000 and the pi^{/}t 60 3.2.4 The 68230 parallel interface/timer chip . . . . . . . . 60 3.2.5 The dip switches . . . . . . . . . . . . . . . . . . . . . 62 3.2.6 Register addresses in the 68230 pi^{/}t on the flight-68k 62 3.2.7 Initialising the pi^{/}t for bit i^{/}o . . . . . . . . . . . . . 64 3.2.8 How the interface card is connected to the parallel chip 67

3.3 The keypad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.3.1 Scanning the keypad . . . . . . . . . . . . . . . . . . . 68 3.3.2 Debouncing the keys . . . . . . . . . . . . . . . . . . . 71 3.3.3 How to detect if any key has been pressed . . . . . . . 72

A The operators in C and their precedence 74 A.1 Operator precedence . . . . . . . . . . . . . . . . . . . . . . . 74

B The ����� code for representing characters 77

C The memory map of the 68000 system in the laboratory 79

D The compile, assemble and link process 81 D.1 The preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . 81

vi  CONTENTS

D.2 The translator (or the main part of the C compiler) . . . . . 83 D.3 The listing file and the map file . . . . . . . . . . . . . . . . . 83 D.4 The assembler . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 D.5 Relocatable object file . . . . . . . . . . . . . . . . . . . . . . 84 D.6 The linker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

D.6.1 The startup code . . . . . . . . . . . . . . . . . . . . . 84 D.7 The library . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

D.7.1 The file flight.h . . . . . . . . . . . . . . . . . . . . 85 D.7.2 The library function DelayMs() . . . . . . . . . . . . . 86 D.7.3 The library function kbhit() . . . . . . . . . . . . . . . 87 D.7.4 The library function getch() . . . . . . . . . . . . . . . 87 D.7.5 The library function WriteSevenSegment() . . . . . . . 88 D.7.6 The library function ReadKey() . . . . . . . . . . . . . 89

D.8 The make program . . . . . . . . . . . . . . . . . . . . . . . . 90

E Why C and not assembly language? 92

F The Quincy interpreter—get it and use it! 94

F.1 What is Quincy? . . . . . . . . . . . . . . . . . . . . . . . . . 94 F.1.1 What is an interpreter? . . . . . . . . . . . . . . . . . 94 F.1.2 What can I do with Quincy? . . . . . . . . . . . . . . . 95

F.2 How to get a copy of Quincy . . . . . . . . . . . . . . . . . . . 95

G Compiler errors: they are there to help you 97 G.1 What do I do if there are lots of error messages? . . . . . . . 98 G.2 Error messages from different parts of the compiler . . . . . . 98

G.2.1 Error messages from the preprocessor . . . . . . . . . 98 G.2.2 Error messages from the main part of the compiler . . 99 G.2.3 Error messages from the linker . . . . . . . . . . . . . 100

CONTENTS  vii

H Editing, compiling, downloading and running a program 102 H.1 The program design . . . . . . . . . . . . . . . . . . . . . . . 102 H.2 The general procedure for editing, assembling, downloading

and running an assembly language program . . . . . . . . . . 102 H.3 Editing the program . . . . . . . . . . . . . . . . . . . . . . . 103 H.4 Compiling your program . . . . . . . . . . . . . . . . . . . . . 105 H.5 Downloading the program . . . . . . . . . . . . . . . . . . . . 105 H.6 Running the program . . . . . . . . . . . . . . . . . . . . . . 106

I Solutions to exercises 107

References 120

Index 122

List of Figures

2.1 A comparison between Pascal and C operators . . . . . . . . 20

2.2 The output required for the program in exercise 2.15 on page 49. 51

3.1 The connections between the 68000 and the parallel chip on the flight-68k microprocessor board, and the connections to port B and port C on the interface board. . . . . . . . . . 58

3.2 The bits for each segment in the seven segment display. Note that DP stands for “decimal point”. . . . . . . . . . . . . . . 62

3.3 The two settings for the jumper on the interface board …. 68

3.4 The connection of the keypad to the 68230 parallel interface/ timer chip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

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

D.1 The programs involved in compiling the program ⟨prog⟩.c. Note that the files ⟨prog⟩.i, ⟨prog⟩.s are deleted automatically unless you ask the compiler to keep them. Also ⟨prog⟩.lst will only be created if you ask the compiler to make it. . . . . 82

List of Tables

2.1 The sizes of different integral types used by our 68000 C crosscompiler in the laboratory. Note that with Quincy and msdos compilers, int and unsigned int are the same size as a short. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.2 The format characters for use with printf (). . . . . . . . . . . 17

3.1 The port addresses in the 68230 pi^{/}t and their names we have used in the laboratory. . . . . . . . . . . . . . . . . . . . . . . 61

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

3.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. . . . . . . . . . . . . . . 65

A.1 A list of all operators provided in the C programming language, showing level of precedence. . . . . . . . . . . . . . . . 76

B.1 The ascii table . . . . . . . . . . . . . . . . . . . . . . . . . . 78

D.1 The return values of the library function ReadKey(). . . . . . 90

Program examples

2.1 Definitions of integral-type variables. . . . . . . . . . . . . . . 10 2.2 An example showing use of printf (). . . . . . . . . . . . . . . 12 2.3 An example showing use of printf () and the newline character,

’\n’. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.4 A program that uses printf () to display integers. . . . . . . . 18 2.5 A program that uses printf () to display a few variables of type

int, unsigned int, unsigned short, char. If you use Quincy, an integer must be in the range −2^{15}..2^{15} − 1, and an unsigned integer must be in the range 0..2^{16} − 1. . . . . . . . . . . . . . 19

2.6 Some examples of expressions. . . . . . . . . . . . . . . . . . . 19 2.7 This program demonstrates the compound assignment oper-

ators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.8 A program that shows how to use the bitwise AND operator

‘&’ to clear bits 0, 1, 2 and 3 of an integer variable. . . . . . . 26 2.9 This program shows the use of the bitwise OR operator, ‘|’. . 28 2.10 This program shows the use of the bitwise exclusive-OR op-

erator, ‘ ’. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.11 examples of valid statements in C. . . . . . . . . . . . . . . . 31 2.12 A program that uses an if statement and the modulus oper-

ator, ‘%’. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.13 A program using while. . . . . . . . . . . . . . . . . . . . . . 35 2.14 A program equivalent to 2.13 on page 35, except that it uses

a for loop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

PROGRAM EXAMPLES  xi

2.15 This program uses a do loop, and produces the same output as the while and for loop programs, 2.13 on page 35 and 2.14 on page 37. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

2.16 A program using two nested for loops. . . . . . . . . . . . . . 39 2.17 A program showing nested while loops. The output in the

terminal window is exactly the same as for program 2.16 on page 39. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

2.18 Using the break statement. . . . . . . . . . . . . . . . . . . . 41 2.19 A program demonstrating the continue statement. . . . . . . 42 2.20 A program to use an array to look up patterns to display on

the leds in the laboratory. . . . . . . . . . . . . . . . . . . . 44 2.21 A program that uses a nested for loop to sort an array. (This

is called a bubble sort). . . . . . . . . . . . . . . . . . . . . . . 46 2.22 A program that shows parameters begin passed to a function. 48 2.23 A program that calls a Silly() function that shows how return

terminates execution of a function. . . . . . . . . . . . . . . . 52 3.1 The function InitParallelChip(), which is called by the startup

code which is executed before main() begins. The purpose of InitParallelChip() is to initialise the pi^{/}t appropriately for the interface board with the leds, seven-segment display, dipswitches and 3 × 4 telephone keypad. . . . . . . . . . . . . . . 67

D.1 A definition of DelayMs(). . . . . . . . . . . . . . . . . . . . . 86 D.2 The assembly language implementation of kbhit(). . . . . . . 88 D.3 The implementation of the library function getch(). . . . . . . 88 D.4 The library function WriteSevenSegment(). . . . . . . . . . . . 89 D.5 The library function ReadKey(). This is an implementation

of the algorithm described in section 3.3.1 on page 69. . . . . 90 H.1 All your programs will have a form similar to this. . . . . . . 104

Chapter 1

Introduction

This booklet is intended to help you to write C programs in your laboratory work. It is too small to replace a good text book on C, but I hope that it is a useful reference for you. It also introduces the Quincy C interpreter, which you may take home and use on your own computer to practice writing programs in C.

1.1 How should you learn the C language?

The best way to learn a computer language is by using it. Because we use ansi C, any ansi C compiler or interpreter understands the same keywords. Obtain the Quincy C interpreter (see appendix F on page 94 and use it to do the exercises and to run the examples in this book.

1.1.1 Practice!

There are many examples and exercises in this book which are designed to work with the Quincy interpreter or Turbo/Borland C, Microsoft C or almost any other C compiler less than 5 years old. All programming exercises and all examples that do not #include <flight.h can be run on such interpreters and compilers. Run the examples, and change them to see what happens. There are exercises in this book; solutions are given to all exercises, in appendix I on page 107. Work through the exercise before reading the solution!

1.1.2 The Quincy interpreter

There is a free C interpreter available from the network in the lab, called Quincy. Copy Quincy from the f:\quincy directory in the laboratory and use it at home. There is a step-by step explanation of how to do this in section F.2 on page 95. Quincy is quick and easy to use and has good on-line help. There is a book in the library written by the person who wrote Quincy; it is [Ste94]. Quincy is discussed in appendix F on page 94.

In particular, it will help you in this course if you become familiar with if statements, while and for loops, defining variables, and using functions with parameters. Also practice using printf () to print out the values of variables, and using it to see what is happening inside your program.

1.1.3 Use your laboratory!

The examples that #include <flight.h> should be run in the microprocessor lab: remember, the door is open to you as much as possible. The lab is available to you for your use most of the time. Make use of this! I run the laboratory with an open door policy. That means that whenever I or Henry (the capable laboratory technician) is there, you may come in and make use of the equipment and ask me questions about your laboratory work or about lectures. Work through the exercises I have set you in your workshops. Try any exercises I have provided in this book. Ask questions. You may also ask me any questions you have about C, C^{++} or Pascal. If you do not make use of this policy, you are not making proper use of the money you have spent on your tuition fees!

1.1.4 Books about C programming

There are many books on C programming with ansi C; the ones in the references section on page 120 are just a tiny selection of what is available in the library. The standard reference book by Kernighan and Ritchie, [KR88], is excellent once you are familiar with the language, but in the meantime I suggest books such as that by Al Stevens, [Ste94], with which the Quincy interpreter is provided. Unfortunately, [Ste94] assumes a (little) background in programming. The book [Per94] may help, but I personally do not like it very much—you may have a different opinion, so have a look at it if you need more help. If you need a book in addition to this one, search through the library for one you like. Please let me know if you find one you like. A useful guide is the [Sum]. This is available on the network.

It may help to buy a book on C; however, I am told that many books which are translations of English language texts have mistakes in translation. If you buy a book in the Chinese language, I suggest that you buy one written directly in the Chinese language by its author, unless you know that the translator is a programmer.

For information about assembly language and microprocessors, please refer to the text book, [Hor92], and also the booklet [Urb96] which is available to all students and staff of this College.

For students who are interested in projects using microprocessors and who would like a practical magazine with lots of interesting ideas, it is hard to do better than [CC].

1.2 The convention used here for C programs

Here are some concepts in the C programming language, some of which will be new to you. You will use many of these in your labs. The C language is very much like the Pascal language. I refer often to the Pascal language here, since some students are familiar with Pascal. If you do not know the Pascal programming language, then ignore the references to Pascal—it is not necessary to learn Pascal before your learn C.

In the programs shown in the lecture and lab notes, all keywords in the C language are shown in a bold font like this: while . All keywords are part of the language and will be found to be common to other ansi^{1} C compilers. You should learn all the keywords of the C programming language.

Other variables and names which are not built in to the language are shown in a font like this: printf ().

Nick Urbanik is often in the lab, and you are welcome to use the computers in the lab to practice your C with the flight-68k boards as long as he is there.

1ansi means American National Standards Institute, and is the organisation which is maintaining a standard which all C compiler manufacturers try hard to make their compilers conform to.

1.3 Embedded systems

The title of this book includes ‘embedded systems’. So what is an embedded system?

Many people think that desktop pcs running programs such as word processors, spreadsheets or games would be the most common application of microprocessors or computers.

However, such a viewpoint is wrong—most microprocessors are part of devices such as washing machines, televisions, remote controllers, pagers, video recorders, motor vehicles, refrigerators, watches and many other devices that you use or see every day.

This course is an introduction to embedded systems. An embedded system is a microprocessor system that is built into a machine—such as a washing machine, a tv, a radio or a robot—instead of being a general purpose desktop computer. Because an embedded system usually does not have a large video screen and a big keyboard, we often communicate with an embedded system via a pc and a serial cable. In the laboratory you will use a pc to communicate through a serial cable to a single-board computer development system. This development system can be used to test and debug^{2} software that will run on hardware that you as a system developer design and build.

The development system is called the ���������� microprocessor board. The flight-68k has a Motorola 68000 microprocessor at its heart. On the flight-68k some programs are stored permanently in roms^{3}. These programs are called the monitor firmware. You will use some of these programs in your first lab session. You can read about them in chapter 4 of The ���������� Users manual, [Fli88], which is available in the laboratory.

1.4 How should I use this book?

How to use this book:

1. Bring it to each lab session

2. (a) Bring a floppy disk to the laboratory (b) Collect the Quincy C interpreter^{4} (c) take it home or to work

2Debugging is the process of finding errors in a system and removing those errors.

3The word ‘rom’ stands for “read only memory”.

4See section F.2 on page 95 for details of how to obtain the Quincy interpreter.

ver 1.7, 27 February 1996

(d) install it on a pc

(e) use it:

(f) read the rest of appendix F for information about Quincy.

(g) work through the examples

(h) work through the exercises

• look at the solution after you have tried to solve the exercise using Quincy or a computer in your laboratory

• type the solution into a computer and run it to see what it does

• If it is not clear to you how the program works,

∘ single-step through the program using Quincy: use the Run→Step command, or press the ⟨F7 ⟩ key repeatedly. ∘ Use Debug→Add a watch variable to put interesting variables into the watch window so that you can see their value change as you step through the program.

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 126!

The C programming language

2.1 Constant values in C

2.1.1 Constant integer values

We can write a constant integer value in our programs in decimal, octal or hexadecimal.

We can write the value 23_{10} in three different ways, all giving the same result:

23  decimal
027 octal
0x17 hexadecimal

Decimal values are the default. We can enter a number in octal by making the first digit zero. So be warned: 027 ≠ 27!

Hexadecimal values begin with ‘0x’.

All of these will be of type int unless we specify otherwise. In each of these cases, an identical value is used by the compiler.

If we write a number such as 1, or 0xf, in C these are both integers—that is, on the 68000, they are 32 bits long; on ms-dos systems—including the Quincy interpreter—they are 16 bits long.

This means that with our 68000 system, the bit pattern for 0xf is:

0000 0000 0000 0000 0000 0000 0000 1111.

2.1.2 Constant character values

A character is written in the C language by putting single quotes ‘ ’ ’ around a single character. So the capital letter ‘A’ is represented in C as 'A', and the lower case character ‘h’ is represented as 'h'.

C does not make a special distinction between a character and its ascii value. Since the ascii value of 'A' is 41_{16} or 65_{10}, 'A' is equivalent to 0x41 or 65. Similarly, 'h' is equivalent to 104 or 0x68. See appendix B on page 77 for more about ascii codes.

Do not put single quotes around more than one character if you want a string. See the next section.

2.1.3 Constant strings

A string is a group of zero or more characters. A string is written in the C language by putting double quotes ‘ ” ’ around the characters. Here are some examples of constant (or “literal”) strings in C: "This is a string",

"This is another string with a newline\n"; here is a “null” or empty

string: ""

In the C programming language, all strings end with the nul ascii character, which has the ascii value zero. This means that the string "A" contains two characters: the character 'A' followed by the nul character. This means that "A" is different from 'A'.

▶ Exercise 2.1 How many characters are there in the “null” string ""? Write down what these characters are.

A string is represented in C as an array of characters. See the section 2.9 on page 41 for more information about arrays.

2.1.4 Named constants in C

It is usually better to write a name that shows what a number is rather than filling your program with lots of unnamed constant integer numbers. For instance, it is better to write NUMPATTERNS than just the number 8 for two reasons:

• Your program is easier to understand, important for a bigger program

• If you later change to a different number of patterns, you only need to change your program in one place instead of looking for all places where you wrote ‘8’ and wondering whether each ‘8’ was the number of patterns (or was it something else?)

Pascal provides the keyword const to declare constants. The same keyword exists in the C language. You may also use the preprocessor to define a constant with #define . These two methods both define a constant value:

#define NUMPATTERNS 8 const int numPatterns = 8;

Note that #define does not have a semicolon at the end, since it is a command to the preprocessor, while the const ant value must be terminated by a semicolon because it is read by the compiler itself. See section D.1 on page 81 for more about the preprocessor.

2.2 Integral types

Data processed by a computer is a series of 1s and 0s. It is the type of a piece of data that gives it some meaning.

Here we look at integral types. An integral type describes data that can hold counting numbers, such as 0, 1, 2, 3, …. There are two kinds of integral type: signed and unsigned . A signed integral type can hold negative counting numbers as well, such as …, −4, −3, −2, −1.

Pascal provides the integer and char integral data types. In C, there are corresponding types int and char.

Each type has a size, in bytes. This size depends on the kind of machine the compiler is made for. Each integer type can be signed or unsigned . Table 2.1 on the facing page shows the name of the type, the size in bytes,

type  size  minimum value  maximum value
char  1  −2^{7} = −128  2^{7} − 1 = 127
unsigned char  1  0  2^{8} − 1 = 255
short  2  −2^{15} = −32 768  2^{15} − 1 = 32 767
unsigned short  2  0  2^{16} − 1 = 65 535
int, long  4  −2^{31} = −2 147 483 648 2^{31} − 1 = 2 147 483 647
unsigned int, unsigned long  4  0 2^{32} − 1 = 4 294 967 295

Table 2.1: The sizes of different integral types used by our 68000 C crosscompiler in the laboratory. Note that with Quincy and ms-dos compilers, int and unsigned int are the same size as a short.

and the range of values the type can hold. These are correct for the MC68000 cross compiler used in the lab. Turbo C, the Quincy interpreter (and other MS-DOS compilers) use 16-bit int and unsigned int types.

▶ Exercise 2.2 For each of the following types:

• integer

• an unsigned character

• a short integer • a character

• an unsigned integer

(a) Write a definition of a variable of each type in C.

(b) How many bits long is each type on the Motorola microprocessor system?

(c) How many bits long is each type on ms-dos systems, including Quincy?

(d) Which variable is represented using two’s complement? Which is represented by a magnitude only?

(e) Write a printf () statement to print out the value of each variable declared in a.

2.2.1 Definitions and declarations of integer-type variables

In C, we put the type first when declaring a variable, and put the variable name second. Program example 2.1 on the next page gives definitions of Program example 2.1 Definitions of integral-type variables.

char ch; /∗ byte-sized variable called ch. ∗/ unsigned char uch; /∗ byte-sized variable called uch. ∗/ int i; /∗ 4-byte integer called i, 68000, not ms-dos ∗/ unsigned int ui; /∗ 4-byte variable called ui, 68000, not ms-dos ∗/ long li; /∗ 4-byte variable called li ∗/ unsigned long uli; /∗ 4-byte variable called uli. ∗/

variables of the types shown in table 2.1 on the preceding page. These definitions are also called declarations of the variables.

An important rule in C is that a function or variable must be declared before it is used.

A program must have exactly one definition for a function or variable that is used. Note that local variables in different blocks are considered different variables, even when they have the same name. See section 2.8.2 on page 31 for the meaning of “block”.

When a variable is defined, it may also be initialised:

int i = 0;

A declaration may declare several variables at once:

int i = 0, j, k = 67;

Here there are three integer variables defined. i and k were initialised with 0, 67 respectively; j is uninitialised. All three are of type int.

2.3 The meaning of #include

The line #include <flight.h> is a command to the preprocessor, which is a part of the C compiler. This command means: “look for the file flight.h in the include directory (which is f:\hpcc68k\include), and put a copy of this file into a copy of the program, just where the #include command appears.” The result is fed into the rest of the compilation process. All commands beginning with # are commands to the preprocessor. The preprocessor also removes all comments from your program (see section 2.4 on the facing page to read about comments). Read section D.1 on page 81 for more about the preprocessor and how to look at the output from the preprocessor in the lab.

2.4 Comments in the C language

In the C language, comments are written beginning with the symbols /∗ and end with the symbols ∗/. A comment can be more than one line long.

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

In the labs, you do not need to type the comments from the 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 at the beginning of each function explaining what it does. This saves a lot of time when you or someone else wants to change the program a year later.

2.5 C is case sensitive

A variable with the name SevenSegment is different from a variable with the name sevenSegment. Capital letters and lower case letters are treated differently. This means that when your program works with a particular variable or function, it should always be written in exactly the same case.

For instance, you need to type “printf ” rather than “Printf ” or “PRINTF” and “KEYPAD” rather than “KeyPad”.

2.6 The use of printf ()

As you may now realise, the printf () function is very useful for showing what is happening inside your program. In our work with embedded systems, we use it mainly for debugging — finding logical errors we have made when writing our programs. printf is used to display program output in the terminal window by serial transfer.

In particular, printf () is able to show the value of variables. This is often a help when the program does not work in the way we expected. Here we look at ways we can print out the values of some variables.

Pascal provides writeln when we want to print a newline, and write if we don’t want want the output to go onto a new line. In C, the printf () library function can provide a newline wherever you want one.

printf () uses the symbol ‘\n’ to mean, “finish the line here and start a new line.” We call \n the newline character. So if we write

     printf ( "Hi -- this is your " );
     printf ( "microprocessor talking!\n" );
     printf ( "This is line2.\n" );

or

     printf ( "Hi -- this is your microprocessor talking!\nThis is line2.\n" );

we will give the same output.

Program example 2.2 An example showing use of printf ().

/∗ This is an example of using the printf () library function ∗/

/∗ These few lines are comments about the program. ∗/

/∗ The example is written by Vincent & Nick. ∗/

#include <stdio.h>

int main( void )

{

        printf ( "Hi! How are you?\n" );

        printf ( "Welcome to this class.\n" );  10

return 0; }

When run, the following appears in the terminal window:

Hi! How are you?
Welcome to this class.

Program example 2.3 on the facing page shows the use of the newline character, '\n'.

▶ Exercise 2.3 Write a C program using the Quincy C interpreter to write your own name on the terminal.

Program example 2.3 An example showing use of printf () and the newline character, ’\n’.

#include <stdio.h>

int main( void )

{

        printf ( "Hi! \nHow are you?\n" );
        printf ( "Welcome to \nthis class.\n" );

return 0; }

The result is:

Hi!
How are you?
Welcome to
this class.

2.6.1 Displaying variables

To use printf () to display variables, there are some formatting characters that we can use to get the output exactly as we want it. printf () is very general purpose, and it can control the output display format very precisely.

Here we look at just a few of the ways of formatting the output.

Displaying an integer

Suppose we have two integer variables that were declared as:

int fahrenheit; int celsius;

Then we can print their values like this:

        printf ( "fahrenheit = %d, celsius = %d\n", fahrenheit, celsius );

There are three parts to this call to printf (). The first part is the format string, which is between double quotes (") and may contain some percent symbols (%) followed by one or more format characters. Here we are using the format character d. For each percent—format character pair, we need to put a value after the format string. The values must be separated by commas. These two other values are shown here, the variables fahrenheit and celsius.

The format character here is ‘d’ for decimal; the value is shown in decimal. If we want to see it in hexadecimal, we can use the format characters ‘%x’ instead.

The first exercises for workshop 3 show some other examples of displaying integers. Note that these must be integer types, i.e., int or unsigned int .

If the number is an unsigned int , then the output will be correct if you use either %u or %x, otherwise unsigned numbers bigger than 32 767 will be shown as negative values.

Displaying a short integer

A sixteen-bit value can be represented by a short or unsigned short . We use the letter ‘h’ in front of the ‘d’ or ‘h’ or ‘u’ to indicate that we are using a ‘half’ sized value, i.e., a 16-bit value instead of a 32-bit value. If we have a short integer variable defined as:

short sh;

We can display this value on the terminal as:

        printf ( "The value of `sh' is %hd\n", sh );

If we have an unsigned short int eger defined as:

unsigned short ush;

We can display the unsigned value of ush on the terminal as:

        printf ( "The value of `ush' is %hu\n", ush );

Or, if you want to display it in hexadecimal, you can write:

        printf ( "The hexadecimal value of `ush' is %hx\n", ush );

Displaying an unsigned char

Suppose we have an unsigned character variable declared as:

unsigned char pattern;

pattern is a variable that can hold eight bits, or a byte. Suppose we use it to to read the keypad like this:

pattern = KEYPAD;

Now we may want to print the numerical value of pattern in hexadecimal. We can do that with:

printf ( "pattern = %hx\n", pattern );

There are two characters used in the format string here: ‘x’ means display an integer in hexadecimal. The letter ‘h’ between the ‘%’ and the ‘x’ is for “half”; the value occupies half an integer, i.e., a word. But you know that a character is a byte—why does it occupy a word? This is because the character is taken from the stack by printf (); you will see later that when a character is pushed onto the stack, an extra zero byte is also pushed onto the stack, making a sixteen-bit word.

▶ Exercise 2.4 Write a C program that defines an initialised character variable, and which displays the signed decimal ascii value, the unsigned decimal ascii value, the hexadecimal ascii value and the character itself.

Displaying a long int

Suppose we have a long integer variable declared as:

long li;

We print its value like this:

printf ( "li = %ld\n", li );

There are two characters used in the format string here: ‘d’ means display an integer in decimal. The letter ‘l’ between the ‘%’ and the ‘d’ is for “long”; the value is a long integer.

2.6.2 Displaying a string

We can display a string with the %s format characters:

        printf ( "A big hello from %s.\n", "Nick" );

This displays the following message on the terminal:

A big hello from Nick.

2.6.3 A warning about the format characters and printf ()

An important thing here is that the format characters must match the size of the variable you want to display, otherwise you may find that some nonsense is displayed on the terminal screen.

A summary of the format characters

For int, use %d. For unsigned int you can use %u to see the decimal unsigned value and %x to display the hexadecimal unsigned value. If you use other format characters, you may see a value that has little to do with the number you want to see.

For long, use %ld, %lx or %lu.

For short , use %hd. For unsigned short , use %hu to see the decimal unsigned value and %hx to display the hexadecimal unsigned value.

For char use %hd, %hx or %hu to see the number value of the byte (i.e., its ascii value). To see the character as just a single character instead of its ascii value, use %c.

If the number is any unsigned type, then the output will be correct if you use either u or x instead of d, otherwise large unsigned numbers will appear as negative values.

2.6.4 Example programs showing the use of printf ()

Program example 2.4 on page 18 uses printf () to display integer type variables.

to display format characters display as
char  %c  the character itself
char  %hd  signed decimal ascii value
char  %hu  unsigned decimal ascii value
char  %hx  unsigned hexadecimal ascii value
short  %hd  signed decimal value
unsigned short %hu  unsigned decimal value
unsigned short %hx  unsigned hexadecimal value
int  %d  signed decimal value
unsigned int  %u  unsigned decimal value
unsigned int  %x  unsigned hexadecimal value
long  %ld  signed decimal value
unsigned long  %lu  unsigned decimal value
unsigned long  %lx  unsigned hexadecimal value
string  %s  display the characters in the string

Table 2.2: The format characters for use with printf ().

Program example 2.5 on page 19 defines three variables and displays them on the terminal.

▶ Exercise 2.5 Write a C program that:

• defines three variables: an int, a short and a char, and

• initialises these three variables to some value, and

• displays the numerical signed value of each variable on the terminal.

2.7 Expressions and operators

Al Stevens defines an expression on page 25 of his book [Ste94] somewhat like this: An expression is a combination of constants, variables, function calls and operators that, when evaluated, have a value. Program example 2.6 on page 19 shows some expressions. Note that getch() is a library function, described in section D.7.4 on page 87. An expression is made of one or Program example 2.4 A program that uses printf () to display integers.

#include <stdio.h>

/∗ This program demonstrates using printf () to display integers ∗/

int main( void )

{ int i = 2; int j = 3;

printf ( "%d  and  %d", i, j );  10

printf ( "%d and %d", j, i );

return 0; }

In the terminal window you will see:

2  and  3
3  and  2

more operations. The objects of the operation(s) are referred to as operands. The operations are represented as operators.

Operators that act on one operand are called unary operators.

An operator is a symbol, like ∗, +, … that performs an operation on one or two operands. These operands are often numbers or variables.

2.7.1 Expressions and operators—C compared with Pascal

C has a large number of operators, more than are available in Pascal. See table 2.1 on page 20 for a comparison that you may use as a reference.

Standard Pascal provides few of these operators. Most Pascals—including Turbo Pascal—have non-standard extensions to provide some of these facilities. Note that all C compilers provide all these operators (and more). A complete list of every operator available in the C programming language is given for your reference in appendix A on page 74.

Some of these operators you already know. Here we examine some you may Program example 2.5 A program that uses printf () to display a few variables of type int, unsigned int, unsigned short, char. If you use Quincy, an integer must be in the range −2^{15}..2^{15} − 1, and an unsigned integer must be in the range 0..2^{16} − 1.

#include <stdio.h> int main( void ) {

int i = −40000; /∗ On 68000 system only. With Quincy, must ∗/ unsigned int j = 400000; /∗ use a smaller value. ∗/ short sh = −4000; unsigned short ush = 40000; char c = 'A';

printf ( "i = %d, sh = %hd\n", i, sh );  10

        printf ( "j = %u, ush = %hu\n", j, ush );
        printf ( "The character c displayed as a character is `%c'\n", c );
        printf ( "The ASCII value of c is %hd, and in hex it is %hx", c, c );

return 0; }

In the terminal window you will see:

i = -40000, sh = -4000
j = 400000, ush = 40000
The character c displayed as a character is `A'
The ASCII value of c is 65, and in hex it is 41

Program example 2.6 Some examples of expressions.

1 + 2

3 ∗ 4 − 6 > 7 && 27 < 5 conversionConstant ∗ getch()

be less familiar with.

20  The C programming language
C operator  Turbo Pascal  What it does

equivalent

++  increment
−−  decrement
!  not  logical NOT; Don’t confuse with ∼
∼  not  bitwise NOT; Don’t confuse with !
∗, &  , @  dereference, address-of
/  /, div  division operator
%  mod  modulus operator
∗  ∗  multiplication operator
+, −  +, −  plus, minus
<<, >>  shl, shr  bitwise shift
<,<=, >,>=  <,<=, >,>=  relational operators
==, !=  =, <>  test for equality, inequality
&  and  bitwise AND; Don’t confuse with &&
xor  bitwise exclusive OR
|  or  bitwise OR; Don’t confuse with | |
&&  and  logical AND; Don’t confuse with &
| |  or  logical OR; Don’t confuse with |
=  :=  assignment
∗=, /=, %=  no direct  Compound assignment operators
+=, −=, <<=  Pascal equivalent

>>=, &=, |=, =

Figure 2.1: A comparison between Pascal and C operators

2.7.2 The assignment operator =

Assignment works in the same way in both C and Pascal. There must be something that can be assigned to on the left of the assignment operator. This is sometimes called an lvalue, because it is on the left. The value at that location will change. This must be a value with an address; it cannot be a constant:

2 = 3; /∗ No, it won’t work — a constant integer is not an lvalue. ∗/

You can think of an lvalue as a location value.

The value on the right can be a constant. The value on the right side is sometimes called an rvalue, because it is on the right side. You can think of it as a read value. It is not changed by the assignment.

The assignment operator and �^{�}�

When we do input and output with the MC68000, we use ordinary assignment statements, since the 68000 has memory-mapped �^{�}� , as discussed in lectures. With a memory-mapped i^{/}o architecture, we use the same instructions for input and output as we use for ordinary memory access. This does not mean that hardware—like a register in a 68230 parallel chip—will behave like a variable. In the lab, the name KEYPAD represents the port A data register, which is connected to the telephone keypad. Suppose we have a byte-sized variable pattern that we use like this:

KEYPAD = 6; /∗ output to the keypad ∗/ pattern = KEYPAD; /∗ input from the keypad ∗/

Do not expect pattern to now have the value 6! This is because the input operation reads the voltages on the pins, while the output operation changes the contents of the flip-flops in the output register. Chapter 3 on page 55 discusses input and output in detail.

2.7.3 The compound assignment operators

There are some “shorthand” assignment operators in C that combine an arithmetic or logical operation with assignment. It is probably easiest to show how they work with examples. Program example 2.7 on the following page shows many compound assignment operators.

2.7.4 The relational operators

Both Pascal and C have relational operators. They compare two values and give a result with the value 1 or 0. They are used mostly in the ⟨condition⟩ of an if statement or of while and for loops.

The test for equality, == and the test for “not equals”, !=, use different symbols from Pascal, but do the same kind of job.

The C language does not have a different built-in type for the values true and false; instead, any value which is zero is equivalent to the value false, while any non-zero value is considered to have the logical value true. Read section 2.7.6 on page 23 for more about this.

Take care not to confuse the assignment operator, =, with the test for equality, ==. This is easy to do and can give confusing results.

Program example 2.7 This program demonstrates the compound assignment operators.

/∗ This program demonstrates the usage of the compound ∗/ /∗ assignment operators. ∗/ /∗ The expression a += b is equivalent to a = a + b ∗/

#include <stdio.h>

int main( void ) {

int i, j, k;

10

i = 2; j = 3; k = 4;

i += j; /∗ like i = i + j; ∗/

printf ( "The first result is %d\n", i );

i −= j; /∗ like i = i − j; ∗/

printf ( "The second result is %d\n", i );

i ∗= j; /∗ like i = i ∗ j; ∗/

printf ( "The third result is %d\n", i);  20

i /= j; /∗ like i = i / j; ∗/

printf ( "The fourth result is %d\n", i );

i %= j; /∗ like i = i % j; the ‘%’ is the modulus operator. ∗/

printf ( "The fifth result is %d\n", i );

return 0; }

If you compile and execute this, you will see these results in the terminal window:

The first result is 5
The second result is 2
The third result is 6
The fourth result is 2
The fifth result is 2

2.7.5 The increment and decrement operators, ++ and −−

In this course, we only use these operators in for loops, although the language allows them to be used anywhere. By itself, “++i’ has the same meaning as ‘i = i + 1’, and by itself, “−−i’ has the same meaning as ‘i = i − 1’. Appendix A on page 74 shows that the ‘++’ and ‘−−’ operators have a high priority.

2.7.6 The logical operators

There are three logical operators, logical AND: &&, logical OR: | | and logical NOT: !. The result of these operations is always either true or false. They are often used in in the ⟨condition⟩ of an if statement or of while and for loops.

They are equivalent to the Pascal keywords and, or and not. Here is an example, which prints the message if i is negative or greater than ten:

if ( i < 0 | | i > 10 )

           printf ( "i is out of range.\n" );

The value of true and false

The result of an expression with a relational operator is the Boolean value true or false. But C has no special Boolean type; in fact, the result of a logical operation is of type int. So what is the integer value of true and false?

The answer for false is that it has the integer value 0; the expression 1 > 2 has the integer value zero.

So what about true? The answer is, “it depends”. The expression 2 > 1 has the integer value 1. All expressions using logical operators have the integer value 1 or 0. However, as we shall see later, when we test the logical value of an expression with an if, for, while or do statement, if that expression has the value zero, the test has the result false, while if it has a non-zero value, the test has the logical result true.

In your laboratory work, the names true and false are used frequently. But these are not special keywords in the language—so where do they come from? They are defined in the header file, flight.h.

▶ Exercise 2.6 Give the integer and logical value of the following logical expressions.

(a) 7 == 6
(b) 5 != 7
(c) 5 < 7
(d) 9 > 0
(e) 5 < 7 && 9 < 0
(f) 5 < 7 | | 9 < 0
(g) 10 >= 0 && 30 <= 30 | | ! ( 50 < 0 ) && 7 − 3 > 5

2.7.7 The bitwise operators and bit patterns

Bitwise operations are often required when programming hardware such as the 68230 parallel chip. In the past, the only way to do such operations was to use assembly language. C provides these operations as a standard part of the language, which makes C especially suited to programming microprocessor systems.

Very often, we need to isolate some particular bits in a bit pattern. A bit pattern is an integral type that we use as a collection of individual bits rather than as a number to do arithmetic with. Bit patterns are also sometimes called bit vectors. Examples of bit patterns that you have used in the lab include the values you wrote to the leds. Individual bits turned individual leds on and off. In C we usually use a hexadecimal base for bit patterns, as it is easier to see where the individual 1s and 0s are in a hexadecimal value than in a decimal value.

We may need to clear some particular bits to zero. We need to be able to set some particular bits to 1. We sometimes need to find the 1’s complement of a bit pattern. You also sometimes need to shift a bit pattern left or right. In C, there is an operator for each of these operations. Here you will see five bitwise operators. They are called bitwise operators because they work with individual bits.

Do not confuse the bitwise operators with the logical operators. The logical operators have only two possible results—a value of true or false—while the bitwise operators can give a wide range of results.

The C bitwise AND operator, &

The bitwise AND operator has the symbol &. We usually use it for clearing individual bits. As an example, we can represent the binary pattern 1100 0011 1010 0101 in our C program with the hexadecimal number 0xc3a5. Now let us suppose we have an integer with this bit pattern:

int pattern = 0xc3a5;

Also suppose we only want the bottom nibble (four bits) because we want to display those four bits on the seven segment display. How do we set all the other bits to zero? The answer is to use the bitwise AND operator:

pattern = pattern & 0x000f;

This applies the AND operation between each corresponding pair of bits. The symbol ‘∧’ is the mathematical symbol for ‘AND’.

1100 0011 1010 0101

0000 0000 0000 1111 ∧

0000 0000 0000 0101

Note that 0xf is the same as 0x000f, that is, on our 68000 systems, they are both 32-bit numbers. See section 2.1 on page 6. So the line

pattern = pattern & 0xf;

will have the same effect.

The result is that pattern now has the value 0x0005, or 5. You should remember how the AND operation works between single bits from your previous course in logic.

Program 2.8 on the following page shows how to clear bits 0, 1, 2 and 3 of a variable, but leaving all other bits unchanged.

Note that on ms-dos systems, integer constants have a size of 2 bytes, while on our 68000 system, integer constants have a size of 4 bytes. This is discussed in section 2.1.1 on page 6. So with Turbo C or Quincy, ∼0xf = 0xfff0, while on our 68000 system, ∼0xf = 0xfffffff0. It is better (because it is more portable^{1} to write ∼0xf rather than 0xfffffff0 or 0xfff0, since ∼0xf will work in both systems where an int is 2 bytes and where an int is 4 bytes in length.

1A portable program is one that can be compiled on many different systems without change, or with minimum change.

Program example 2.8 A program that shows how to use the bitwise AND operator ‘&’ to clear bits 0, 1, 2 and 3 of an integer variable.

#include <stdio.h> int main( void ) {

int pattern = 0x4567;

int pattern2 = pattern & ∼0xf;

        printf ( "pattern = %x, and pattern & ∼0xf = %x.\n",

pattern, pattern2 ); return 0;

}

Using bitwise AND to examine the ROW bits in the keypad

Here is an example relevant to reading a keypad. To understand this better, read section 3.3 on page 68 first. It may also help to read your laboratory sheet about keypads.

Suppose we have a variable, pattern, which is a byte-sized variable into which we read the value from the port connected to the keypad:

unsigned char pattern;

pattern = KEYPAD;

Now we want to read the row bits (i.e., bits 3 to 6 of port A) and see if they are all 1s or if some of them are not 1. If they are all 1, then no key in the current column was pressed. Otherwise, a key in the current column could have been pressed.

The problem is that the other bits could have other values which we are not interested in. How do we find out if a key was pressed?

Let me write the bits in the byte pattern. The bits in pattern correspond to the bits from port A.

Bit number PA_{7} PA_{6} PA_{5} PA_{4} PA_{3} PA_{2} PA_{1} PA_{0}
Mask value 0  1  1  1  1  0  0  0

If we bitwise AND pattern with the binary number shown as the “mask value” in the table above, then all the other bits in pattern that we are not interested in will be set to zero. The binary number 0111 1000_{2} is 78_{16} in hexadecimal. In C we would write this as 0x78. To clear bits 0, 1, 2 and 7 of pattern, we can write:

pattern = pattern & 0x78;

Here we call 0x78 a bit mask because we are using it to hide the value of bits 0, 1, 2 and 7.

But how do we find out if a key was pressed? Well, pattern will now have the value 0x78 if bits 3, 4, 5 and 6 are set to 1, so we can test if these bits are all 1 by:

if ( pattern == 0x78 )

/∗ Then no key was pressed in this column ∗/ else

/∗ A key was pressed ∗/

The symbol “==” is a test for “is equal to”. We can also use the symbol “!=” to test for “not equal to”. Pascal provides the symbol <> to mean the same thing.

The C bitwise OR operator, |

The bitwise OR operator has the symbol |. We usually use it for setting individual bits. Let’s see an example. We have a number where we want to set the three bits numbers 4, 5 and 6 in the second nibble but leave all the other bits as they were:

int pattern = 0x45a6;

If we OR this bit pattern with the pattern 0x0070, we will set the bits 4, 5 and 6 but leave all the other bits as they were:

pattern = pattern | 0x0070;

This applies the OR operation between corresponding bits. The symbol ‘∨’ is the mathematical symbol for the OR operator.

0100 0101 1010 0110

0000 0000 0111 0000 ∨

0100 0101 1111 0110

The result is that pattern now has the value 0x45f6.

Program 2.9 on the next page shows the use of the bitwise OR operator.

Program example 2.9 This program shows the use of the bitwise OR operator, ‘|’.

/∗ The use of the bitwise OR operator ∗/

#include <stdio.h>

int main( void )

{ int i = 0xf; int j = 0x12; int k;

10

k = i | j;

        printf ( "The bitwise OR of 0x%x and 0x%x is: \n", i, j );

printf ( "0x%x\n", k );

return 0; }

The terminal output is:

The bitwise OR of 0xf and 0x12 is:
0x1f

The operation is shown below. The symbol ‘∨’ is the mathematical symbol for the OR operator.

\begin{matrix}0000 1111 \\ 0001 0010 & ∨ \\ 0001 1111 \end{matrix}

The C bitwise NOT operator, ∼

The bitwise NOT operator has the symbol ∼. We use it to get the ones’ complement of an integer number. If we apply the bitwise NOT operator to a number, the bits that had the value ‘1’ are changed to ‘0’, while zero bits are changed to ones.

An example:

short pattern = 0x45a6;

If we apply the NOT operator to this pattern: pattern = ∼pattern;

each bit is complemented. The symbol ‘¬’ is the mathematical symbol for ‘NOT’.

\begin{matrix}0100 0101 1010 0110 & ¬ \\ 1011 1010 0101 1001 \end{matrix}

The result is that pattern now has the value 0xba59.

Another example: On our 68000 system, ∼0xf has the value 0xfffffff0, because in our 68000 system, constant values such as 0xf are 32-bit integers (See section 2.1 on page 6), while with ms-dos systems ∼0xf has the value 0xfff0, since an integer has a size of 16 bits in ms-dos systems.

The C bitwise exclusive-OR operator,

The bitwise exclusive-OR operator has the symbol . We usually use it for toggling individual bits. The word ‘toggle’ means we change a ‘1’ bit to a ‘0’ and a ‘0’ to a ‘1’. Another way to say this is that we can selectively complement individual bits. That is different from the complement operator, ‘∼’, which complements all the bits.

Let’s see an example. We have a number where we want to toggle the three bits numbers 4, 5 and 6 in the second nibble but leave all the other bits as they were:

int pattern = 0x45a6;

If we exclusive-OR this bit pattern with the pattern 0x0070, we will toggle the bits 4, 5 and 6 but leave all the other bits as they were:

pattern = pattern 0x0070;

This applies the exclusive-OR operation between corresponding bits. The symbol ‘⊕’ is the mathematical symbol for the exclusive-OR operator.

0100 0101 1010 0110

0000 0000 0111 0000 ⊕

0100 0101 1101 0110

The result is that pattern now has the value 0x45d6.

Program 2.10 on the next page shows the use of the bitwise exclusive-OR operator.

Program example 2.10 This program shows the use of the bitwise exclusive-OR operator, ‘ ’.

/∗ The use of the bitwise exclusive-OR operator ∗/

#include <stdio.h>

int main( void )

{ int i = 0xf; int j = 0x12; int k;

10

k = i j;

        printf ( "The bitwise exclusive-OR of 0x%x and 0x%x is: \n", i, j );

printf ( "0x%x\n", k );

return 0; }

The terminal output is:

The bitwise exclusive-OR of 0xf and 0x12 is:
0x1d

The operation is shown below. The symbol ‘⊕’ is the mathematical symbol for the exclusive-OR operator.

\begin{matrix}0000 1111 \\ 0001 0010 & ∨ \\ 0001 1101 \end{matrix}

The shift operators, << and >>

C provides two shift operators, the left shift: << and the right shift: >>. These are useful for accessing individual bits of a bit pattern. They shift the bits of the left operand some number of positions to the left or right.

unsigned char bits = 1; /∗ 0000 0001 ∗/ bits = bits << 1; /∗ 0000 0010 ∗/ bits = bits << 2; /∗ 0000 1000 ∗/ bits = bits >> 3; /∗ 0000 0001 ∗/

2.8 Statements

A statement is the smallest unit in a program that can be executed. Each statements is terminated with a semicolon. A statement may consist of one or more expressions. Program example 2.11 gives examples of six simple statements. Note that the if statement and while statement occupy two program lines each. The other four statements occupy one program line each. The functions getch() and kbhit() are library functions discussed in section D.7.4 on page 87 and section D.7.3 on page 87. You have used the library function DelayMs() before; it is described in section D.7.2 on page 86.

Program example 2.11 examples of valid statements in C.

++i;

DelayMs( 300 );

j = i + k | m + getch();

if ( kbhit() )

c = getch(); while( ! kbhit() )

;

     printf ( "A key was pressed on the PC.\n" );

2.8.1 The null statement

The simplest statement is the empty or null statement:

; /∗ a null statement ∗/

A null statement is sometimes used with a for or while loop. See program example D.1 on page 86 for an example using a null statement in the DelayMs() function.

2.8.2 Compound statements

A compound statement is used often used for the body of a if statement or while or for loops when you need more than a single statement to be executed. A compound statement is a sequence of single statements surrounded by braces ‘{’ and ‘}’. The body of a loop is a single statement or compound statement that can be repeated.

for ( i = 0; i < 16; ++i )

{ WriteSevenSegment( i, false ); DelayMs( 200 );

}

It is not necessary to terminate a compound statement with a semicolon. Note that WriteSevenSegment() and DelayMs() are library functions, described in sections D.7.5 on page 88 and D.7.2 on page 86.

You may declare local variables at the beginning of any compound statement. Such a compound statement is called a block. These variables will be only available inside that block.

2.8.3 if statement

The syntax of the if statement is:

if ( expression ) statement 1;

else statement 2;

If the expression evaluates to a nonzero value, the condition is considered to be true and statement 1 is executed; otherwise, statement 2 is executed. Note that if statement 1 is not a compound statement, it must be terminated with a semicolon. This is different from Pascal, where a semicolon there is illegal. The C compiler will report a syntax error if you leave out a semicolon there.

The else part is optional:

if ( expression ) statement 1;

2.8 Statements  33

A statement may also be another if statement:

if ( expr1 )

statement 1; else if ( expr2 )

statement 2; else if ( expr3 )

statement 3; /∗ more if … else if statements … ∗/ else

statement n;

I recommend using this indentation style for such nested if statements.

Note that the if statement is not a loop!

Program example 2.12 shows the use of if and the modulus operator, %.

Program example 2.12 A program that uses an if statement and the modulus operator, ‘%’.

/∗ Uses an if statement and the modulus operator ∗/ /∗ This is a program to test if a number is odd or even ∗/

#include <stdio.h>

int main( void ) {

int i, temp;

   printf ( "Input a number using the key pad \n" );  10

i = GetNum(); /∗ The function get a number from the key pad ∗/ temp = i % 2; /∗ Find the remainder ∗/ if ( temp == 1)

      printf ( "The number is odd \n" );

else

      printf ( "The number is even \n" );

return 0; }

2.8.4 The while statement

The syntax of the while statement is:

while ( expression ) statement;

The while loop works like this:

1. Evaluate the expression.

2. If the condition is true, execute the statement.

3. Go back to the first step.

Program example 2.13 on the facing page uses a while loop:

2.8.5 The for statement

A for loop is often used for processing fixed length data structures like arrays. The C^{++} for loop is more general than the Pascal for loop. The syntax has the form:

for ( init expression; expression 1; expression 2 )

statement;

Any of init expression, expression 1 or expression 2 can be empty. If all three are empty, we get for(;;), which is an infinite loop. You could pronounce this “forever”.

• init expression is an expression. It is usually used to assign to a set of variables. It can be empty, i.e., null. It is executed just once before the loop body is executed. Here are some legal instances of init expression:

for ( i = 0; …

for ( ; /∗ null init expression ∗/ …

for ( lo = 0, hi = max, mid = max / 2; /∗ three initialisations ∗/ …

• expression 1 is the loop condition. The statement (the loop body) is executed each time after expression 1 evaluates to true. If expression 1 is empty (null), then it is considered to have the value true. Here are some legal instances of expression 1:

for ( …; index < arraySize; …)

for ( …; ptr; …)

for ( …; ch = GetNextChar(); …) for ( …; ; /∗ null expression 1 ∗/ …)

2.8 Statements  35

Program example 2.13 A program using while.

/∗ A program which calculates ∑_{i=1}^{k} i for k = 1, …, 5 using a while loop. ∗/

#include <stdio.h>

int main( void ) {

int sum, i;

i = 1;

sum = 0;  10

printf ( "  i     sum from 1 to i\n" );
printf ( " ---    ---------------\n" );

while ( i <= 5 ) {

sum = sum + i;

printf ( " %d %d\n", i, sum ); ++i;

} return 0;

}  20

The output in the terminal window is:

i     sum from 1 to i
---    ---------------
1      1
2      3
3      6
4      10
5      15

• expression 2 is evaluated after each iteration of the loop. It is usually used to modify the variables initialised by init expression. Here are some legal instances of expression 2:

for ( …; …; ++i )

for ( …; …; ++i, −−j, ++cnt ) for ( …; …; ) /∗ null expression 2 ∗/

Except for the case of an empty expression 1, and the use of the continue statement (see section 2.8.9 on page 39), shown soon in this lecture, the for statement above is equivalent to the following while loop:

init expression;

while ( expression 1 )

{ statement; expression 2;

}

A for loop is useful when a loop counter is used. Otherwise, a while loop may be a simpler choice. Really, both types of loop are rather similar, and often it doesn’t matter which you use.

Program 2.14 on the facing page uses a for loop and is equivalent to program 2.13 on the page before.

▶ Exercise 2.7 Write a program that uses a loop to print the square of all the numbers from 10 to 30. The program will use printf ().

▶ Exercise 2.8 Write a for loop that is equivalent to the following while loop:

i = 10;

while ( i < 20 )

{ SEVEN SEGMENT = pattern[ SWITCHES & 0xf ]; DelayMs( 200 ); i = i + 1;

}

Note that this loop uses names defined in flight.h, so to test the program you will need to compile it in the laboratory.

Program example 2.14 A program equivalent to 2.13 on page 35, except that it uses a for loop.

#include <stdio.h>

/∗ Demonstration of the for loop ∗/

int main( void )

{ int sum, i;

sum = 0;

        printf ( "  i     sum from 1 to i\n" );  10

        printf ( " ---    ---------------\n" );

for ( i = 1 ; i <= 5; ++i )

{ sum = sum + i; printf ( " %d %d\n", i, sum );

}

return 0; }

The output in the terminal window is the same as the output from the program demonstrating the while loop in section 2.8.4 on page 35.

2.8.6 The do statement

The do loop is used in situations like the Pascal repeat … until loop where the body of the loop needs to be executed at least once, perhaps to set up the requirements for the loop control. The syntax of the do loop is:

do {

statement; } while ( expression );

statement is executed before expression is evaluated. If the condition evaluates to false, the do loop terminates.

Program 2.15 on the following page is equivalent to program 2.13 on page 35 and 2.14, but it uses a do loop.

▶ Exercise 2.9 Write a C program that is equivalent to that given in exer-Program example 2.15 This program uses a do loop, and produces the same output as the while and for loop programs, 2.13 on page 35 and 2.14 on the preceding page.

#include <stdio.h>

int main( void )

{ int sum, i;

i = 0; sum = 0;

        printf ( "  i     sum from 1 to i\n" );

        printf ( " ---    ---------------\n" );  10

do {

sum = sum + i;

printf ( " %d %d\n", i, sum ); ++i;

} while ( i <= 5 );

return 0; }

cise 2.12 on page 45, but which uses a do loop instead of a while loop.

2.8.7 Nested loops

Loops can be nested inside one another. Program example 2.16 on the facing page shows how.

Program 2.17 on page 40 is the same program but implemented using two nested while loops.

2.8.8 The break statement

A break statement terminates the smallest enclosing while, do, for or switch statement. Execution resumes at the statement immediately following the terminated statement. break is a very useful statement, missed by some Program example 2.16 A program using two nested for loops.

/∗ This program demonstrates nested loops ∗/

#include <stdio.h>

int main( void )

{ int i, j, data; for ( i = 1; i <= 5; ++i ) {

for ( j = 1; j <= 3; ++j )  10

{ data = i ∗ j; printf ( "%d * %d = %d ", i, j, data );

}

printf ( "\n" ); } return 0;

}

The result is:

1 * 1 = 1      1 * 2 = 2       1 * 3 = 3
2 * 1 = 2      2 * 2 = 4       2 * 3 = 6
3 * 1 = 3      3 * 2 = 6       3 * 3 = 9
4 * 1 = 4      4 * 2 = 8       4 * 3 = 12
5 * 1 = 5      5 * 2 = 10      5 * 3 = 15

Pascal programmers.

This example program shows how the break statement can be used.

2.8.9 The continue statement

A continue statement causes the current iteration of the nearest enclosing while, for or do loop to terminate.

In while and do loops, execution resumes with the evaluation of the control expression.

In for loops, continue causes execution to resume with the evaluation of Program example 2.17 A program showing nested while loops. The output in the terminal window is exactly the same as for program 2.16 on the preceding page.

#include <stdio.h>

int main( void )

{ int i, j, data; i = 1; while ( i <= 5 ) {

j = 1;

while ( j <= 3 )  10

{ data = i ∗ j; printf ( "%d * %d = %d ", i, j, data ); ++j;

}

printf ( "\n" );

++i; } return 0;

}  20

expression 2.

Unlike the break statement, which terminates the loop, the continue statement terminates only the current iteration.

In this example, the telephone keypad in the lab is read continuously. If not all row inputs are high (if not all of the bits 3, 4, 5 and 6 of port A are 1), then a key has been pressed and the keystroke is processed, otherwise the \underline{2.9 Arrays 41}

Program example 2.18 Using the break statement.

/∗ The break statement ∗/ /∗ Test the key pad ∗/

#include <stdio.h>

int main( void ) {

int i;

while ( true )

{  10

      printf ( "Press '2' on the key pad\n" );

i = GetNum(); if ( i == 2 ) break;

}

   printf ( "The key '2' key is ok." );

return 0; }

current loop iteration is terminated:

while ( ! keyPressed ) {

pattern = KEYPAD & 0x78;

if ( pattern == 0x78 )

continue; /∗ terminate this iteration, since no key has been pressed ∗/

/∗ … process this keystroke … ∗/ }

Program 2.19 on the next page shows the use of the continue statement:

2.9 Arrays

What is an array? An array is a table which contains a number of things of the same type, such as bit patterns. The “things” in the array are called elements of the array. Each element is accessed by a number Program example 2.19 A program demonstrating the continue statement.

/∗ This program calculates 1 + 2 + 3 + 4 + 6 + 7 + 8 + 9. Notice that the sum skips adding 5 because of the continue statement.

∗/

#include <stdio.h>

int main( void )

{

int i;  10

int sum = 0;

for ( i = 1 ; i < 10 ; ++i )

{

if ( i == 5 ) continue; sum = sum + i;

}

        printf ( "The sum is %d.", sum );

return 0;  20

}

The terminal screen will show:

The sum is 40.

called an index or subscript. The first element in the array has the index 0, the next has the index 1, and so on. To access an element, we put the index for that element in square brackets [ ] after the array name. An array is also sometimes called a lookup table.

An array has a particular length. We write the length when we declare the array.

As an example, here is a declaration for an array of 10 integers:

int table[ 10 ] = { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 };

\underline{2.9 Arrays 43}

This array has been initialised with the numbers 5, 10, 15, … 45, 50. The name of this array is table. Its length is 10. Notice that there is a semicolon ‘;’ after the closing brace. We can access each of these ten elements by indexing the array with a number in the range 0 to 9. For example, to find the value of the element 20, which has the index 3, we can find the value of table[ 3 ]. If N is the length of the array, then legal indexes are in the range 0 to N − 1. Note that the length (here 10) is out of range for legal indexes. The first element in this array is accessed as table[ 0 ]. We can assign this value to another variable, or copy it to one of the ports on the parallel chip.

/∗ Create a local integer variable called tableValue and initialise it with the value of the first element in table: ∗/ int tableValue = table[ 0 ]; /∗ tableValue now equals 5. ∗/

The index for the element 10 is 1, and we access it as table[ 1 ]. Similarly, we access elements 15, 20, 25, 30, 35, 40, 45 and 50 as table[ 2 ], table[ 3 ], table[ 4 ], table[ 5 ], table[ 6 ], table[ 7 ], table[ 8 ] and table[ 9 ], respectively.

▶ Exercise 2.10 Write a C program that:

• defines an array of ten integers

• initialises the array to the values 1. .10

• adds the contents of the array together in a loop

• prints the sum on the terminal

▶ Exercise 2.11 Write a program that

• Contains the definition of an array of 10 integers

• Initialises the first array element to 1, the second to 2, the third to 3, and so on, so that the last element has the value 10. Use a loop to do this.

• Prints out the contents of the array using a second loop and the printf () function.

2.9.1 Array indexing errors

The last element in this array is table[ 9 ]. It has the value 50.

If we index an array with an index that is out of range, then we get an array indexing error. The program may still run if it has such an error, but it will give you a number outside of the array if you try to read that value. The result will be unexpected.

It is very important that the index to an array be in the range for legal indexes, i.e., in the range 0 to N − 1, where N is the length of the array.

How can you make sure an index is in the legal range? If the length of the array is a power of two, such as sixteen, as the length of sevenSegPattern is in workshop 2, we can use the bitwise AND operator to clear the bits that make an index bigger than the length of the array.

Program 2.20 shows the use of an array as a lookup table. Note that if an array is initialised, it is not necessary fo write the length of the array in the definition of the array.

Program example 2.20 A program to use an array to look up patterns to display on the leds in the laboratory.

#include <flight.h>

int pattern[ ] = { 0x81, 0x42, 0x24, 0x18, 0x24, 0x42 }; int main( void ) {

int i;

while ( true ) for ( i = 0; i < 6; ++i ) {

LEDS = pattern[ i ];  10

DelayMs( 100 ); } return 0;

}

It causes the eight leds on the interface board in the laboratory to light up in this pattern:

First: • ∘ ∘ ∘ ∘ ∘ ∘ •
then: ∘ • ∘ ∘ ∘ ∘ • ∘
then: ∘ ∘ • ∘ ∘ • ∘ ∘
then: ∘ ∘ ∘ • • ∘ ∘ ∘
then: ∘ ∘ • ∘ ∘ • ∘ ∘
then: ∘ • ∘ ∘ ∘ ∘ • ∘

and so on, endlessly.

Program 2.21 on the next page is another example which uses a nested for loop to process an array. This is called a bubble sort; it is a simple method of sorting an array. To understand this algorithm properly, I suggest you read the beginning of chapter 23 of [Per94].

▶ Exercise 2.12 Write a C program that is equivalent to the following program, but which uses a for loop instead of a while loop.

#include <stdio.h> int main( void ) {

int i = 0;

while ( i < 10 )

{ printf ( "i = %d\n", i ); ++i;

}

return 0;  10
}

▶ Exercise 2.13 There are two array indexing errors in this program. Show where they are and change the program so that all of the elements of the array are accessed without an array indexing error.

#include <flight.h>

#define NUMPATTERNS 8

unsigned char patterns[ NUMPATTERNS ] = { 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff };

int main( void )

{ int i; for ( i = 1; i <= NUMPATTERNS; ++i )

SEVEN SEGMENT = patterns[ i ];  10

return 0; }

2.10 Functions

Pascal provides both functions and procedures. In C, all subroutines are called functions.

Program example 2.21 A program that uses a nested for loop to sort an array. (This is called a bubble sort).

/∗ An example of nested for loops using an array ∗/

#include <stdio.h> #define NUM ITEMS 8

int main( void ) {

int i, j, temp; int data[ NUM ITEMS ] = { 2, 95, 50, 64, 1032, 58, −345, 80 };

10

for ( i = 0; i < NUM ITEMS − 1; ++i ) {

for ( j = i; j < NUM ITEMS; ++j )

{ if ( data[ j ] > data [ i ] ) {

/∗ swap them: move bigger numbers towards beginning of array ∗/ temp = data [ j ]; data [ j ] = data [ i ];

data [ i ] = temp;  20

} }

}

printf ( "The sorted data is:\n" );

for ( i = 0; i < NUM ITEMS; ++i )

printf ( "%d\n", data[ i ] ); return 0;

}

The terminal will show:

The sorted data is:
1032
95
80
64
58
50
2
-345

A function is a group of program statements that contain a single idea or algorithm, and that has a name. The function may be called from anywhere in the program, and when it has finished, it returns control to the place immediately after where it was called.

Functions are used mainly:

• to organise the program into smaller units that each do a simple, welldefined job. This makes a complicated program easier to write and understand.

• to reduce the size of a program since they can be called repeatedly from many different places in the program.

The format of a function definition is:

return type FunctionName( parameter list )

{ local variable definitions;

zero or more statements; }

A function often returns a value, such as the function min() below, which has the value of the smaller of its parameters.

The parameters (or arguments) passed to a function where it is called are the actual parameters. The parameters when referred to from inside the function are the formal parameters. In the following example, the formal parameters are a and b. The actual parameters are i and j. The formal parameters are simply local copies of the actual parameters. The example program 2.22 on the following page shows parameters passed to a min() function. A function prototype is the declaration of a function, consisting of the return type, name and parameter list. You may put the same function prototype as many times as you like in a file. The function prototype for the min() function is

int min( int a, int b );

The best arrangement is to put all function prototypes in header files, and to include them with #include "..." whenever the definitions of those functions are used. Section D.7 on page 85 shows some library functions together with their prototypes. Look there for more examples of function prototypes.

Program example 2.22 A program that shows parameters begin passed to a function.

int min( int a, int b )

{

if ( a < b ) return a;

else

return b; }

int main( void )

{  10

int i = 1, j = 2;

int k = min( i, j ); /∗ k = 1. ∗/

return 0; }

Maintenance of a program with many declarations for each function is more difficult. There should be only one declaration to avoid such problems.

As in Pascal, C functions can be called recursively. This means that a function can call itself.

▶ Exercise 2.14 Write a function Leds() in C that will simulate the eight leds you used in the laboratory. Your function will take one parameter so that if a bit in the parameter is zero, the character in that position will be a dot ‘.’, and if that bit is a one, the character in that position will be a ‘o’. Each time your function is called, it will display eight of the characters, either ‘.’ or ‘o’ on one line. The next time it is called, it will display eight characters on the next line. Following is a program that is complete except that it is missing the body of the fucntion Leds(). The output of the program follows.

#include <stdio.h>

#define NUMPATTERNS 6

void Leds( unsigned char pattern )

{

/∗ The body of Leds() is missing here: your exercise is to write it. ∗/ } unsigned char pattern[ NUMPATTERNS ] = { 0x81, 0x42, 0x24, 0x18, 0x24, 0x42 };

int main( void )

{ int i, j; for ( j = 0; j < 3; ++j )

for ( i = 0; i < NUMPATTERNS; ++i ) Leds( pattern[ i ] );

return 0; }

The output on the terminal should look like this:

o......o
.o....o.
..o..o..
...oo...
..o..o..
.o....o.
o......o
.o....o.
..o..o..
...oo...
..o..o..
.o....o.
o......o
.o....o.
..o..o..
...oo...
..o..o..
.o....o.

▶ Exercise 2.15 Write a function SevenSegment()in C that will simulate the seven segment display you use in the laboratory. The function shall take one parameter and shall display a figure on the terminal using the ascii characters ‘ ’, ‘|’, the space character, the newline character ‘\n’ and the ‘.’ character to represent the decimal point. One seven-segment figure will be displayed per line, so that the following program (which is complete except that it is missing the body of the function SevenSegment()) will produce the output that is shown after:

#include <stdio.h>

void SevenSegment( unsigned char pattern )

{ /∗ The body of SevenSegment() is missing here: your exercise is to write it. ∗/

} unsigned char sevSegPattern[ ] = {

0x3f, 6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 7, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71

};

int main( void )

{ int i; unsigned char pattern; for ( i = 0; i < 16; ++i ) {

pattern = sevSegPattern[ i ]; SevenSegment( pattern );

}

10

/∗ Now show with the decimal point on: ∗/ for ( i = 0; i < 16; ++i ) {

pattern = sevSegPattern[ i ] | 0x80; SevenSegment( pattern );

}

return 0; }

The output on the terminal should look something like that shown in figure 2.15 on the preceding page, although I have used four columns here to save paper, while your program should display each digit on the left side of the terminal window. Note that you will want to use the bitwise AND operator ‘&’ here: the idea is that your function should be able to behave as much like a physical seven segment display as possible.

2.10.1 Function return values

In Pascal, a function returns a value when you assign a value to the name of the function inside the body of the function. In C, you use the keyword return . C functions behave in much the same way as they do in Pascal.

A function can return a single item of almost any type.

A function of return type void is just like a Pascal procedure; it does not return any value.

A function with no return type specified is assumed to return int. Please do not rely on this, since it sometimes means that the programmer forgot

_ | | |_| | | _ _| |_ _ _| _| |_| | _ |_ _| _ |_ |_| _ | |                    _ |_| |_| _ |_| _| _ |_| | | |_ |_| _ | |_ _| |_| _ |_ |_ _ |_ |       _ | | |_|. | |. _ _| |_. _ _| _|. |_| |. _ |_ _|. _ |_ |_|. _ | |.                   _ |_| |_|. _ |_| _|. _ |_| | |. |_ |_|. _ | |_. _| |_|. _ |_ |_. _ |_ |  .

Figure 2.2: The output required for the program in exercise 2.15 on page 49.

to write in the return type rather than that int was intended.

A function of return type void does not need a return statement. All other functions must return a value. They must have at least one return statement followed by an expression that can be converted to the return type, like min() above. When the return statement is executed, no more statements are executed in the body of the function, even if there are more statements after the return statement. Program example 2.23 on the next page shows how return terminates a function.

The return type of main()

The main() function is special, in that the return type is required by the ansi standard to be of type int. The purpose of the return value of main() is to indicate whether an error occurred or if the program was successful. This is used either by the operating system if there is one, or in the case of the flight-68k boards to indicate the terminating condition of the program through the terminal program. If the program is successful, the convention is that it returns zero. If there is an error, then main() will return a non-zero error number.

Program example 2.23 A program that calls a Silly() function that shows how return terminates execution of a function.

#include <stdio.h>

int Silly( void )

{ int i; return 6; /∗ Nothing after the return statement will be executed. ∗/ for ( i = 0; i < 20; ++i )

           printf ( "This will never be executed.\n" );

}  10

int main( void )

{ int j = Silly();

        printf ( "the return value of Silly() is %d\n", j );

return 0; }

The output on the terminal will be:

the return value of Silly() is 6

2.10.2 The function parameter list

The parameter list is often called the signature of the function, because the parameter list is often used to tell one function from another.

When we call a function, it must always have a parameter list, even if that list is empty, unlike Pascal. When we define a function where the parameter list contains the single keyword ‘void’, it means that function takes no parameters. Every program must contain a main() function. In our course, we put void in main()’s parameter list to show that main() has an empty parameter list.

Other functions take parameters. Parameters are like the inputs to the function. The return value of the function is its output.

When we define a function, we put the type before each parameter and then a name for each corresponding parameter. This applies to all parameters in the parameter list when we define the function. The name of each parameter is the name of the formal parameter. The name formal parameter is just a term that indicates the name of the parameter when we are defining the function.

When we call a function, we do not write the type: we just put the name of the parameter. As mentioned previously, the name of each parameter when we call the function is the name of each actual parameter. Note that the name actual parameter is just a term that means the name of the parameter when we call the function. Note too that we can just use a constant value as the actual parameter, as in this call to the library function DelayMS():

DelayMs( 200 ); /∗ delay 200 milliseconds. ∗/

When we call a function, the value of the actual parameter is copied into the formal parameter. Formal parameters stop existing when the current execution of the function terminates. Execution of a function terminates either when a return statement is executed in the body of that function, or when execution reaches the last closing brace ‘}’ of the body of the function.

▶ Exercise 2.16 The keyword void appears twice in this function Delay(). What does it mean in each case?

void Delay( void )

{ int i;

for ( i = 0; i < 100000; ++i )

; /∗ Do nothing; just increment the counter. ∗/ }

▶ Exercise 2.17 The formula to convert Fahrenheit temperatures to Centigrade is C = 5×(F −32)÷9. Write a function to convert Fahrenheit degrees to Centigrade. The function will take one integer parameter and will return a value of type integer. Write a complete program that calls this function.

▶ Exercise 2.18 Write a function Max() that takes two integer numbers as parameters and returns the larger of the two. The function will return a value of type int. Write a complete program that calls this function.

▶ Exercise 2.19 This exercise is similar to exercise 2.7 on page 36, but it requires that you write a function.

Write a function Square() that takes one integer parameter n and returns one integer parameter which is the square of the parameter, i.e., n^{2}.

Write a program that uses this function Square() in a loop to print the square of all the numbers from 10 to 30. The program will use printf ().

▶ Exercise 2.20 Write a function Power() that takes two integer numbers and raises the first number to the power of the second. The function can return an int result. Write a complete program that calls this function. Execute your program using Quincy or a C compiler.

2.11 Indentation of your programs

Notice that all the program examples given to you in lectures and labs are systematically indented and formatted. This makes a program much easier for a human to read. For instance, putting opening and closing braces ‘{’ and ‘}’ above each other makes it much easier to see if one is missing. Indenting the body of a loop or the body of an if statement makes it much easier to see the flow of the program. There are documents that describe styles of writing programs: one such guide is [CEK^{+}] which is available electronically. A copy is kept in the laboratory.

I strongly urge you in your lab work to choose an indentation system and stay with it. A good text editor (such as Emacs or Brief), automatically indents your program as you write it.

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 SEVEN SEGMENT 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 programs, to do output, we put the name of the output port on the left side of an assignment statement, e.g., LEDS = 0xc8;

• To do input in our programs, we put the name of the input port anywhere except on the left side of an assignment statement, e.g., j = 47 ∗ SWITCHES; • The printf () statement causes output; we see a message in the terminal

window of the pc. It gets there by serial transfer.^{1} The serial transfer takes place through the 68681 duart. The printf () library function writes to an output port in the duart.

• In our assembly language programs, to do output, we move the data to the port, i.e., the port is the destination, e.g., if the A6 register contains the base address of the 68230 parallel chip:

       move.b  #$c8,leds(a6)

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

       add.b  switches(a6),d0

3.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 3.2 on page 63. 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 C.1 on page 80.

1This is not true for all microprocessor systems, since some computers are connected more directly to a video system, such as our pcs.

ver 1.7, 27 February 1996

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

LEDS = 0xf; /∗ Output: turn on the four leds on the right. ∗/

pattern = SWITCHES; /∗ Input from the dip switches into a variable pattern. ∗/ KEYPAD = 0; /∗ Output zero to the keypad on Port A. ∗/

Similar operations can be done in assembly language:

        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 3.1 on page 61 and 3.2 on page 63.

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 3.1 on page 61 and 3.2 on page 63.

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, SEVEN SEGMENT, SWITCHES and KEYPAD are named constants, defined in the file flight.h. See section 2.3 on page 10 for more about the file flight.h.

3.1.1 The interface card

On the 68000 microprocessor i^{/}o interface card^{2} 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. The connections of the devices on the interface board are shown in figure 3.1 on the following page.

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

3.2 The ���������� microprocessor board

[figure: V_{CC}; 68230 pi^{/}t; 68000 μP; D_{7-0}; A_{5-1}; r^{/}\overline{w}; \overline{dtack}; \overline{reset}; {addr. \atop dec.}; \overline{cs}; A_{23}; A_{22}; A_{21}; \overline{as}; a; b; c; d; e; f; g; DP; a; b; c; d; e; f; g; dp; lsb; msb; PBDR; PCDR; JP1; \overline{g}; \overline{g}; 244 octal buffer; 244 octal buffer]

Figure 3.1: The connections between the 68000 and the parallel chip on the flight-68k microprocessor board, and the connections to port B and port C on the interface board.

Figure 3.1 shows how the leds and the seven segment display are connected to port B, and how the dip-switches are connected to port C. It also shows how the microprocessor is connected to the parallel chip. The jumper, JP1, (also shown in figure 3.3 on page 68) selects one of the seven-segment display or the leds to port B.

The microprocessor is connected to 16 KB of ram, 32 KB of eprom—which contains the monitor firmware—and the 68681 duart (serial chip) as well as the 68230 pi^{/}t (parallel chip). See [Urb96] for more details about the 68000 microprocessor. See also your text book, [Hor92]. See the flight-68k manual [Fli88] for more about the flight-68k board itself.

3.2.1 Octal tristate buffers on the interface board

The 244 octal buffers have their outputs the same logical value as the input when the gate \overline{g} is active, but when the gate is inactive, the outputs are open, or high impedance. We sometimes say the outputs are “high Z”. The jumper, JP1, either activates the gate of the buffers for the leds or the gate of the buffers for the seven segment display. In this way, either the leds or the seven-segment display is connected to port B of the pi^{/}t.

3.2.2 Address decoder for the 68230 ��^{�}�

Notice the address decoder for the 68230 pi^{/}t. As inputs it takes only three address lines, A_{23–21}, and the address strobe control line, \overline{as}, which is the way the microprocessor lets the rest of the computer know that the address on the address lines is valid. The address decoder is connected to the chip select input to the pi^{/}t. This means that when the microprocessor accesses any of the addresses in table 3.2 on page 63, the address decoder will be activated and the pi^{/}t will “wake up” and listen to the microprocessor’s commands.

Partial address decoding

Notice that there are only five address lines, A_{5–1}, connected directly to the pi^{/}t. This means that 15 address lines, A_{20–6}, are not connected either to the address decoder or to the pi^{/}t. These address lines are “don’t cares” to the pi^{/}t. It means that the pi^{/}t is activated only depending on the address lines A_{20–6}, regardless of the values of address lines A_{20–6}. This is partial address decoding. It means that each of the registers in the pi^{/}t can be accessed not only at the addresses shown in table 3.2 on page 63, but also at 2^{15} = 32767 other addresses! The memory map in figure C.1 on page 80 is not strictly accurate. It shows the pi^{/}t registers mapped to addresses 800000_{16} to 80003F_{16}, only 64 bytes. In fact, the pi^{/}t is mapped to locations 800000_{16} to 9FFFFF_{16}, occupying two megabytes of address space!

Why use partial address decoding: it seems to waste a lot of address space! The reason it is used is because it makes the address decoder much simpler, and so much cheaper. Full address decoding would require a circuit with 19 inputs; here we need a simple circuit circuit that only has four inputs.

The full circuit diagram of the flight-68k board is shown in appendix G of [Fli88].

▶ Exercise 3.1 If there are three address lines, A_{23–21}, connected to the address decoder for the 68681 duart (serial chip), and four address lines A_{4–1} connected directly to the duart, and the lowest address to which the registers in the duart are mapped is A00000_{16}, then, as a result of partial address decoding,

(a) What is the highest address at which the duart registers are mapped? In other words, what is the highest address at which the chip select of the duart will be active?

(b) How many addresses can each register in the duart be accessed at?

3.2.3 The other connections between the 68000 and the ��^{�}�

There are eight data lines connecting the 68000 to the pi^{/}t, so only eight bits can be transferred at once. The r^{/}\overline{w} line is high if the microprocessor wants to read from the pi^{/}t, and low if the 68000 wants to write to registers in the pi^{/}t. The \overline{reset} input resets the pi^{/}t when the power is turned on or when the RESET button is pressed on the flight-68k. The \overline{dtack} output from the pi^{/}t tells the microprocessor when the pi^{/}t has finished data transfer, either into or out from a pi^{/}t register.

3.2.4 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_{16}. The base address is determined by the design of the address decoder for the pi^{/}t. The address decoder is shown as a box in figure 3.1 on page 58. The port A data register has offset 10_{16} from the base address of the pi^{/}t, the port B data register has offset 12_{16} from the base address, while port C has offset 18_{16} from the base address. This means that port A is at address 800 001_{16} + 10_{16} = 800 011_{16}, while the port B data register is at address 800 001_{16} + 12_{16} = 800 013_{16}, while the port C data register is at address 800 001_{16} +18_{16} = 800 019_{16}. See section 3.1.4 on page 15 of [Urb96] for how these constants are defined. See table 3.1 for a list of the port addresses.

register  address offset name
Port A data register 800011_{16} 10_{16}  KEYPAD
Port C data register 800019_{16} 18_{16}  SWITCHES
Port B data register 800013_{16} 12_{16}  SEVEN SEGMENT or LEDS

Table 3.1: The port addresses in the 68230 pi^{/}t and their names we have used in the laboratory.

The seven segment display

The seven segment display on the interface board is connected as shown in figure 3.1 on page 58. The least significant bit (lsb) of port B controls segment a. When bit 0 of port B is set, and the jumper JP1 selects the seven-segment display, then segment a will light up. Figure 3.2 on the next page shows which bit of port B controls which segment. We can access the seven-segment display using the name SEVEN SEGMENT or PBDR, both defined in the header file flight.h.

a b c d e f g  DP

The names for each segment in the seven segment display

bit number  7 6 5 4 3 2 1 0
segment label DP g f e d c b a

Figure 3.2: The bits for each segment in the seven segment display. Note that DP stands for “decimal point”.

3.2.5 The dip switches

On the interface board is a bank of eight small switches, which can be moved with a pen or your fingernail. They are connected to port C, as shown in figure 3.1 on page 58. We can access them by the name SWITCHES or by the name PCDR. These names are defined in the header file flight.h. We can perform input from them into a variable val by a C statement:

val = SWITCHES; /∗ An input operation. ∗/

3.2.6 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 3.2 on the next 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

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  KEYPAD
Port B data register  PBDR  800013  12  SEVEN SEGMENT

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 3.2: The addresses of registers in the 68230 pi^{/}t on the flight-68k. All addresses and offsets are in hexadecimal. 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 .^{3} 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:

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 3.2.7 on page 66 we will examine the function of the data direction registers.

3.2.7 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 3.3 on the next page) showing what we want to use in our lab work.

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 3.3 on the facing 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

3The 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.7, 27 February 1996

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 3.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.

SUBMODE 1X contains the correct value, we can initialise port A with the instruction

PACR = SUBMODE 1X ;

Or if the name submode 1X has a suitable definition, and the register A6 holds the base address of the parallel chip, we can achieve the same result with:

  move.b   #submode_1X,PACR(a6)

▶ Exercise 3.2 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 3.3 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 3.4 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 3.3 on the page before 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.

As mentioned in section D.6.1 on page 84, the C compiler automatically calls a function that initialises the 68230 parallel chip before executing the main() function. See program example 3.1 on the next page for this function, just to show that there is no magic here. The file containing this is f:\hpcc68k\ env\flight\src\flight.c. You may examine this and the other files in that directory if you like.

Program example 3.1 The function InitParallelChip(), which is called by the startup code which is executed before main() begins. The purpose of InitParallelChip() is to initialise the pi^{/}t appropriately for the interface board with the leds, seven-segment display, dip-switches and 3 × 4 telephone keypad.

#include <flight.h>

/∗ Note: the file flight.h defines the names for the pi^{/}t registers as mentioned above, and also the following: #define SUBMODE 1X 0x80 #define ALL OUTPUTS 0xff #define ALL INPUTS 0 #define COL MASK 7

∗/

void InitParallelChip( void )

{ PACR = SUBMODE 1X ; PBCR = SUBMODE 1X ; /∗ the columns are bits 0, 1 and 2 of port A; want these to be

outputs. The other bits are inputs. ∗/ PADDR = COL MASK; PBDDR = ALL OUTPUTS;

PCDDR = ALL INPUTS;  10

/∗ Turn off the seven segment display (or leds). ∗/

SEVEN SEGMENT = 0; }

3.2.8 How the interface card is connected to the parallel chip

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 3.3 on the following page. When the jumper is in the upper position across pins 1 and 2, the leds are selected.

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

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

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”.

3.3 The keypad

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

The keypad is connected rather like figure 3.4 on the next page. 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.

3.3.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

[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 3.4: The connection of the keypad to the 68230 parallel interface/ timer chip

n + m port pins to scan a n × m keypad^{4}.

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 3.4). 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.

4The 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.

• 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.

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.

3.3.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 DelayMs() library function described in section D.7.2 on page 86 to create a delay of 20 milliseconds:

DelayMs( 20 ); /∗ Delay 20 milliseconds. ∗/

3.3.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:

KEYPAD = 0; /∗ make all keypad columns low. ∗/

or in assembly language, we can write

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

Remember, to write to a port register—in other words, to perform output— we put the name of the port on the left side of an assignment statement. In assembly language, we put the port 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:

pattern = KEYPAD; /∗ The variable pattern now holds the column bits ∗/

If A6 holds the base address of the parallel chip, we could write this in assembly language as

        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 bitwise AND operator ‘&’ in the C programming language (or the and assembly language instruction) to clear these bits.

We can write

pattern = pattern & 0x78; /∗ clear all bits that are not row bits. ∗/

or in assembly language,

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

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

▶ Exercise 3.5 What we can say about the number of keys pressed if the value read from the rows is equal to 0x78?

Appendix A

The operators in C and their precedence

Table A.1 on page 76 shows all the operators available in the C programming language. The table also shows the precedence of each operator, together with its associativity.

A.1 Operator precedence

We learned in school that multiply has a higher priority in arithmetic than plus or minus, so that when we write 3×4+7, we mean (3×4)+7 (which has the value 19), not 3 × (4 + 7) (which has the value 33). This is an accepted rule used in arithmetic.

Similarly, the C programming language has priorities for each operator. These are called the rules of precedence of the operators. The rules of precedence are part of the ansi standard, so you can depend on them remaining as shown in table A.1. If you are unsure of the precedence of an operator, it is better to use lots of parentheses in complex expressions (or better still, to break complex expressions down into many simple expressions).

The level of precedence of each operator is shown in the left-most column of table A.1. The highest precedence is 15; the lowest is 1. Each precedence is shown together with a letter ‘L’ or ‘R’. This is the associativity of the operator. Those with the letter ‘L’ are said to be left associative, or that they ‘associate from left to right’. Those with an ‘R’ are said to be right associative, or to ‘associate from right to left’.

▶ Exercise A.1 (a) Put parentheses around the following expressions to show the exact order of evaluation of the expression. If possible, write the value of the expressions.

(b) 3 + 4 ∗ 7 − 2
(c) 2 & 3 | 4 + 7
(d) 1 << 4 + 3
(e) 0x45 | 1 << 3
(f) 4 + 2 << 3 ∗ 2 − 2 | 4 | 3
(g) ++a[ 4 ]
(h) 3 < 4 + 7 − 1 == 7 + 3 ∗ 4

(i) a %= 4 + 5 * 7 % 8

Level  Operator  Function
15L  −>, .  structure member selectors
[ ]  array index
()  function call
14R  sizeof  size in bytes
++, −−  increment, decrement
∼  bitwise NOT
!  logical NOT
+, −  unary plus, minus
∗, &  dereference, address-of
()  type conversion (cast)
13L  ∗, /, %  multiply, divide, modulus
12L  +, −  arithmetic operators
11L  <<, >>  bitwise shift
10L  <,<=, >,>=  relational operators
9L  ==, !=  equality, inequality
8L  &  bitwise AND
7L  bitwise exclusive-OR
6L  |  bitwise OR
5L  &&  logical AND
4L  | |  logical OR
3L  ?:  arithmetic if
2R  =  assignment operator
∗=, /=, %=  compound assignment operators
+=, −=, <<=
>>=, &=, |=, =
1L  ,  comma operator

Table A.1: A list of all operators provided in the C programming language, showing level of precedence.

Appendix B

The ����� code for representing characters

Computers represent text as well as numbers. How can a computer use binary numbers to represent the letters of the alphabet, punctuation and all the other symbols we use in a text file?

There are a number of different systems. They each use a binary number to represent a character. Some large ibm mainframe computers use a system called the ebcdic code. However, all microprocessor systems use the ����� code. ascii stands for American Standard Code for Information Interchange. Each character is represented by a seven-bit number. This allows a total of 128 characters. Table B.1 on the next page shows the ascii code. Since 128 characters is not really enough, most computers have extensions to the ascii code, but these are not standard, and they may vary from one manufacturer to another.

The first 32 ascii characters are control codes; these are used for such things as making the computer beep (BEL), for serial communications (DC1 and DC3), the backspace (BS), for starting a new line (LF and CR), a form feed for starting a new page (FF) and so on. They can often be entered from a keyboard as ⟨Ctrl-a⟩ for SOH, ⟨Ctrl-b⟩ for STX, ⟨Ctrl-c⟩ for ETX, ⟨Ctrl-d⟩ for EOT, and so on up to ⟨Ctrl-z⟩ for SUB.

Notice that we can convert an upper case letter to a lower case letter by adding 32_{10}.

b7 b6 b5 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1

BITS_{b4 b3 b2 b1}

\overline{CONTROL} SYMBOLS NUMBERS

UPPER CASE LOWER CASE

0 0 0 0 NUL_{0  0}^{0} DLE_{10  20}^{16} SP_{20  40}^{32} 0_{30  60}^{48} @_{40  100}^{64} P_{50  120}^{80} ‘_{60  140}^{96} p_{70  160}^{112}
0 0 0 1 SOH_{1  1}^{1} DC1_{11  21}^{17} !_{21  41}^{33} 1_{31  61}^{49} A_{41  101}^{65} Q_{51  121}^{81} a_{61  141}^{97} q_{71  161}^{113}
0 0 1 0 STX_{2  2}^{2} DC2_{12  22}^{18} ”_{22  42}^{34} 2_{32  62}^{50} B_{42  102}^{66} R_{52  122}^{82} b_{62  142}^{98} r_{72  162}^{114}
0 0 1 1 ETX_{3  3}^{3} DC3_{13  23}^{19} #_{23  43}^{35} 3_{33  63}^{51} C_{43  103}^{67} S_{53  123}^{83} c_{63  143}^{99} s_{73  163}^{115}
0 1 0 0 EOT_{4  4}^{4} DC4_{14  24}^{20} $_{24  44}^{36} 4_{34  64}^{52} D_{44  104}^{68} T_{54  124}^{84} d_{64  144}^{100} t_{74  164}^{116}
0 1 0 1 ENQ_{5  5}^{5} NAK_{15  25}^{21} %_{25  45}^{37} 5_{35  65}^{53} E_{45  105}^{69} U_{55  125}^{85} e_{65  145}^{101} u_{75  165}^{117}
0 1 1 0 ACK_{6  6}^{6} SYN_{16  26}^{22} &_{26  46}^{38} 6_{36  66}^{54} F_{46  106}^{70} V_{56  126}^{86} f_{66  146}^{102} v_{76  166}^{118}
0 1 1 1 BEL_{7  7}^{7} ETB_{17  27}^{23} ’_{27  47}^{39} 7_{37  67}^{55} G_{47  107}^{71} W_{57  127}^{87} g_{67  147}^{103} w_{77  167}^{119}
1 0 0 0 BS_{8  10}^{8} CAN_{18  30}^{24} (_{28  50}^{40} 8_{38  70}^{56} H_{48  110}^{72} X_{58  130}^{88} h_{68  150}^{104} x_{78  170}^{120}
1 0 0 1 HT_{9  11}^{9} EM_{19  31}^{25} )_{29  51}^{41} 9_{39  71}^{57} I_{49  111}^{73} Y_{59  131}^{89} i_{69  151}^{105} y_{79  171}^{121}
1 0 1 0 LF_{A  12}^{10} SUB_{1A  32}^{26} *_{2A  52}^{42} :_{3A  72}^{58} J_{4A  112}^{74} Z_{5A  132}^{90} j_{6A  152}^{106} z_{7A  172}^{122}
1 0 1 1 VT_{B  13}^{11} ESC_{1B  33}^{27} +_{2B  53}^{43} ;_{3B  73}^{59} K_{4B  113}^{75} [_{5B  133}^{91} k_{6B  153}^{107} {_{7B  173}^{123}
1 1 0 0 FF_{C  14}^{12} FS_{1C  34}^{28} ,_{2C  54}^{44} <_{3C  74}^{60} L_{4C  114}^{76} \_{5C  134}^{92} l_{6C  154}^{108} |_{7C  174}^{124}
1 1 0 1 CR_{D  15}^{13} GS_{1D  35}^{29} −_{2D  55}^{45} =_{3D  75}^{61} M_{4D  115}^{77} ]_{5D  135}^{93} m_{6D  155}^{109} }_{7D  175}^{125}
1 1 1 0 SO_{E  16}^{14} RS_{1E  36}^{30} ._{2E  56}^{46} >_{3E  76}^{62} N_{4E  116}^{78} _{  ˆ 5E  136}^{94} n_{6E  156}^{110} _{  ˜ 7E  176}^{126}
1 1 1 1 SI_{F  17}^{15} US_{1F  37}^{31} /_{2F  57}^{47} ?_{3F  77}^{63} O_{4F  117}^{79} {95 \atop 5F  137} o_{6F  157}^{111} DEL_{7F  177}^{127}

LEGEND: dec CHAR^{hex oct}

Table B.1: The ascii table

ver 1.7, 27 February 1996

Appendix C

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 C.1 on the following page. This organisation is determined by the design of the address decoders for each of these four main devices in the flight-68k microprocessor system.

Figure C.1 on the next page 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 3.1 on page 56.

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.

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 C.1: A memory map of the flight-68k microprocessor board. M means megabytes; K means kilobytes. 1M = 2^{20}; 1K = 2^{10}.

Appendix D

The compile, assemble and link process

You are familiar with the Edit/Compile/Download/Run procedure that you have used many times—once each time you create and run a program on the flight-68k microprocessor board. You should refer to your lab sheets for more information about the procedure for editing, downloading and running a program.

So you have used a C compiler often. But what is a C compiler? A compiler is a software translator that translates a high-level language that microprocessors do not understand to another target language. Usually, the target language is machine language, which a microprocessor does understand.

In our labs, the compiler consists of many programs: it contains a preprocessor, a translator from preprocessed code to assembly language, an assembler and a linker. There are also some other programs that can be part of the process, such as an optimiser and also a listing program. What do all these do? See figure D.1 on the next page for what happens when you compile the program ⟨prog⟩.c.

D.1 The preprocessor

The preprocessor takes a C language program, removes all the comments (see section 2.4 on page 11) and also obeys all the commands in your program

[figure] [figure] [figure] [figure] [figure] [figure]

Editor edit.com

Preprocessor cpp68k.exe

C Compiler ccm68000.exe

Assembler as68k.exe

Linker ld68k.exe Lister clst68k.exe

include files

⟨prog⟩.c

⟨prog⟩.i

⟨prog⟩.s

⟨prog⟩.obj

                                       aout.abs

⟨prog⟩.lst

libraries other compiled object code

                                                   linkcom.map

C source file

preprocessed C file: no comments, include files inserted

Assembly language

Assembler listing with C code

Motorola S records

⟨prog⟩.lst

Figure D.1: The programs involved in compiling the program ⟨prog⟩.c. Note that the files ⟨prog⟩.i, ⟨prog⟩.s are deleted automatically unless you ask the compiler to keep them. Also ⟨prog⟩.lst will only be created if you ask the compiler to make it.

that begin with a ‘#’, such as the commands “#include <flight.h> ” and “#define NUMPATTERNS 8 ”.

You can look at the output from the preprocessor. To look at the output from preprocessing a file ⟨prog⟩.c, type:

C:\MICROP> cc68k -P ⟨prog⟩.c

The output is in the file ⟨prog⟩.i. Try that in the lab with any file containing the line

#include <flight.h>

D.2 The translator (or the main part of the C compiler)

The translator produces assembly language as output. You can read the assembly language output from the compiler. If you are working with a program ⟨prog⟩.c, you can read the assembler output by typing:

C:\MICROP> cc68k -S ⟨prog⟩.c

The output will be ⟨prog⟩.s. You can look at this with the editor. Do this next time you are in the lab. However, you get a more interesting view of your program if you look at the listing file.

D.3 The listing file and the map file

The listing file shows the C language for your source program and the assembly language together. To get a listing file for the program ⟨prog⟩.c, type:

C:\MICROP> cc68k -Lxi ⟨prog⟩.c

The listing file is then ⟨prog⟩.lst. Try this in the lab and have a look at the list file with the editor. The listing file is produced by the listing program.

A file linkcom.map is also produced. You may wish to take a look at that also. This is called a map file. It shows the address of all the variables and functions in your program.

When you run the compiler, you will get the following error message from the linker if you are successful:

 Errors: 0, Warnings: 0
CC68K.EXE: can't open .map or aout.lst in the current directory

Ignore this message; the map file linkcom.map and the listing file ⟨prog⟩.lst are both created successfully.

D.4 The assembler

The output of the translator is assembly language. Assembly language is rather close to the machine language that the microprocessor understands.

There is usually one machine language instruction for each assembly language instruction.

D.5 Relocatable object file

The assembler takes the file produced by the translator and converts this into relocatable machine code. The result is an object file. If you compile ⟨prog⟩.c, the object file is ⟨prog⟩.obj.

Note two things about the object file:

• It does not have any absolute memory locations specified.

• It does not contain any library routines, such as printf ().

D.6 The linker

The final step in the compilation process is linking. A program called the linker does two things:

• It determines the absolute address that each function and variable will have.

• It adds the startup code and any also any library code you may have used, such as printf ().

The result is the file aout.abs. This file contains the machine-language output, in a form called Motorola S-records. You can look at the file aout.abs with a text editor, since it is a text file and not a binary file. It is made this way so that it can be conveniently downloaded by serial transfer.

D.6.1 The startup code

It would seem that your C program should begin execution at the beginning of the main() function. However, it does not—every C compiler provides some startup code (usually written in assembly language) which initialises the microcomputer system ready for your program to execute. Such initialisation includes tasks such as giving an initial value to the stack pointer, giving initial values to the interrupt vectors (to be covered in lectures), and various other tasks that may be required for the particular hardware. When you work with a C compiler for an embedded system, you will quite likely need to make minor modifications to the startup code. For example, the stack must be located in physical ram, the location of which may vary from one microcomputer system to another.

I have added a simple function to the startup code for our system, which initialises the 68230 pi^{/}t and puts it into submode 1x. See section 3.2.6 on page 64 for more about submode 1x. The function is called InitParallelChip() and is shown in program example 3.1 on page 67.

D.7 The library

A C compiler comes with one or more library files. These contain functions that have already been written and compiled by someone else. These library functions extend the basic language, and include such functions as printf () and many others. Many such library functions have been standardised by the ansi committee (see section E on page 92), so all compilers come with these library functions.

Whenever we use a library function, we always #include a header file, which contains a declaration for that function. For instance, whenever we use printf (), we always #include <stdio.h> . See section 2.3 on page 10.

D.7.1 The file ��������

At the top of all of your programs that use perform input and output through the parallel chip, the 68230 pi^{/}t, you put the line:

#include <flight.h>

This file contains:

• the definition of the names LEDS, SWITCHES, SEVEN SEGMENT, KEYPAD and all the names shown in the ‘abbreviation’ column of table 3.2 on page 63 which provide a name for all the registers in the pi^{/}t.

• A definition for a Boolean type, and the constant values true and false.

• declarations of various functions which I have written to save you time. These include the following functions: kbhit(), getch(), DelayMs(), WriteSevenSegment(), ReadKey(). I will add to this list as the need arises.

You need to include the header file flight.h whenever you use any of these. Note that these functions are not standard, and so are unlikely to be found on other compilers, although Borland C offers kbhit() and getch(). Quincy also provides getch().

D.7.2 The library function DelayMs()

This is a simple function that uses a for loop nested inside a while loop to wait for a number of microseconds to pass by. The function prototype is:

void DelayMs( int milliSeconds );

We can also call a function prototype a function declaration.

Program example D.1 gives a definition of DelayMs().

Program example D.1 A definition of DelayMs().

void DelayMs( int milliSeconds )

{ int i; while ( milliSeconds−− > 0 )

for ( i = 0; i < 193; ++i ) ; /∗ null statement ∗/

}

How did I come up with the magic number 193? I simply used my stopwatch and an iterative guessing process called an interpolation search.

To use DelayMs() in a program to wait 300 milliseconds, type a line like this into your program:

DelayMs( 300 );

D.7.3 The library function kbhit()

The kbhit() library function returns true if a key has been pressed on the terminal keyboard—the keyboard on the pc compatible computer connected to the flight-68k—and false if no key has been pressed. If a key has been pressed, to stop kbhit() from still returning true you need to call getch()—see section D.7.4. The function prototype for kbhit() is:

int kbhit( void );

kbhit() is very useful in loops that you want to terminate when someone presses a key on the pc. As an example, instead of using the while statement:

while ( true ) …

in program 2.20 on page 44, you could use

while ( ! kbhit() ) …

instead. The program will terminate when you press a key rather than requiring you to press the reset button on the flight-68k.

The implementation is in assembly language. It calls a monitor firmware routine called inkey. Such routines are called using software interrupts: that is what the trap instruction is. Read chapter 5 of the flight-68k manual, [Fli88] for details of the monitor firmware routines. See also section 3.11 on page 38 in [Urb96]. The file is f:\hpcc68k\env\flight\src\ kbhit.s; you may read it if you like. The source is shown in program example D.2 on the following page for your interest.

D.7.4 The library function getch()

The getch() function works rather like kbhit(), except that it will not return until a key is pressed. As soon as a key on the pc terminal is pressed, the function will return with the 7-bit ascii code of the key character. Like kbhit(), getch() is implemented using a monitor firmware routine which is called with a trap instruction.

The function prototype of getch() is:

int getch( void );

Program example D.2 The assembly language implementation of kbhit().

* Flight-68k monitor ROM routine to input a character, checking for
* break and hold.
inkey   equ     27
        section libc,,c
_kbhit  trap    #11             Call the monitor ROM routine to check
        dc.w    inkey           for a waiting character.
        beq     noKey           If it is there,
        moveq   #1,d0              show it by returning 1
        bra     end             else
noKey   moveq   #0,d0              return 0
end     rts
        end

The implementation is rather simple and is shown in program example D.3.

The original file is f:\hpcc68k\env\flight\src\getch.s. The getch mon-

itor firmware routine is described on page 5-10 od [Fli88].

Program example D.3 The implementation of the library function getch().

* Flight-68k monitor ROM routine to input a character, checking for
* break and hold.
getch   equ     13
        section libc,,c
_getch  trap    #11             Call the monitor ROM routine to wait
        dc.w    getch           for a character.
        rts
        end

D.7.5 The library function WriteSevenSegment()

The @WriteSevenSegment()@ function is available for you to write to the seven segment display without having to calculate all the values for the lookup table. The function prototype is

void WriteSevenSegment( int number, Boolean showPoint );

The bottom four bits of the parameter @number@ are displayed on the seven segment display. If the parameter @showPoint@ is @true@, the decimal point is turned on, otherwise it is turned off. To turn off all seven segments (but not the decimal point: that is controlled by the second parameter), set @number@ to @-1@.

The original file is f:\hpcc68k\env\flight\src\sevseg.c. Program ex-

ample D.4 shows part of the implementation, but the hexadecimal values are not shown.

Program example D.4 The library function WriteSevenSegment().

const unsigned char sevenSegPattern[ 16 ] =

{ 0x3f, /∗ 0 ∗/ 0x06, /∗ 1 ∗/

⋮

0x71 /∗ f ∗/ };

/∗ Pass -1 to turn off all the seven segments ∗/

void WriteSevenSegment( int number, Boolean showPoint ) {

unsigned char pattern = sevenSegPattern[ number & 0xf ]; if ( number == −1 ) pattern = 0; if ( showPoint )

SEVEN SEGMENT = pattern | 0x80; else

SEVEN SEGMENT = pattern;  10

}

D.7.6 The library function ReadKey()

The library function implements one scan of the 3 × 4 telephone keypad on the interface board. The algorithm is described in detail in section 3.3.1 on page 69. The value NOKEYPRESSED is defined in the header file flight.h, and is the return value of ReadKey() if no key is pressed, otherwise it will return the values shown in table D.1 on the following page.

ReadKey() return value: 0 1 2 3 4 5 6 7 8 9 10 11
Value printed on the key: 1 2 3 4 5 6 7 8 9 # 0 ∗

Table D.1: The return values of the library function ReadKey().

The function prototype for ReadKey() is

int ReadKey( void );

Program example D.5 shows the implementation of ReadKey().

Program example D.5 The library function ReadKey(). This is an implementation of the algorithm described in section 3.3.1 on page 69.

#include <flight.h> #define NUMCOLS 3 #define NUMROWS 4

int ReadKey( void )

{ int row, col; unsigned char pattern; for ( col = 0; col < NUMCOLS; ++col )

{  10

KEYPAD = ∼( 1 << col ); pattern = KEYPAD & 0x78; if ( pattern == 0x78 )

continue; for ( row = 0; row < NUMROWS; ++row ) if ( ( pattern & ( 8 << row ) ) == 0 ) return col + row ∗ NUMCOLS;

}

return NOKEYPRESSED;

}  20

D.8 The make program

In our course, all the C programs have occupied only one file. When you write a larger program for your final year project or in your work, it will be easier to write if you use a number of source files for your program. There is a program called make that is very useful for larger programs. It is beyond the scope of this book to explain how to use make; I just want to point out to you that it will be worth learning how to use make for large programs.

Appendix E

Why C and not assembly language?

In this course we use the C language. C is a high level language that has a number of features:

• C has been standardised. The American National Standards Institute (ansi) has provided a standard for the language that not only covers the basic language, but also includes many library functions, such as printf ().

• All C compiler manufacturers work hard to be sure that their compiler is close to the standard.

• The result is that the language is perhaps the most portable language available.

• C has good support for low-level access to the hardware of a computer— it was originally designed to implement the UNIX operating system. It is sometimes called a portable assembly language.

• C can easily be mixed with assembly language.

The C language is a high-level language. Once line of C may replace 6 lines of assembly language. However, it takes a programmer about the same time to write a line of assembly language as it does to write a line of C. So a programmer writing in C may produce up to 6 times the amount of code that the same programmer can produce when writing in assembler.

This means that many manufacturers will prefer to have their products programmed in C rather than in assembler, since it will cost less and take less time to finish. This is particularly true for larger embedded systems.

The cost of using C rather than assembly is that a C program will usually translate into more machine language instructions than an equivalent assembly language program. An assembly language program can also be faster than a C program. In small systems where memory is very limited, assembly language may be the only option although the use of assembly language is declining.

The usual practice in writing a C program is to write most in C, then to identify the short parts of the program that are executed most often, then rewrite those few parts in assembly language, often starting with the assembly language output from the translator.

Appendix F

The Quincy interpreter—get it and use it!

F.1 What is Quincy?

Quincy is freely available software that can run ansi C programs. It requires very little hard disk space (only 360 KB). It is an interpreter, not a compiler—see the next section. Quincy is written by Al Stevens, a professional programmer who writes the column C Programming in Dr Dobb’s Journal, [DDJ]. Quincy is described in the book [Ste94], available in the library.

F.1.1 What is an interpreter?

To explain what a C interpreter is, we will compare it with a C compiler.

A C compiler translates a C source file into a file containing machine language. This file is sometimes called an executable file. If this is a crosscompiler, we can download the executable file into the memory of the microprocessor system then execute it. The executable file can run at high speed. The executable file can be run without any further requirement for the C compiler. As an example, the spreadsheet program Excel has been written in C or C^{++}, but you do not need a C or C^{++} compiler to run Excel.

A C interpreter, such as Quincy, does not produce an executable file. Instead, the interpreter translates the C source file during the process of executing it. The program cannot be run without the interpreter: the translation to machine language is done each time the program is run.

There are many interpreters available for many languages. Perhaps basic is the best known interpreted language: every copy of ms-dos comes with a copy of the qbasic interpreter. There are many other interpreters available. The ms-dos program command.com itself is a language interpreter; it interprets the Bat language.^{1} Some other well known interpreted languages include perl, forth, awk, Java, Rexx, sed and the shell scripting language sh that is part of unix.

F.1.2 What can I do with Quincy?

If we cannot produce an executable file, and we cannot run a program without the interpreter, what use is the Quincy interpreter? The answer is that Quincy is very useful for learning C—that is what it was written for. It is also useful for debugging programs, since it has good debugging facilities: you can single step through your C programs, statement by statement, and you can set breakpoints to halt the execution of your program at any statement, and you can watch variables change as your program executes. The error messages are also often more helpful: for example, the program testc.c on page 100 will produce the error message missing ';', which is more clear than the message from the C compilers.

You can develop a program using Quincy, then compile it with an ansi compiler.

F.2 How to get a copy of Quincy

Quincy is freely available to you. To take a copy to your computer at home, work or your friend’s house, you need a floppy disk with only 360 KB of free space. To copy it to a floppy disk, do the following:

1. Go to the Microprocessor Applications Laboratory, D226

1Holy cow, Batman! Do you mean we have been using Batlanguage all this time without realising it? Yes, Boy Wonder, it’s true—now I realise why I like writing batch files so much.

96  The Quincy interpreter—get it and use it!

2. Turn on one of the computers

3. Select the “Novell network” option from the boot menu

4. type:

f: ⟨Enter⟩

5. Type:

   cd \quincy ⟨Enter⟩

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

7. Run the batch file copyit.bat by typing:

copyit ⟨Enter⟩

8. When disk activity stops, remove your floppy disk

To install the software on a computer, you need 360 kilobytes of free space on the hard disk. Here is how to install Quincy.

1. Take the floppy disk home

2. Make a directory on your computer by typing md \qnc ⟨Enter⟩

3. Change to that directory with cd \qnc ⟨Enter⟩

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

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

Appendix G

Compiler errors: they are there to help you

A C compiler performs two main functions:

• it translates a C source file to machine language

• it detects syntax errors and shows their location and shows what type of syntax error each one is.

After you write a few C programs you will see some error messages appear when you run the compiler. Many people feel bad when this happens, as if the compiler is telling you that you don’t know how to write proper C. I have seen some people look with fear at the error messages, then rush back to the editor to change the program at random, hoping that the errors would go away. This is not a good approach.

Compiler error messages are your friends: they are there to help you. Here we see how. I am mainly talking about the error messages from the 68000 C cross-compiler system.

G.1 What do I do if there are lots of error messages?

If there are many error messages, a good place to start is the first one. Very often, one syntax error causes the compiler to misunderstand parts of the program that follow. The first error message often gives a better clue to problems than some of the error messages that come immediately after.

Suppose that there are so many error messages that they roll right off the top of the window?^{1} The best I can suggest at present is to press the ⟨Pause⟩ key as soon as the error messages begin to appear.

G.2 Error messages from different parts of the compiler

The compiler consists of many programs. Each can generate error messages. The preprocessor, the main part of the compiler, and the linker can all give error messages. It may help to read appendix D on page 81, and particularly to look at figure D.1 on page 82.

G.2.1 Error messages from the preprocessor

The preprocessor deals with:

• all lines of your program that begin with #

• all comments—the preprocessor removes these from its output.

Error messages do not give you a copy of the wrong line, but they do give you the line number. For example, the following program error.c has the #include directive spelt wrongly:

1It would be nice to redirect them to a file and look at them at leisure. Unfortunately, the programs write to standard error, which (in ms-dos version 6 and 6.22) I do not know how to redirect. I hope that eventually the compiler manufacturer may reply to my question.

ver 1.7, 27 February 1996

#inclood <flight.h> int main( void ) {

int i, j;

for ( i = 0; i < 9; ++i )

j = i; return 0;

}

If you type

cc68k error.c

the preprocessor will provide the following error message:

\texttt{error1.c: 1: Unknown preprocessing directive.}

This indicates that the preprocessor did not understand the preprocessing directive “#inclood”. This message usually means that the name following the ‘#’ is spelt wrongly. Notice that the error message does tell you where the error is: the ‘1:’ means that the preprocessor found the problem on line 1.

G.2.2 Error messages from the main part of the compiler

Most error and warning messages come from the main part of the compiler. You should find the cause of all warning messages as well as error messages. Remember—they are there to help you.

Each error and warning message from the main part of the compiler tells you:

• the line number where the compiler found something wrong

• the exact location on the line where the compiler found something wrong

• the type of error or warning.

Here we look at a simple example. If you compile the following program (which was called testc.c):

int main( void )

{

return 0 }

you will get the following error message:

}
^
"testc.c", 4: Syntax error.

Firstly we have the line in which the compiler discovered the error. On the next line we have a little arrow ‘^’ which shows the exact location in that line at which the compiler discovered the error. On the next line we have:

1. name of the file in which the compiler found the error

2. next is the line number

3. finally we have the nature of the error (but in this case, the message is very general).

So we know that the compiler found the error when it reached line 4. But here is one important fact about error compiler messages: the compiler will indicate a position \underline{after} the actual error. In other words, you should usually look at the position in the program at the text just \underline{before} where the compiler indicates an error. So what is the text just before where the compiler shows where it discovered the error? It is the character 0. Is there something wrong with that? Yes! We know it should be followed by a semicolon, ‘;’, as discussed in section 2.8 on page 31.

Let me say once more: the first error message is often the most useful place to start.

G.2.3 Error messages from the linker

The final message you see after successful compilation is

 Errors: 0, Warnings: 0

This message comes from the linker. Sometimes you will see this message even after the main part of the compiler displays a warning message! The reason for this contradiction is that the linker has no warnings to give.

However, the linker can give error messages of its own. If you compile this little program (in the source file prog.c):

int main( void ) {

Delay(); }

you will see the following error messages:

   Delay();
        ^
"prog.c", 3: warning- Call of undeclared function 'Delay'.
ERROR: (320) UNRESOLVED EXTERNALS:
SYMBOL                                    MODULE
_Delay                                    prog
 Errors: 1, Warnings: 0

The warning message is from the main part of the compiler; it is warning you because the function Delay() has no declaration. As mentioned before in section 2.2.1 on page 9, all functions and variables should be declared before they are used.

The error message at the end is from the linker. It cannot find any library function Delay() in the library to link with your program. Such an error message usually means that you have misspelled the name of a function or variable.

Appendix H

Editing, compiling, downloading and running a program

H.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 71. Flow charts are also sometimes useful in working out complicated loops and decisions, mainly for assembly language sections of your program. 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.

H.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 ; • compile ; • download  • run ; • observe

This is an appendix showing the general procedure for creating and running a program. Here we will call the program ⟨program⟩.c. Replace all occurrences of “⟨program⟩” by the name of the program you are writing or working with.

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 are using the seven-segment display or the LEDs, set the jumper on the interface board to the correct position. See figure 3.3 on page 68.

3. If you turn on the computer, select Novell Network from the MS-DOS startup menu.

4. Log into the Novell network by typing:

     f:login guest ⟨Enter⟩

The screen will fill with information about which drive letter is mapped to which drive. At the top a message says “You

          have logged into the TYEE22601 File Server.” This indic-

ates that you have logged in successfully.

The network provides access to the Hewlett Packard C crosscompiler.

5. Start up microsoft windows by typing:

win ⟨Enter⟩

H.3 Editing the program

Here we use the same editor—MS-DOS Editor—as in previous labs. You can also use Emacs or Q-edit if you prefer.

Each program you write needs the format shown in program example H.1 on the following page. Note too that any variable or function you use must be defined before it is used.

104  Editing, compiling, downloading and running a program

Program example H.1 All your programs will have a form similar to this.

#include <flight.h>

/∗ Define global variables here. ∗/

/∗ Define your functions here. Each function has a similar format ∗/ /∗ to main(); the local variables come first. ∗/

int main( void ) {

/∗ Define local variables here. ∗/  10

/∗ Put the body of your program here ∗/

return 0; }

1. Start the editor MS-DOS Editor by double-clicking on the MS-DOS Editor icon in the window labeled HP Real-time C Debugger.

Do not start up more than one editor, and do not exit from the editor until the end of this workshop. It is easier and quicker to leave a single copy of the editor, DOS prompt and terminal programs running all the time and to switch between them using ⟨Alt-Tab⟩, or the mouse.

2. Remember the following points about the C language:

• C is case sensitive; type #include not #Include, etc.

• Each statement must be terminated with a semicolon “;”.

• Do not put a semicolon on the same line as the keyword if, because the statement only finishes after the body of the if statement, i.e., you should write

if ( pattern != 0x78 ) keyPressed = true;

and not:

if ( pattern != 0x78 ); keyPressed = true;

H.4 Compiling your program  105

The if statement above should finish at the end of “keyPressed = true;”. The same thing is true of the while statement.

3. Save your edited program as ⟨program⟩.c using ⟨Alt-f ⟩⟨s⟩.

Note: the name must have .c at the end, otherwise the compile will not succeed.

H.4 Compiling your program

1. If you have no MS-DOS Prompt window open, open one, otherwise switch to the previous MS-DOS Prompt window using ⟨Alt-Tab⟩.

2. Compile your program by typing cc68k ⟨program⟩.c.

If there are any error messages shown, edit your file again to correct the error, save the changed file, then re-compile your program until you get no error messages. Ask for help if you cannot see the reason for the error message. Note that the filename must be in lower case, otherwise the compile will not succeed. The compiler produces the file aout.abs which contains machine language encoded in Motorola S-record format. To find out more about Motorola S-records, see appendix E of the Flight-68K User Manual. You can also look at the file aout.abs with a text editor, since it is a text file.

H.5 Downloading the 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 up the Terminal program by double-clicking once on the Terminal icon.

If Terminal is already running, do not start a second Terminal program running; instead, switch back to the Terminal program you started earlier with ⟨Alt-Tab⟩.

2. Press ⟨Enter⟩ two times.

106  Editing, compiling, downloading and running a program

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.

4. Press the ⟨Enter⟩ key When prompted with:

ENTER OFFSET (<CR> IF NONE) :

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. Press ⟨Enter⟩ when asked for an offset.

6. With the mouse, select the Transfers menu, and from this select the Send Text File … menu item.

7. Type aout.abs ⟨Enter⟩ into the dialog box.

H.6 Running the program

1. Type ⟨g⟩⟨o⟩, then enter the starting address 400400, as always.

Appendix I

Solutions to exercises

Solution 2.1 The “null” string "" contains one character: the nul ascii character.

Solution 2.2

(a) The sizes shown here are for the 68000 system in the laboratory:

int i; /∗ a long word-sized integer called i. ∗/ unsigned char uch; /∗ a byte-sized variable called uch. ∗/ short sh; /∗ a word-sized variable called sh. ∗/ unsigned int ui; /∗ a long-word sized variable called ui. ∗/ char ch; /∗ a byte-sized variable called ch. ∗/

Here are three more definitions, of an unsigned short , a long int and an unsigned long int :

unsigned short ush; /∗ a word-sized variable called ush. ∗/ long li; /∗ a long-word sized variable called li ∗/ unsigned long uli; /∗ a long-word sized variable called uli. ∗/

(b) For the 68000 system in the laboratory:

type  size (bits) size (bytes) 2’s compl/magnitude
integer  32  4  2’s complement
unsigned char  8  1  magnitude only
short integer  16  2  2’s complement
unsigned integer  32  4  magnitude only
char  8  1  2’s complement^{1}
unsigned short integer  16  2  magnitude only
long integer  32  4  2’s complement
unsigned long integer  32  4  magnitude only

(c) For ms-dos:

type  size (bits) size (bytes) 2’s compl/magnitude
integer  16  2  2’s complement
unsigned char  8  1  magnitude only
short integer  16  2  2’s complement
unsigned integer  16  2  magnitude only
char  8  1  2’s complement
unsigned short integer  16  2  magnitude only
long integer  32  4  2’s complement
unsigned long integer  32  4  magnitude only

(d) See the table above. “Magnitude only” means that the computer uses the unsigned value for the number and so can only hold values ≥ 0. “Two’s complement” means that the computer uses the signed value for that number and so can hold a negative value.

(e) Here is one printf () statement for each variable:

  printf ( "The integer i = %d\n", i );

/∗ print the single character uch: ∗/

  printf ( "The unsigned character uch = %c\n", uch );

/∗ print the ASCII value of uch, in decimal: ∗/

  printf ( "The unsigned character uch = %hu\n", uch );

/∗ print the ASCII value of uch, in hexadecimal: ∗/

  printf ( "The unsigned character uch = %hx\n", uch );
  printf ( "The short integer sh = %hd\n", sh );

1However the char type can be unsigned by default in some compilers.

ver 1.7, 27 February 1996

Solutions to exercises  109

/∗ print the single character ch: ∗/

printf ( "The character ch = %c\n", ch );  10

/∗ print the ASCII value of ch, in decimal: ∗/

printf ( "The character ch = %hd\n", ch );
printf ( "The unsigned integer ui = %u\n", ui );

/∗ print the value of ush, in decimal: ∗/

printf ( "The unsigned short integer ush = %hu\n", ush );

/∗ print the value of ush, in hexadecimal: ∗/

printf ( "The unsigned short integer ush = %hx\n", ush );
printf ( "The long integer li = %ld\n", li );

/∗ print the value of uli, in decimal: ∗/

printf ( "The unsigned long integer uli = %lu\n", uli );  20

/∗ print the value of uli, in hexadecimal: ∗/

printf ( "The unsigned long integer uli = %lx\n", uli );

Solution 2.3

#include <stdio.h> int main( void ) {

   printf ( "This is a message from Nick.\n" );

/∗ Or you could write: ∗/

   printf ( "%s", "This is a message from Nick.\n" );

/∗ An alternative is to use puts(). Notice that puts() gives a newline. ∗/

   puts( "This is a message from Nick." );

return 0;

}  10

The library function puts() is also part of the standard library; I have implemented puts() on the 68000 system also. Of course it is also available with Quincy. The puts() library function adds a newline character at the end of the string, so I have not put one there in this solution.

Solution 2.4

#include <stdio.h> int main( void ) {

unsigned char ch = 129;

   printf ( "The signed value of ch is %hd;", ch );
   printf ( " the unsigned value is %hu\n", ch );
   printf ( "The hexadecimal value of ch is %hx;", ch );
   printf ( " the character value is %c\n", ch, ch );

return 0;

}  10

The output of this program is:

The signed value of ch is 129; the unsigned value is 129
The hexadecimal value of ch is 81; the character value is ü

Solution 2.5

#include <stdio.h>

int main( void )

{ int i = 27; short sh = 56; char ch = 'C';

        printf ( "i = %d, sh = %hd, ch = %hd\n", i, sh, ch );

return 0;  10

}

Solution 2.6

(a) 7 == 6 has the integer value 0, which is the logical value false.

(b) 5 != 7 has the integer value 1, which is the logical value true.

(c) 5 < 7 has the integer value 1, which is the logical value true.

(d) 9 > 0 has the integer value 1, which is the logical value true.

(e) 5 < 7 && 9 < 0 has the integer value 0, which is the logical value false.

(f) 5 < 7 | | 9 < 0 has the integer value 1, which is the logical value true.

(g) 10 >= 0 && 30 <= 30 | | ! ( 50 < 0 ) && 7 − 3 > 5

Solution 2.7 Because the program uses printf (), we must #include stdio.h.

#include <stdio.h>

int main( void )

{ int i; for ( i = 10; i <= 30; ++i )

           printf ( "%d squared = %d\n", i, i ∗ i );

return 0;

}  10

Solution 2.8

for ( i = 10; i < 20; ++i ) {

SEVEN SEGMENT = pattern[ SWITCHES & 0xf ];

DelayMs( 200 ); }

Solution 2.9

#include <stdio.h> int main( void ) {

int i = 1;

do

{ printf ( "i = %d\n", i ); ++i;

} while ( i <= 10 );

return 0;  10

}

Solution 2.10

#include <stdio.h> int table[ ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; /∗ Initialise at compile time. ∗/ int main( void ) {

int i, sum = 0;

for ( i = 0; i < 10; ++i )

sum = sum + table[ i ]; /∗ could write: sum += table[ i ]; ∗/

   printf ( "The sum of the numbers is %d\n", sum );

return 0;  10

}

Here is another solution which assigns the table[ ] values rather than initialises the array:

#include <stdio.h> int table[ 10 ]; int main( void ) {

int i, sum = 0;

for ( i = 0; i < 10; ++i )

table[ i ] = i + 1; /∗ Assign at run time. ∗/

for ( i = 0; i < 10; ++i )  10

sum = sum + table[ i ];

   printf ( "The sum of the numbers is %d\n", sum );

return 0; }

Solution 2.11

#include < stdio.h> #define LEN 10 int array[ LEN ];

int main( void ) {

int i;

for ( i = 0; i < LEN; ++i )

array[ i ] = i + 1;

10

for ( i = 0; i < LEN; ++i )

printf ( "array[ %d ] = %d\n", i, array[ i ] ); return 0;

}

Solution 2.12

#include <stdio.h> int main( void ) {

int i;

for ( i = 0; i < 10; ++i )

printf ( "i = %d\n", i );

return 0; }

Solution 2.13 The problem is that the index for patterns[ ], which is i, takes the values 1, 2, …,9, 10 instead of the values 0, 1, …,8, 9. There is an array indexing error at each end of the array. By starting with the index 1 instead of 0, the first element in the array is missed. More seriously, the last index used by this program causes the computer to access memory past the end of the array. The value that this memory location holds is unknown. To solve this, change the for loop to:

for ( i = 0; i < NUMPATTERNS; ++i )

Notice that there are two changes to this line.

There is one other thing about this program: it will display the patterns so quickly on the seven segment display that only the last pattern will be seen. To be able to see all the patterns, it will be necessary to put a delay in the loop. A call to the function Delay() given in question 2.16 on page 53 will do the job. Alternatively you could put a call to the library function DelayMs() described in section D.7.2 on page 86. The loop would then look something like this:

for ( i = 0; i < NUMPATTERNS; ++i )

{ SEVEN SEGMENT = patterns[ i ]; DelayMs( 500 );

}

Solution 2.14

void Leds( unsigned char pattern )

{ int i; for ( i = 7; i >= 0; −−i ) {

if ( pattern & ( 1 << i ) )

printf ( "o" ); else

printf ( "." );

}  10

printf ( "\n" ); }

Solution 2.15 Refer to figure 3.2 on page 62.

#define a 1 #define b 2 #define c 4 #define d 8 #define e 0x10 #define f 0x20 #define g 0x40 #define DP 0x80

void SevenSegment( unsigned char pattern )  10

{

if ( pattern & a )

printf ( " _\n" ); else

printf ( " \n" ); if ( pattern & f )

printf ( "|" ); else

printf ( " " );

if ( pattern & g )  20

printf ( "_" ); else

printf ( " " ); if ( pattern & b )

printf ( "|\n" ); else

printf ( " \n" ); if ( pattern & e )

printf ( "|" );

else  30

printf ( " " ); if ( pattern & d )

printf ( "_" ); else

printf ( " " ); if ( pattern & c )

printf ( "|" ); else

printf ( " " );

if ( pattern & DP )  40

printf ( ".\n" ); else

printf ( " \n" ); }

Solution 2.16 The first void (to the left of the name of the function Delay) means that the function Delay() returns no value at all. It also means that we cannot put a return statement in Delay() that returns a value—for example, the compiler would not allow you to put return 0; in the body of the Delay() function.

The second void, the one in the parentheses, means that Delay() takes no parameters. In other words, no additional data is required by the Delay() function to decide what it should do—it delays by looping a fixed number of times.

Solution 2.17

#include <stdio.h>

int FtoC( int fahrenheit )

{

return 5 ∗ ( fahrenheit − 32 ) / 9; }

int main( void )

{ int centigrade;

centigrade = FtoC( 75 );

        printf ( "75 degrees Fahrenheit is %d Centigrade.\n", centigrade );

return 0; }

Solution 2.18

#include <stdio.h>

int Max( int a, int b )

{

if ( a > b ) return a;

else

return b; } int main( void ) {

int i = 40;

int k = 54;

int biggest;

biggest = Max( i, k );

   printf ( "The biggest of i and k is %d.\n", biggest );

return 0;  10

}

Solution 2.19 Because the program uses printf (), we must #include stdio.h.

#include <stdio.h>

int Square( int n ) {

return n ∗ n; }

int main( void ) {

int i;

for ( i = 10; i <= 30; ++i )

printf ( "%d squared = %d\n", i, Square( i ) );

return 0; }

Notice that we can put a function call anywhere inside another function (such as main() here) where we might use a variable. Compare this solution with that for exercise 2.7 on page 36.

Solution 2.20 Assume that the value of exp is positive or zero. Power() calculates base^{exp}.

#include <stdio.h>

int Power( int base, int exp ) {

int i, p = 1;

for ( i = 0; i < exp; ++i )

p = p ∗ base; return p;

} int main( void ) {

int n = Power( 2, 15 );

   printf ( "2 to the power of 15 is %d\n", n ); /∗ n = 32768 ∗/

return 0; }

Solution 3.1 1. If there are three address line inputs to the address decoder, A_{23–21}, then a new range of addresses is selected when A_{2}1 changes. So the lowest address, A00000_{16} is active when A23 = 1, A_{22} = 0 and A_{21} = 1. This three-bit number is 101_{2}. When this three-bit number is incremented, then the duart will cease to be activated. This will happen when A23 = 1, A_{22} = 1 and A_{21} = 0, which (when all other bits are zero) is C00000_{16}. The address one less than this is BFFFFF_{16}. That is the highest address at which the chip select of the duart will be activated.

2. There are 16 address lines, A_{20–5}, that are connected neither directly to the duart nor to its address decoder. Therefore there are 2^{16} = 65536 different addresses for each register in the duart.

Solution 3.2 We can write the value 1000 0000_{2} or 80_{16} to the port A control register. We can do so with this statement:

PACR = 0x80;

Note that there are many other possible answers, as the ‘X’s in table 3.3 on page 65 can be either 1 or 0; they are “don’t cares.”

Solution 3.3 We can write the same value as for the previous exercise: 1000 0000_{2} or 80_{16}, or any of many other possible values. We can set port B into submode 1X with:

PBCR = 0x80;

Solution 3.4 We would write the value 1111 0000_{2} to the port B data direction register with:

PBDDR = 0xf0; /∗ Now bits 3. .0 of port B are inputs, 7. .4 are outputs ∗/

Solution 3.5 If the value read from the rows is equal to 0x78 then we know that no key has been pressed, since each row bit is high; none of the keys have connected a row wire to a column wire—we know this because all the columns have been made low, and if the wires contact each other, the corresponding row will be pulled low.

Solution A.1 In these solutions, the symbol ‘∧’ is the mathematical symbol for AND, and the symbol ‘∨’ is the mathematical symbol for OR.

(a) ( 3 + ( 4 ∗ 7 ) ) − 2

This expression has the value 29.

(b) ( 2 & 3 ) | ( 4 + 7 )

This expression has the value

(2 ∧ 3) ∨ (4 + 7) = 2 ∨ 11
= 11.
(c) 1 << ( 4 + 3 )

This expression equals 1 << 7 which is 2^{7} = 128.

(d) 0x45 | ( 1 << 3 )

which is equal to 45_{16} ∨ 2^{3} = 45_{16} ∨ 8 = 4D_{16}.

(e) ( ( ( 4 + 2 ) << ( ( 3 ∗ 2 ) − 2 ) ) | 4 ) | 3

(((4 + 2) << ((3 × 2) − 2)) ∨ 4) ∨ 3 = ((6 << (6 − 2)) ∨ 4) ∨ 3 = ((6 << 4) ∨ 4) ∨ 3 = ((6 << 4) ∨ 4) ∨ 3 = (60_{16} ∨ 4) ∨ 3 = 64_{16} ∨ 3 = 67_{16}.

(f) ++( a[ 4 ] )
(g) ( 3 < ( ( 4 + 7 ) − 1 ) ) == ( 7 + ( 3 ∗ 4 ) )

(3 < ((4 + 7) − 1)) == (7 + (3 × 4)) = (3 < (11 − 1)) == (7 + 12) = (3 < 10) == 19 = 1 == 19 = 0 (h) a %= ( 4 + ( ( 5 * 7 ) % 8 ) )

a %= ( 4 + ( 35 % 8 ) ) a %= ( 4 + 3 ) a %= 7 which has the same value as a = a % 7.

References

[CC] Circuit Cellar Inc. monthly. Also called Computer Applications Journal. Available in library (soon). A great magazine for practical projects using embedded microprocessors.

[CEK^{+}] L. W. Cannon, R. A. Elliott, L. W. Kirchhoff, et al. Recommended C Style and Coding Standards—also known as the Indian Hill Style Guide. Available by ftp from cd.washington.edu as pub/cstyle.tar.Z, updated occasionally. A copy is kept in the laboratory—ask if you are interested.

[DDJ] Dr. Dobb’s Journal. Published monthly, available in the library. A good magazine about programming, mainly in C or C^{++}, mainly on pcs.

[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.

[KR88] Brian W. Kernighan and Dennis M. Ritchie. The C Programming Language. Prentice-Hall, Englewood Cliffs, second edition, 1988.

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

[Per94] Greg Perry. Absolute beginner’s guide to C. Sams publishing (division of Prentice Hall), Indianapolis IN, second edition, 1994. QA76.73.C15P458 1994.

[Ste94] Al Stevens. Al Stevens Teaches C: an interactive tutorial. MIS press, 1994. ISBN 1-55828-387-0. QA76.73.C15S698 1994.

[Sum] Steve Summit. compl.lang.c Answers to Frequently Asked Questions (FAQ List). http://www.eskimo.com/∼scs/C-faq.top.html. A copy is kept in f:\faq\c\c.faq, but this gets rather out of date.

[Urb96] Nick Urbanik. 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 & Communications Engineering, 14 January 1996.

Index

∼, 25, 28–29 =, 20–21 &, 25–27 <<, 30 >>, 30 , 29 |, 27

actual parameter, 53 address, 79

range in 68000 system, 79 address decoder, 59–61, 79 address decoding

partial, 59–60 address space, 79 addresses

used in flight-68k, 79 AND

bitwise operator, 25–27 arrays, 41–45 assignment, 20–21

compound operator, 21 lvalue, 20 rvalue, 20

assignment operator

compared with equality operator, 21

base address, 61 BatLanguage, 95n Batman, 95n bitwise AND operator, 25–27 bitwise complement operator, 25,

28–29 bitwise exclusive-OR operator, 29 bitwise NOT operator, 28–29 bitwise OR operator, 27 bitwise shift operators, 30 block, 32 break statement, 38–39 bubble sort, 45

can’t open .map

error message, 83 capital and lower case letters, 11 case sensitive, 11 chunks

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

clearing individual bits, 25–27 comments, 11 compound statement, 31 conditional tests, 23–24 continue statement, 39–41

data direction register, 66 debouncing a keypad, 71–72 debugging, 4n, 95

using printf (), 11–12 DelayMs()

\underline{INDEX 123}

library function, 86 development system, 4 dip switches, 55, 57, 62 dip-switches, 57 do statement, 37–38 downloading, 105–106

doesn’t work, 106 \overline{dtack}, on control bus, 60 duart, 56, 59, 60, 79

Emacs, 103 embedded system, 4 equality operator

compared with assignment op-

erator, 21 error messages

can’t open .map, 83

first one most important, 98,

100 linker, 100–101 main part of the compiler, 99–

100 occur just before where the message indicates, 100

pausing, 98

preprocessor, 98–99

very many, 98 warnings, 99

exclusive-OR

bitwise operator, 29 expression

definition, 17

in a for statement, 34–36 expressions, 17–30

F> prompt

how to get, 106 false, 21, 23–24 firmware

monitor, 79 flow charts, 102 for statement, 34–36

expressions in, 34–36 formal parameter, 53 function actual parameter, 53 formal parameter, 53 signature, 52 termination, 53

functions, 45–54

getch()

library function, 87–88

how to use this book, 4–5

indentation, 54

input, 55

interface card, 57

interpreter

what is it?, 94–95 i^{/}o

memory mapped, 21 i^{/}o ports, 55–57

Java, 95 jumper, 67

selecting leds or seven-segment, 58–59, 61, 67–68

kbhit()

library function, 87 keypad, 67–68

debouncing, 71–72

detecting key press, 72–73

scanning, 68–71 kilobyte, 80

lab

open door policy, 2 leds, 57 left shift

bitwise operator, 30 library function

DelayMs(), 86 ReadKey(), 89–90 WriteSevenSegment(), 88–89 getch(), 87–88 \underline{124 INDEX}

kbhit(), 87 puts(), 109

logging into the network, 103 logical value

false, 23–24 true, 23–24

lookup table, 41–45 loops

nested, 38 lvalue, 20

make

program, 90–91 megabyte, 80 memory location, 79 memory map, 80 memory mapped i^{/}o, 21 microprocessor, 59–60 microprocessors, 4 monitor firmware, 59, 79

prompt, 106

\n, 12–13 nested loops, 38 network

logging in, 103 newline character, 12–13 NOT

bitwise operator, 25, 28–29

octal buffer, 59 ones’ complement operator, 25, 28–

29 open door policy, 2 open lab policy, 2 operator

bitwise AND, 25–27 bitwise exclusive-OR, 29 bitwise NOT, 28–29 bitwise OR, 27 bitwise shift, 30 precedence, 74–75 operators, 20, 17–30, 74–75 OR

bitwise operator, 27 output, 55

parallel chip

initialising, 64–67 parallel interface/timer base address, 61 initialising for bit i^{/}o, 64–66 registers, 62 submode 1x, 64–66

parameter list, 52

partial address decoding, 59–60 perl, 95 port

i^{/}o, 55–57 A, 57, 67 B, 67 C, 57, 61, 65, 66, 68

ports, 61

printf (), 11–17 displaying integer, 13–14 displaying long integer, 15 displaying newline character,

12–13 displaying short integer, 14 displaying string, 16 displaying unsigned char, 15 displaying variables, 16, 17 examples, 13, 16–17, 109

program design, 102 pseudocode, 71, 102 puts() library function, 109

Quincy obtaining, 95–96 what can it be used for?, 95

ReadKey()

library function, 89–90 registers data direction in pi^{/}t, 66 registers pi^{/}t, 62

right shift

bitwise operator, 30 rvalue, 20 r^{/}\overline{w}, on control bus, 60

selecting leds or seven-segment,

58–59, 67–68 setting individual bits, 27 seven segment display, 61 seven-segment display, 57, 67 shifting bit patterns, 30 signature of a function, 52 single step, 5 68681 duart, 56, 59, 60, 79 68000 microprocessor, 59–60 68230

initialising, 64–67 sort

bubble, 45 statement

break, 38–39 continue, 39–41 do, 37–38 for, 34–36 while, 34 compound, 31–32 definition, 31 examples, 31

submode 1x, 64–66

table, 41–45 toggle

meaning, 29 toggling individual bits, 29 tristate, 59 true, 21, 23–24

unknown preprocessor directive, 98– 99 unresolved externals, 100–101

void in parameter list, 52, 115 return type, 50, 115

warnings, 99

watching variables, 5 while statement, 34 WriteSevenSegment()

library function, 88–89

Reference: EE/27/LA/20/95

Compiled by  Issue  Date  Remark
Nick Urbanik  1  27 February 1996  This is a major reworking of workshop 0 for 2501, 2503

and 2504 from 1994/95. I have turned it into a book format and have improved the organisation, and added more useful information, such as extended information on the C language as we use it in our laboratory, information about the freely available Quincy interpreter and an index and list of references. I have also included many exercises, all with solutions.