Файл: Introduction to Microcontrollers. Architecture, Programming, and Interfacing of the Motorola 68HC12 (G.J. Lipovski, 1999).pdf

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

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

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

Добавлен: 14.06.2025

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

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

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

248

Chapter 8 Programming in C and C++

a. Write a C procedure encode ( )

to convert the ASCII string to Huffman code, as

defined by the coding tree in Figure 8.5a, storing the code as a bit string, first bit as most significant bit of first element of int code [16];.

b. Write a C procedure decode ( ) that decodes such a code in int code [ 16], using the coding tree in Figure 8.5a, putting the ASCII string back as it was in char string[4Q].

9. Repeat Problem 8 for the Huffman coding tree in Figure 8.5b.

10. Write an initialization and four shortest C procedures void pstop( int) push to

top, int pltop() pull from top, psbot(int) push to bottom, int

plbot( ) pull

from bottom, of a ten-element 16-bit word deque. The deque's

buffer is int

deque [ 10]. Use global int pointers, top and bottom. Use global char varaibles for the size of the deque, size, and error flag errors which is to remain cleared if there are no errors and to be 1 if there are underflow or overflow errors. Note that C always initializes global variables to zero if not otherwise initialized. The procedures should manage the deque correctly as long as errors is zero. Procedures pstop() and psbot ( ) pass by value, andprocedures pltop() and plbot ( ) pass byresult.

11. Write a C procedure get (char *a, int

i), whose body consists entirely of

embedded assembly language, which moves i

bytes following address a into a char

global vector v, assuming v has a dimension larger than or equal to i. To achieve speed, use the MOVE and DBNE instructions. The call to this procedure, get (s, n ) , is implemented:

Idx s pshx

Idx n jsr get leas 4,sp

12. Write a shortest C procedure hexString(unsigned int n, char *s) that runs in a target machine to convert an unsigned integer n into printable characters in s that represent it in hexadecimal so that sfOJ is the ASCII code for the 1000's hex digit, s[l] is the code for the IQO's hex digit, and so on. Suppress leading Osby replacing them with blanks.

13. Write the shortest procedure int inhex() in C to input a four-digit hexadecimal number from the keyboard (the letters A through F may be upper or lower case; typing any character other than 0...9, a...f, A...F, or entering more than four hexadecimal digits terminates the input and starts the conversion) and convert it to a binary number, returning the converted binary number as an unsigned int. Although you do not have to use a compiler and target machine to answer this problem, you can use it without penalty, and it may help you get error-free results faster.


PROBLEMS

251

invert an interval containing 0 or get the square root of an interval containing

a negtive

value. Finally, write a main() procedure that will initialize intervals

a to <1,2>, b to

<3,4>, and c to <5,6>, and then evaluate the result of the expression (

-b +

sqrt ( b

* b - 4 * a * c ) ) / (a + a).


9

Implementation of C Procedures

This chapter is perhaps the most important chapter in this book. We show how the 6812 assembly language implements C expressions and statements. We will use the HiWare C++ compiler in our examples. While different compilers will generate different code for the same C statement, studying one such implementation prepares you well to understand other implementations.

We first discuss how C allocates and accesses global and local variables. Then we consider how variables of different types are correctly coded in expressions and assignment statements. Next we discuss the implementation of conditional statements. We then describe how arrays and structs are accessed and then how loops are executed. Finally we discuss procedure calls and arguments, and we present our conclusions.

After you study this chapter, you will be able to read the assembly-language output of a C compiler with ease. One of the incidental benefits of this chapter is that you will see how to implement many operations in assembly language, by reading a "definition" of the problem to be solved in a C expression or statement and seeing the "solution" to the problem in assembly language. You will also be able to write C code that produces more efficient assembly-language code. As a further benefit, you will be able to fine-tune a C procedure by replacing parts of it with assembly-language code that can be embedded in the C procedure. Also, you will learn that you can write a C procedure that you can debug on a personal computer and hand-compile it into an assembly-language program. The C source program statements can be written in assembly-language comments to document your assembly-language program. This is one way to quickly write complex assembly-language programs.

This is therefore a very interesting chapter to complete the earlier chapters. You will really understand how hardware, which we showed in Chapters 1 to 3 implemented the 6812 instruction set, becomes a powerful machine that executes C and C++ procedures, in which you can express complex algorithms. You should be comfortable writing in a high-level language like C or C++, knowing what really happens, right down to the machine level, whenever you write an expression in your program.

We point out that the examples in this chapter are generated by a specific version (5.0.8) of the HiWare C++ compiler, with selected optimization options. You can expect to get slightly different code using different compilers, versions, or optimization options.

253


9.2 Expressions and Assignment Statements

257

The input and output statements use page-zero addressing, which provides improved static and dynamic efficiency over direct adddressing. Note that the MOVB instruction is not useful for accessing these I/O ports, because there is no page-zero address option in MOVB. The LDAB and STAB instructions above are more efficient than a MOVB instruction.

