|
3
Gordon Drive, P.O.Box 1347 Rockland, Maine 04841 U.S.A.
|
|
© 2004 Avocet Systems, Inc.
|
Call
Us Today at 207-596-0080
|
|
Avocet Systems, Inc. : The Complete Solution for Embedded Systems
Development Tools
|
|
|
Tech
Tips
David Bardon
I have your 68HC05 compiler and I wrote some code using character arrays. The
code wasn't working right so I rewrote some of it using character pointers.
There must be something wrong with the compiler because the assembly generated
was different from the code generated using arrays.
The code should be different by reason of the difference between a character
array and a pointer to a character.
Q But they're the same! I've been using them interchangeably for years!
A It is true that they are often handled the same way in writing code, but internally
they are handled differently by most compilers. You can prove to yourself that
they are different by attempting to compile the following code with any decent
compiler:
char ch_array[20];
char *pc;
main()
{
ch_array = pc;
pc = ch_array;
}
You will always get an error message on the first assignment and no message
on the second.
Q Why is that?
A An array name represents the address at which the array starts, or to look
at it another way, the address of the first element. This address is fixed for
the lifetime of the array. The pointer, on the other hand, is a variable which
holds an address, and that can be changed. You can, of course, assign values
to array elements but the address range of the array itself remains the same.
How is it that I can pass an array name to a function prototyped to take a character
pointer as an argument, with no complaint from the compiler?
A What really happens is that the address of the array is assigned to the character
pointer argument. You are really making an implicit typecast of an array name
to a character pointer. With some compilers, if the warning sensitivity is cranked
up, you might get a message that you are effecting a type conversion. If we
continue our example code by adding a function call where we pass the array
name to a function:
void fn(char*);
charch_array[20];
char*pc;
main()
{
.
fn(ch_array);
.
then the code generated is:
lda #high _ch_array
sta ?_fn
lda #low _ch_array
sta ?_fn+1
bsr _fn
The symbol _ch_array is the assembly code label marking the address where the
array starts; i.e. its address. Byte by byte, you can see that this value is
loaded via the accumulator into that part of the compiled stack used by the
function (the start address of such a space is marked by ?_ followed by the
function name).
If on the other hand, we write the function call:
fn(pc);
then we get:
lda _pc
sta ?_fn
lda _pc+1
sta ?_fn+1
bsr _fn
Here you can see that the data residing at the address, rather than the address
itself is loaded; i.e. the memory contents at _pc; in other words, what it points
to.
Using your compiler for the 68HC05, tell me, how do you program the options
register?
It depends on the type of 6805 you have. Certainly with many of the earlier
processors, the register was implemented entirely in ROM, and was often referred
to ask the Mask Option Register. I don't know if any current 6805 processors
are implemented that way. I certainly haven't noticed any in any recent list
I've seen.
Well, we haven't decided exactly which 6805 type to use yet, so, just to make
sure we have all bases covered, how would you do it for a ROM implemented device.
To do this we have to make the compiler produce a hex file which contains a
byte with data we need at the address of the Mask Options Register. Let us take
the MC68705P3 by way of an example, no matter whether this particular one is
still current or not! The Mask Option register for this processor resides at
$784. We could use the #asm and #endasm directives to insert a piece of assembly
code into any function in the following way:
#asm
psect optreg,abs
org $784
fcb $42
psect text
#endasm
You will typically find a hex record generated with the load address specified
in our org statement, with the single data byte defined. On burning into the
processor's (E)PROM, the appropriate data is then set in the Mask Option Register.
Fine, and for more recent devices?
A For a modern device most of the control bits are implemented as RAM. As an
example, let's take the MC68HC05C8. The Options Register in this processor is
located at $1FDF. Bits 7, 6 and 1 are RAM implemented, although bit 1, designated
IRQ, can only be written to once following each reset. Suppose we wish to set
bit 7 to 1. When this bit, designated RAM0, is set, we have 32 bytes of RAM
in the address range $30 to $4F.
We start by defining a bit-field type for the options
register and then defining a name to use in accessing its hardware address:
typedef struct
{
unsigned RAM0:1; /* bit 7 */
unsigned RAM1:1;
unsigned unused1:2;
unsigned SEC:1;
unsigned unused2:1;
unsigned IRQ:1;
unsigned unused3:1; /* bit 0 */
} OPTREGTYPE;
OPTREGTYPE optreg @ 0x1FDF;
We can then set or clear control bits typically as follows:
optreg.RAM0=1;
optreg.IRQ=0;
|
|
|