Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 5314

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

8. Assembly language 229

tst memory

TeST memory for zero

tsta

TeST Accumulator for zero

Code suitable macros. Why do you think this approach might not be such a good idea?


CHAPTER 9

High-Level Language

All the programs we have written in the last six chapters have been in symbolic assembly language. Whilst assembly-level software is a quantum step up from pure machine-level code (see page 198) nevertheless there is still a one-to-one relationship between machine and assemblylevel instructions. This means that the programmer is forced to think in terms of the MCU’s internal structure – that is of registers and memory

– rather than in terms of the problem algorithm. Although most assemblers have a macro facility, whereby several machine-level instructions can be grouped to form pseudo high-level instructions, this is only tinkering with the di culty. What is this di culty with machine-oriented language? In order to improve the e ectiveness, quality and reusability of a program, the coding language should be independent of the underlying processor’s architecture and should have a syntax more oriented to problem-solving.

We are not going to attempt to teach a high-level language in a single short chapter. However, after completing this chapter you will:

Understand the need for a high-level language.

Appreciate the advantages of using a high-level language.

Understand the problems of using a high-level language for embedded microcontroller applications.

Be able to write a short program in C.

The di culty in coding large programs in a computer’s native language was clearly appreciated within a few years of the introduction of commercial systems. Apart from anything else, computers quickly became obsolete with monotonous regularity, and programs needed to be rewritten for each model introduction. Large applications programs, even at that time, required many thousands of lines of code. Programmers were as rare as hen’s teeth and worth their weight in gold. It was quickly deduced that for computers to be a commercial success, a means had to be found to preserve the investment in scarce programmers’ time. In developing a universal language, independent of the host hardware, the opportunity would be taken to allow the programmer to express the code in a more natural syntax related to problem-solving rather than in terms of memory, registers and flags.

232 The Quintessential PIC Microcontroller

Of course there are many di erent classes of problem tasks which have to be coded, so a large number of languages have been developed since.1 Amongst the first were Fortran (FORmula TRANslation) and COBOL (COmmon Business Oriented Language) in the early 1950s. The former has a syntax that is oriented to scientific problems and the latter to business applications. Despite being around for over 40 years, the inertia of the many millions of lines of code written has made sure that many applications are still written in these antique languages. Other popular languages include Algol (ALGOrithmic Language), BASIC, Pascal, Modula, Ada, C, C++ and Java.

Although writing programs in a high-level language may be easier and more productive for the programmer, the process of translation from the high-level source code to the target machine code is much more complex than the assembly process described in Chapter 8. The translation package for this purpose is called a compiler and the process compilation.

The complexity and cost of a compiler was acceptable on the relatively powerful and extremely expensive mainframe computers of that time. However, until the mid-1980s the use of high-level languages as source code was virtually unknown for MPU-controlled circuitry. In the last decade the easy availability of relatively powerful and cheap personal computers and workstations, capable of running compilers, together with the growing power of MPU/MCU targets and financial importance of this market, is such that the majority of software written for such targets is now in a high-level language.

If you are going to code a task in a high-level language to run in a system with an embedded MCU; for example, a washing-machine controller, then the process is roughly as follows.

1.Take the problem specification and break it up into a series of modules, each with a well-defined task and set of input and output data.

2.Devise a coding to implement the task for each module.

3.Create a source file using an editor in the appropriate high-level syntax.

4.Compile the source file to its assembly-level equivalent.

5.Assemble and link to the machine-code file.

6.Download the machine code to the target’s program memory.

7.Execute, test and debug.

This is virtually identical to the process outlined in Fig. 8.3 on page 211, but with the extra step of compilation. Some compilers go directly from the source file to the machine-code file; however, the extra flexibility of going through the assembly-level phase, as shown in Fig. 9.1, is nearly universal when embedded MPU/MCU circuitry is targeted.

1A popular definition of a computer scientist is one who, when presented with a problem to solve, invents a new language instead!


while(n>0)

Compile

{

sum = sum + n;

--n;

}

(a) First, compile to assembly-level code.

L28 movf _n,f btfsc STATUS,Z

goto

L41

Assemble

movf

_n,f

addwf

_sum,f

btfsc STATUS,C

incf _sum+1,f

decf _n,f goto L28

L41

(b) Second, assemble-link to machine code.

9. High-Level Language 233

L28 movf

_n,f

btfsc

STATUS,Z

goto

L41

movf

_n,f

addwf

_sum,f

btfsc

STATUS,C

incf

_sum+1,f

decf

_n,f

goto

L28

L41

0000100010010011

0001100100000011

0010100000001111

0000100000010011

0000100000010011

0000011110010100

0001100000000011

0000101010010101

0111100000000111

Fig. 9.1 Conversion from high-level source code to machine code.

The choice of a high-level language for embedded targets is crucial. Of major importance is the size of the machine code generated by a high-level language task implementation as compared with the equivalent assembly-level solution. Most embedded MCU circuitry is lean and mean, such as the remote controller for your television. Lean translates to physically small and mean maps to low processing power and memory capacity – and cost! Most low-cost MCUs have a low-capability processor with a few hundred bytes of RAM and a few kilobytes of ROM Program store at best. Thus to be of any use the high-level language and the compiler must generate code, that if not as e cient as assembly-level (low-level), at least is in the same ball park.2