The assignment of I/O ports to global variable names should be written and executed before true global variables are assigned, because the origin will be set to the beginning of RAM (at $800) to assign true global variables. The declaration of globally defined I/O ports is often put in an #include file, which is inserted in a program before globals are defined in the program.

9.2 Expressions and Assignment Statements

In this section, we illustrate how operators are used in expressions. We will look at addition and subtraction statements that use same-width and different-width operands in a discussion of upcasting and downcasting. We will then study statements that use logical and arithmetic operators. We will carefully consider the increment and decrement operators and then look at expressions that save temporary results on the hardware stack.

The program in Figure 9.2a has several local and global variables, some of which are signed and others of which are unsigned, and some of which are 8-bit and others of which are 16-bit. Figure 9.2b shows assembly language developed from this program. Observe that each variable's name is an abbreviation of its characteristics; gsi is a global signed integer.

Many C statements are easily and efficiently translated into assembly language. This

is especially true when all the variables

in a statement are 8-bit char or unsigned

char

variables or when all the variables in a statement are 16-bit int or unsigned

int

variables. Assume the following statements

are written in Figure 9.2a's main.

Figure 9,2's statement

gsi

= lui + 12;

is easily encoded as

LDX

0, SP

; get 16-bit local variable

lui

LEAX

12, X

; add 12 (note that this is shorter than addd #12)

STX

$0801

; put into 16-bit global variable gsi

and similarly the statement

guc

= Isc

- 33;

is simply encoded as

LDAB

2, SP

; get 8-bit local variable Isc

SUBB

#33

; subtract 33

STAB

$0800

; put into 8-bit global variable guc

If a statement gets an int

variable and writes a char variable, the source is truncated

when it is read. Figure 9.2's statement guc

= lui

+ 9;

is encoded as

LDAB

1, SP

; get low byte of 16-bit local variable lui

ADDB

#9

; add 9

STAB $0800

; put into 8-bit global variable guc

An optimizing compiler can change the instruction ADDD #9

to ADDB #9

because the result will not be altered (reducing the precision is called

downcasting).


9.2

Expressions and Assignment Statements

259

If a statement gets a char variable and writes an int or unsigned

int

variable,

the result is sign extended when it is read. Figure 9.2's statement gsi =

gsi

+ Isc;

or equivalently gsi

+= isc ,•is simply encoded as

LDAA

2, SP

; get 8-bit global variable Isc

SEX

A, D

; upcast from char to int or unsigned

int

ADDD

$08 01

; add in 16-bit global variable gsi

STD

$0801

; put into 16-bit global variable

gsi

But if a statement gets an unsigned char variable and writes an int

or unsigned

int

variable, the high byte is cleared before the unsigned

char variable is read.

Figure 9.2's statement lui =

guc - 17; is encoded as

LDAB $0800

; get 8-bit global variable guc saved earlier

CLRA

; upcast from unsigned char to int

or unsigned

int

SUED

#17

; subtract 17

STD

0, SP

; put into 16-bit global variable lui

You should observe that the declaration char or int affects the instruction data length, and char and unsigned char determine whether, on upcasting, the 8-bit data is sign extended with an SEX instruction or filled with zeros using a CLRA instruction.

The previous examples should indicate to the C programmer how to decide how a

variable is

to be type cast. If its range of values is 0 to 127, declare it to

be an

unsigned

char, because upcasting is done with a short CLRA instruction rather than a

longer SEX

instruction. If its range is 0 to 256, declare it to be an unsigned

char,

but if the range is -128 to 127, declare it a char, to save space and time. Otherwise declare it to be int.

To discuss how common operators are handled, we use the following main as an example; it merely ANDs, ORs, multiplies, and divides some variables. Figure 9.3a's program is compiled into the assembly-language program in Figure 9.3b.

Logical bit-by-bit ANDing is illustrated in Figure 9.3 by the expression Isc = lsc& guc; or equivalentlyby Isc &= guc;, which is realized by

LDAA

2, SP

; get local variable Isc

ANDA $0800

; AND with global variable guc

STAA

2, SP

; put into local variable Isc

However, if one of the operands is constant, the BCLR instruction can be used. The expression in Figure 9.3, Isc = lsc& 0x12;,or equivalently Isc &= 0x12; is realized by

BCLR 2, SP, #23 7 ; ANDlocal variable Isc with inverted constant 0x12

Note that the complement of the constant is used in the operand of BCLR. Logical bit-

by-bit ORing is illustrated

in Figure 9.3 by the expression gsi - gsi | lui; or

equivalently bygsi |= lui;, which isrealizedby

LDD

$0801

; get global variable gsi

ORAA

1, SP

; OR with high byte of local variable

lui

ORAB

0, SP

; OR with low byte of local variable

lui

STD

$0801

; put into global variable gsi