Файл: 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 |
|