By far the most common high-level language used to source code for embedded MPU/MCU circuitry is C. Historically C was developed as a language for writing operating systems. At its simplest level, an operating system (OS) is a program which makes the detailed hardware operation of the computer’s terminals, such as keyboard and disk organization, invisible to the operator. As such, the writer of an OS must be able to poke about the various registers and memory of the computer’s peripherals and easily integrate with assembly-level driver routines. As conventional high-level languages and their compilers were profligate with resources, depending on a rich and fast environment, assembly language was mandatory up to the early 1970s, giving intimate machine contact and tight fast code. However, the sheer size of such a project means that

2In the author’s experience a code size increase factor of ×1.25 · · · × 2.5 is typical.


234 The Quintessential PIC Microcontroller

Fig. 9.2 Onion skin view of the steps leading to an executable program.

it is likely to be a team e ort, with all the di culties in integrating the code and foibles of several people. A great deal of self-discipline and skill is demanded of such personnel, as is attention to documentation. Even with all this, the final result cannot be easily transplanted to machines with other processors, needing a nearly complete rewrite.

In the early 1970s, Ken Thompson – an employee at Bell Laboratories

– developed the first version of the UNIX operating system. This was written in assembler language for a DEC PDP7 minicomputer. In an attempt to promote the use of this operating system (OS) within the company, some work was done in rewriting UNIX in a high-level language. The language CPL (Combined Programming Language) had been developed jointly by Cambridge and London universities in the mid-1960s, and has some useful attributes for this area of work. BCPL (Basic CPL) was a somewhat less complex but more e cient variant designed as a compiler-writing tool in the late 1960s. The language B (after the first letter in BCPL) was developed for the task of rewriting UNIX for the DEC PDP11 and was essentially BCPL with a di erent syntax.

Both BCPL and B only used one type of object, the natural size machine word – 16 bits for the PDP-11. This typeless structure led to di culties in dealing with individual bytes and floating-point computation. C (the second letter of BCPL) was developed in 1972 to address this problem, by

9. High-Level Language 235

creating a range of objects of both integer and floating-point types. This enhanced its portability and flexibility. UNIX was reworked in C during the summer of 1973, comprising around 10,000 lines of high-level code and 1000 lines at assembly level. It occupied some 30% more storage than the original version.

Although C has been closely associated with UNIX, over the intervening years it has escaped to appear in compilers running under virtually every known OS, from mainframe CPUs down to single-chip MCUs. Furthermore, although originally a systems programming language, it is now used to write applications programs ranging from Computer Aided Design (CAD) packages down to the intelligence behind smart egg-timers!

For over 10 years the o cial definition was the first edition of The C Programming Language, written by the language’s originators Brian W. Kernighan and Dennis M. Ritchie. It is a tribute to the power and simplicity of the language that over the years it has survived virtually intact, resisting the tendency to split into dialects and new versions. In 1983 the American National Standards Institute (ANSI) established the X3J11 committee to provide a modern and comprehensive definition of C to reflect the enhanced role of this language. The resulting definition, known as Standard or ANSII C, was finally approved during 1990.

Apart from its use as the language of choice for embedded MPU/MCU circuits, C (together with its C++ and Java object-oriented o spring) is without doubt the most popular general-purpose programming language at the time of writing. It has been called by its detractors a high-level assembler. However, this closeness of C to assembly-level code, together with the ability to mix code based on both levels in the one program, is of particular benefit for embedded targets.

The main advantages of the use of high-level language as source code for embedded targets are:

It is more productive, in the sense that it takes around the same time to write, test and debug a line of code irrespective of language. By definition, a line of high-level code is equivalent to several lines of assembly code.

Syntax is more oriented to human problem-solving. This improves productivity and accuracy, and makes the code easier to document, debug, maintain and adapt to changing circumstances.

Programs are easier to port to di erent hardware platforms, although they are rarely 100% portable. Thus they are likely to have a longer productive life, being relatively immune to hardware developments.

As such code is relatively hardware-independent, the customer base is considerably larger. This gives an economic impetus to produce extensive support libraries of standard functions, such as mathematical and communication modules, which can be reused in many projects.


236 The Quintessential PIC Microcontroller

Of course there are disadvantages as well, specifically when code is being produced to run in poorly resourced MPU/MCU-based circuitry.

The code produced is less space-e cient and often runs more slowly than native assembly code.

The compiler is much more expensive than an assembler. A professional product will often cost several thousand pounds/dollars.

Debugging can be di cult, as the actual code executed by the target processor is the generated assembler code. The processor does not execute high-level code directly. Products that facilitate high-level debugging are, again, very expensive.

Program 9.1 is an example of a C function (a function is C’s counterpart to a subroutine) that evaluates the relationship:

n

sum = k

k=1

for example, if n = 5 then we have:

sum = 5 + 4 + 3 + 2 + 1

In the implementation n is the integer passed to the function, which computes and returns the integer sum as defined. The program implements this task by continually adding n to the pre-cleared sum, as n is decremented to zero.

Let us dissect it line by line. Each line is labelled with its number. This is for clarity in our discussion and is not part of the program.

Line 1: This line names the function (subroutine) summation and declares that it returns an unsigned long integer (a 16-bit unsigned object in the compiler used to illustrate this chapter) and expects an unsigned integer (a 8-bit unsigned object) to be passed to it called n.

Line 2: A left brace { means begin. All begins must be matched by an end, which is designated by a right brace }. It is good practice

Program 9.1 A simple function coded in C.

1:unsigned long summation(unsigned int n)

2:{

3:unsigned long sum = 0;

4:while(n>0)

5:{

6:sum = sum + n;

7:--n;

8:}

9:return sum;

10:}