Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C Programming for the 68HC05 Microcontroller


AUG91: C PROGRAMMING FOR THE 68HC05 MICROCONTROLLER

Ted is an applications engineer in Motorola's Semiconductor Products Sector. You can reach him c/o Motorola Inc., 12254 Hancock St., Carmel, IN 46032.


Whenever the topic of high-level languages and microcontrollers comes up, the response is usually something like, "The microcontroller doesn't have enough RAM," or "There's never enough ROM," or "Compilers don't create the tight code needed for microcontroller operation." Of course, these statements most often come from assembly language programmers who believe that compiler writers are more concerned with compilers than with the final performance of the code generated by the compiler.

Nevertheless, there are advantages to using a high-level language and compiler for programming microcontrollers. One is comprehensibility. Code written in a high-level language has a format derived from the problem being solved. The program comprises a series of statements, each statement solving a small portion of the problem without concern for the computer. Almost anyone with some understanding of the problem can examine a high-level language program that executes the problem and understand the intent of the program. This is unlike assembly language, where code written by one assembly language programmer often cannot be understood by another.

Furthermore, high-level programs can usually be written faster than assembly language programs because high-level language programmers work with the problem, focusing their efforts on solving the problem. They don't need to worry about the available computer resources required to resolve the problem. The compiler writer, on the other hand, can use all available computer resources properly in the implementation of any program.

Portability is another advantage of high-level languages. Different machines have completely dissimilar assembly languages. Even machines within the same family of parts have differing assembly-level features. Compiler writers must take great care to mask these differences so that the language will be the same from machine to machine. Programs written in a high-level language for one part in a family of parts should require little rework to be moved to another family member.

In this article, I'll discuss high-level microcontroller programming, using Motorola's 68HC05 and Byte Craft's C6805 C compiler. As an example, I'll add time-of-day functionality to two members of the 68HC05 family, the MC68HC05J1 and MC68HC05C8.

Internal Time-of-Day Clock for the MC68HC05J1

The MC68HC05J1 is the simplest of the HC05 family of parts. Its clock is primitive, and the time intervals at which a periodic interrupt can be generated are not very flexible.

C code for the clock portion of such a system is shown in Listing Two (page 128). Two header files are included: HC05J1.H, which contains the component-specific pragmas and the definitions of all bits in the timer control status register; and GENERAL.H, which contains several macro definitions that are useful in writing code. For example, one of the macros in this file, #define FOREVER while(TRUE), is used to create a loop that executes forever in the main program.

All of the variables used for this program are declared as global. Inside the main program, several initialization statements are executed, and the interrupts on the part are enabled. The comments in Listing One (page 128) explain these statements. Following the initialization is a FOREVER statement, a loop that executes while the microcontroller is running.

Inside this loop, the variables sec, mts, and hrs are tested, incremented, and set. The value of sec is incremented in the interrupt service routine __TIMER(). In the main loop, sec is tested to determine if it is less than 60. When it is equal to 60, sec is reset to 0 and mts is incremented and tested to determine if its incremented value is 60. When mts is 60, it is reset to 0, and hrs is incremented. When the incremented value of hrs is 13, hrs is reset to 1.

This simple clock is followed by a WAIT() statement which places the MC68HC05J1 into the wait mode until another interrupt occurs. The variable locations hrs, mts, and sec contain the current time: Other routines are required to display the time on an external device or set the time with some type of push-button arrangement.

The timer is set up to cause a timer interrupt to occur at 8.192-millisecond intervals. When the interrupt occurs, the Timer Overflow Flag and the Real-Time Interrupt (RTI) Flag are both reset. Because the interrupt time interval is fixed at a rather odd value, a simple integer count of the number of interrupts will not provide an accurate one-second interval. A count of 122 interrupts is about one second. The error is large enough that it must be corrected if this unit is to be used as a clock. The correction algorithm is as follows:

  1. Count 122 8.192-ms ticks per second for 13 seconds. On the 14th second count 123 ticks. This routine provides 14.000128 seconds per indicated 14-second period.
  2. Repeat the above cycle 79 times and on the 80th cycle use a cycle of 14 seconds with 122 ticks in each second. The elapsed interval of this sequence is 1120.002048 seconds with an indicated time of 1120 seconds.
  3. Finally, repeat cycle 2 three times, and on the fourth cycle drop one 8.192-ms tick to provide an indicated and elapsed time of exactly 4480 seconds.

The variables corr1, corr2, and corr3 are used to keep track of the cycles just mentioned. The C code to implement this correction scheme is contained in the timer interrupt service routine of Listing One.

The compiled version of this code is shown in Listing Three (page 128) where the files HC05J1.H and GENERAL.H are expanded. The global variables are placed in the RAM memory beginning at the address 0xc0. The first compound statement in main() is compiled as three CLR memory instructions, and the initialization of the timer control status register is accomplished by 5-bit clear or set instructions. The interrupts are turned on with the CLI() instruction.

The code to implement the FOREVER loop is the branch at the address 0x330 that returns the execution to the address 0x311. Thirty-one bytes of code are used to execute the complete clock operation in the main() program. The interrupt service routine follows the main program and requires 70 bytes of code.

Assembly language programmers should examine this code carefully and determine if they could do any better than the compiler has done here. My immediate reaction was that the bit manipulation instructions could have been replaced by byte operations that require less code. On reflection, however, I concluded that the bit manipulations were coded in C by me, and I could easily have used byte-type operations and saved the same amount of code space within the C program. Otherwise, the single RTS instruction at the end of the main program is the only wasted byte in the program.

Internal Time-of-Day Clock for the MC68HC05C8

As the C program in Listing Four (page 134) illustrates, the timer of the MC68HC05C8 is much more flexible than that of the MC68HC05J1. While the executing portion of this program is a few lines shorter than the equivalent program for the MC68HC05J1, the setup portion of the program is significantly longer because the MC68HC05C8 is a much bigger part.

More Details.

Examine the code in Listing Four. The MC68HC05C8 has several registers that are 16 bits. The machine must handle these registers as two 8-bit registers. The three declarations at the beginning of this program provide a foolproof means of dealing with the two 8-bit parts of a 16-bit register. The first declaration is that of a structure, as shown in Example 1(a), page 74, and the second is for a union, as in Example 1(b). A union is compiled to provide enough space for the storage of the largest element in its argument list. In this case, the union contains two 16-bit items, so the declaration in Example 1(c) provides 16 bits of storage. It is possible to deal with the 16-bit location as either a long or 2 bytes, and there is no question as to where the bytes will be stored. Note that in the interrupt service routine __TIMER(), this union is used to move the contents of the timer count register into memory with two 1-byte moves, and then 500 is added to the long word of the union.

Example 1: A union is compiled to provide enough space for the storage of the largest element in its argument list. (a) The first declaration is that of a structure; (b) the second is for a union; (c) the union contains two 16-bit items, so the declaration provides 16 bits of storage.

  (a) struct bothbytes
      {
           int hi;
           int lo;
      }

  (b) union isboth
      {
           long l;
           struct bothbytes b;
      }

  (c) union isboth time_comp_count;

The main() routine of this program has a different setup because it is necessary only to enable the output compare interrupt and the interrupts for the processor. The remainder of the main program is identical to that of the earlier version for the MC68HC05J1.

The timer interrupt service routine is significantly different in this program. After an interrupt occurs, it is necessary to clear the timer overflow and the output compare flag bits in the timer status register. The timer overflow bit is cleared by reading the timer status register prior to reading the low byte of the timer count register. The output compare flag is reset by writing to the output compare low byte after reading the timer status register. These operations are accomplished in the first seven lines of code in the interrupt service routine. Also, in this portion of the code, the contents of the timer compare register are incremented by 500 to prepare for the next interrupt time. This processor -- when running with a 4-MHz oscillator -- will have the internal clock increment every two microseconds. Therefore, adding 500 to the output compare register will cause the processor to be interrupted by the timer once every millisecond.

The remainder of the interrupt service routine is quite simple. The interrupt service routine is entered once each millisecond. Therefore, when the value of count which is initialized to 1000 is decremented to 0, exactly one second has passed. If the decremented value is not count, then the program returns to the main program. When the decremented value is zero, the location sec is incremented, and count is reset to 1000. Remember that sec is processed in the main program loop so nothing more is needed in the interrupt service routine.

In the compiled listing version of Listing Four, the file HC05C8.H is much longer than the corresponding file for the MC68HC05J1. (Due to space constraints the compiled version is not shown here, but it is available electronically; see "Availability," page 3.) The MC68HC05C8 has many more registers than the MC68HC05J1, and the individual bits within these registers are each named in the HC05C8.H file.

The declaration of bothbytes and isboth does not cause memory allocation. The declaration of the union time_comp_count causes the allocation of the memory. With the exception of the initialization, the compiled version is functionally the same as that shown in Listing Three. The interrupt service routine requires 56 bytes in this case. There are two returns from the interrupt service routine, and in both cases the compiler inserted an RTI instruction.

Summary

The C6805 compiler was written for a microcontroller that has many limitations when compared with the typical computer. All the unique features of the microcontroller can be placed in a header file; inclusion of this header in the program will assure that the proper features of the microcontroller will be made available to the compiler. C6805 adheres to the ANSI standard for the C language as far as is practical when the computer is considered.

Products Mentioned

C6805 Code Development System Byte Craft Limited 421 King Street North Waterloo, Ontario N2J 4E4 Canada 519-888-6911

C6805 Specifics

The C6805 compiler was written to support the MC68HC05 family of parts. Because some MC68HC05 microcontroller instructions have no counterpart in C, special directives identify unique microcontroller characteristics to the compiler.

Table 1 lists nine assembly instructions available to the 68HC05 that have no equivalent C call. They can be accessed as either a single instruction (all uppercase) or as a function call, as shown. The function call requires a pair of closed parentheses to follow the name of the instruction.

Table 1: Assembly codes directly callable by C6805

  Function           Operation
  ------------------------------------------------------------

  CLC or CLC()       Clear Carry Bit
  SEC or SEC()       Set Carry Bit
  CLI or CLI()       Clear Interrupt Flag (turn interrupts on)
  SEI or SEI()       Set Interrupt Flag (turn interrupts off)
  NOP or NOP()       No Operation
  RSP or RSP()       Reset Stack Pointer
  STOP or STOP()     STOP Instruction
  SWI or SWI()       Software Interrupt
  WAIT or WAIT()     WAIT Instruction

A pragma is a C preprocessor command not defined by the language. As such, the compiler writer can use the #pragma command to satisfy a need not specifically identified by the langua e. C6805 uses pragmas to identify microcontroller-specific characteristics. Table 2 contains a list of pragmas used by C6805.

Table 2: C6805 pragma directives

  pragma             Function
  -----------------------------------------------

  #pragma portxy     I/O port definition
  #pragma memory     RAM/ROM definition
  #pragma mor        Mask Option Register
  #pragma has        Instruction set options
  #pragma options    Compiler directives
  #pragma vector     Interrupt vector definitions

Listing One (page 128) shows part of the file HC05J1.H, a header file used by C6805 when compiling a file for the MC68HC05J1. The first six entries in this file define the fixed port locations used with this part. The format of a pragma directive here is #pragma portxx portname @ address where portxx can be portr, portw, or portrw, which shows whether the port is read, write, or both. portname is the name used in the program for the port. The at symbol (@) identifies a memory address. The first line in Listing One tells the compiler that porta is a read/write port located at address 0x0 in the computer memory space. Note that the timer_count register at memory location 0x9 is a read-only register.

The location and amounts of RAM and ROM are identified in the two memory directives. This part has a STOP, a WAIT, and a MUL instruction. Finally, the vector entries identify the locations of all interrupt vectors and the names of the interrupt service routines associated with each vector. A vector pragma causes the address of the function with the given name to be placed in the specified vector location.

The header files that contain the pragmas for each microcomputer are not part of the compiler and must be written by the programmer. Programmers will want to identify in the header file each of the bits in the fixed registers, such as the timer control register. That way, the main program will not be cluttered with defines more than necessary.

--T.V.S

Knowing Your Microprocessor Reduces Code Size and Execution Speed

One of the major advantages of high-level languages is that they isolate the programmer from the target environment. The application can be abstracted to a target-independent description; that is, the programmer describes the application algorithmically and lets the high-level language compiler do its best job of implementing it on a particular target. Little has to be done to change the application from one target to another.

For smaller code size and better execution speed, the design engineer can use fixed arrays rather than pointer arithmetic, allocate variables to Page 0 for shorter instructions, and keep control statements short for branches.

High-level language compilers usually generate highly optimized code. Where can design engineers gain their next advantage? From the processor. By knowing the strengths and weaknesses of the target processor, the design engineer creates better implementations, even using good tools.

This is true of all processors, but especially the 8-bit ones. Experience shows, even with an extremely good C compiler, there are still ways the programmer can influence both code size and execution speed as evidenced by the following three code examples.

Array Vs. Pointer Arithmetic

Consider for a moment the differences between pointer and array arithmetic. An array access consists of a base address indexed by a constant or variable. Pointer access is done by a variable indexed by a constant or variable. In the 6805 instruction set, 56 percent of the opcode map is dedicated to accessing a variable, yet only 12 percent of the opcode map can be used to access an array indexed by a constant or variable. The 6805 has no way to use a 16-bit variable to access memory without doing some form of self-modifying code. Knowing this, the developer using fixed arrays rather than pointer arithmetic for the storage and retrieval of data will get better code from the compiler

Example 1 shows the differences between the two access methods. In an array, data is accessed first by an array access and secondly by a pointer. The lack of a 16-bit index register causes the compiler to generate considerably longer code for the pointer access.

Example 1: Differences between accessing data with pointers or arrays on the 6805

  0010 0011                      unsigned int i,j;
  0C1C 0014                      char a[20];
  0029                           int *ptr;

  0100 BE 11      LDX   $11      i = a[j];   /* Getting data from an
  0102 D6 OC  1C  LDA   $0C1C,X                 array in the 6805 */
  0105 B7 10      STA   $10

  0107 A6 0C      LDA   #$0C     ptr = &a;   /* Setting a pointer to
  0109 B7 29      STA   $29                     the beginning of the
  010B A6 1C      LDA   #$1C                    array */
  010D B7 2A      STA   $2A

  010F BB 12      ADD   $12      i = *(ptr + j);
  0111 B7 19      STA   $19                  /* Accessing the array
  0113 B6 29      LDA   $29                     with pointers using
  0115 A9 00      ADC   #$00                    self modifying code
  0117 B7 18      STA   $18                     to handle 16 bit
  0119 AE C6      LDX   #$C6                    pointers.  */
  011B BF 17      STX   $17
  011D AE 81      LDX   #$81
  011F BF 1A      STX   $1A
  0121 BD 17      JSR   $17
  0123 B7 10      STA   $10

Page 0 Variables

The 6805, like many single-chip microprocessors has dedicated some of its instructions to accessing data in the first 256 locations of memory. In the 6805, this area of memory is dedicated to memory-mapped I/O ports, scratch-pad RAM, and often some ROM. Almost half the 6805 instruction set is Page 0 and accesses this area directly. Good compilers know where the target data of its instructions are and emit appropriate code. The programmer can allocate frequently used variables in the first 256 bytes of memory to substantial advantage. Example 2 shows two syntactically identical code fragments generating 7 bytes or 2 bytes depending on variable location.

Example 2: Syntactically identical code for two variables generates tighter code in the first 256 bytes of memory.

  0125 C6 0B B8  LDA  $0BB8   y = -y;
  0128 40        NEGA
  0129 C7 0B B8  STA  $0BB8

  012C 30 14     NEG  $14     x = -x;

Branching

The instruction set in the 6805 restricts conditional branch offsets to a single byte (129-bytes forward and 126-bytes back from the branch instruction). To conditionally branch beyond this range, a branch with the complement condition branches around a jump instruction. This requires 5-bytes of generated code and on average increases the execution speed. By keeping control statements (if, while, and for) short conditional branches will be generated with 2 bytes of code. This is only a 3-byte savings but over a large program it can be substantial. See Example 3.

Example 3: For branches beyond 128 bytes, the 6805 C compiler reduces 5 bytes of code to 2 bytes.

  012E B6 11     LDA  $11     if (j < 25) {
  0130 A1 19     CMP  #$19
  0132 24 01     BCC  $0135
  0134 9D        NOP              NOP (); }

  0135 B6 10     LDA  $10     if (i < 25) {
  0137 A1 19     CMP  #$19
  0139 25 03     BCS  $013E
  013B CC 01 D7  JMP  $01D7
  013E 9D        NOP              NOP (); NOP ();
  013F 9D        NOP
                              . . . . . .
  01D4 9D        NOP
  01D5 9D        NOP              NOP (); NOP (); }
  01D6 9D        NOP
  01D7 81        RTS          }

Walter is president of Byte Craft Limited and has been programming embedded systems on single-chip microcomputers for over 20 years. He can be reached at 421 King Street North, Waterloo, Ontario N2J 4E4 Canada.


_C PROGRAMMING FOR THE 68HC05 MICROCONTROLLER_
by Truman T. Van Sickle



[LISTING ONE]
<a name="01cf_0016">

#pragma portrw PORTA @ 0x00;
#pragma portrw PORTA @ 0x01;
#pragma portrw DDRA  @ 0x04;
#pragma portrw DDRB  @ 0x05;
#pragma portrw TCST   @ 0x08;
#pragma portr  TCNT  @ 0x09;

#pragma memory RAMPAGE0 [64] @ 0xc0;
#pragma memory ROMPROG [1024] @ 0x300;

#pragma has STOP ;
#pragma has WAIT ;
#pragma has MUL ;

#pragma vector __TIMER @ 0x07f8;
#pragma vector __IRQ @ 0x07fa;
#pragma vector __SWI @ 0x07fc ;
#pragma vector __RESET @ 0x07fe;




<a name="01cf_0017">
<a name="01cf_0018">
[LISTING TWO]
<a name="01cf_0018">

#pragma option v
#include "hc05j1.h"
#include "general.h"

/* define the global variables  */
int hrs,mts,sec;
int count;
int corr1,corr2,corr3;       /* used to correct the time errors  */

main(void)
{
    corr1=corr2=corr3=0;     /* time corrections */
    TCST.RT0=0;   /* 57.3 ms cop timer */
    TCST.RT1=0;   /* 8.192 ms RTI */
    TCST.RTIE=1;  /* Turn on the RTI */
    TCST.RTIF=0;  /* Reset interruput */
    TCST.TOF=0;   /* flags */
    CLI();                   /* turn on interrupt */
    FOREVER
    {
        if(sec==60)  /* do clock things each minute  */
        {
            sec=0;
            if(++mts==60)
            {
                mts=0;
                if(++hrs==13)
                    hrs=1;
            }
        }
        WAIT();   /* wait here to save the energy  */
    }
}
void __TIMER(void)   /* routine executed every RTI (8.192 ms) */
{
    TCST.TOF=0;  /* reset the interrupt */
    TCST.RTIF=0; /* flags  */
    if (++count==122)
    {
       sec++;               /* increment seconds */
       if(++corr1==14)      /* To correct for 8.192 ms per tick */
       {
           corr1=0;         /* run 122 ticks per second for 13  */
           if(++corr2==80)  /* seconds, and 123 for the 14th second */
           {                /* With this algorithm there are 14.000128 */
                   corr2=0; /* actual seconds per 14 indicated. Then run */
                   if(++corr3==4)
                {
                   count=1;
                   corr3==0;
                }
                else
                    count=0;/* 79 of these cycles followed by 1 cycle of */
            }               /* 14 seconds with 122 ticks per second. The */
            else            /* elapsed time for this cycle = 1120.002048 */
               count=(-1);  /* seconds for and indicated time of 1120 */
       }                        /* seconds. Repeat this cycle 4 times; on */
       else                     /* last cycle drop 1 tick makes indicated &
           count=0;             /* elapsed time exactly 4480 seconds.*/
   }
}




<a name="01cf_0019">
<a name="01cf_001a">
[LISTING THREE]
<a name="01cf_001a">

                                  #pragma option v
                                  #include "hc05j1.h"
0000                              #pragma portrw PORTA @ 0x00;
0001                              #pragma portrw PORTB @ 0x01;
0003                              #pragma portr  PORTD @ 0x03;
0004                              #pragma portrw DDRA  @ 0x04;
0005                              #pragma portrw DDRB  @ 0x05;
0008                              #pragma portrw TCST  @ 0x08;
0009                              #pragma portrw TCNT  @ 0x09;
07F0                              #pragma portrw __COPSVS @ 0x7f0;
07F8                              #pragma vector __TIMER @ 0x07f8;
07FA                              #pragma vector __IRQ @ 0x07fa;
07FC                              #pragma vector __SWI @ 0x07fc ;
07FE                              #pragma vector __RESET @ 0x07fe;
                                  #pragma has STOP ;
                                  #pragma has WAIT ;
                                  #pragma has MUL ;
00C0 0040                         #pragma memory RAMPAGE0 [64] @ 0xc0;
0300 0400                         #pragma memory ROMPROG [1024] @ 0x300;
0000                              #define RT0         0  /* timer_cont_stat */
0001                              #define RT1         1
0004                              #define RTIE        4
0005                              #define TOFE        5
0006                              #define RTIF        6
0007                              #define TOF         7
                                  #include "general.h"
0001                              #define TRUE        1
0000                              #define FALSE       0
0001                              #define FOREVER     while(TRUE)
0002                              #define max(a,b)    (a) > (b) ? (a) : (b)
0003                              #define min(a,b)    (a) < (b) ? (a) : (b)
0004                              #define abs(a)      (a) >= 0 ? (a) : -(a)
                                  /* define the global variables  */
00C0 00C1 00C2                    int hrs,mts,sec;
00C3                              int count;
00C4 00C5 00C6                    int corr1,corr2,corr3; /* time corrections */
                                  main(void)
                                  {
0300 3F C6     CLR    $C6           corr1=corr2=corr3=0; /* time corrections */
0302 3F C5     CLR    $C5
0304 3F C4     CLR    $C4
0306 11 08     BCLR  0,$08            TCST.RT0=0;   /* 57.3 ms cop timer */
0308 13 08     BCLR  1,$08            TCST.RT1=0;   /* 8.192 ms RTI */
030A 18 08     BSET  4,$08            TCST.RTIE=1;  /* Turn on the RTI */
030C 1D 08     BCLR  6,$08            TCST.RTIF=0;  /* Reset interruput */
030E 1F 08     BCLR  7,$08            TCST.TOF=0;   /* flags */
0310 9A        CLI                    CLI();        /* turn on interrupt */
                                      FOREVER
                                      {
0311 B6 C2     LDA    $C2           if(sec==60)  /* do clock things */
0313 A1 3C     CMP    #$3C
0315 26 18     BNE    $032F
0317 3F C2     CLR    $C2                   sec=0;
0319 3C C1     INC    $C1                   if(++mts==60)
031B B6 C1     LDA    $C1
031D A1 3C     CMP    #$3C
031F 26 0E     BNE    $032F
                                            {
0321 3F C1     CLR    $C1                       mts=0;
0323 3C C0     INC    $C0                       if(++hrs==13)
0325 B6 C0     LDA    $C0
0327 A1 0D     CMP    #$0D
0329 26 04     BNE    $032F
032B A6 01     LDA    #$01                          hrs=1;
032D B7 C0     STA    $C0
                                            }
                                        }
032F 8F        WAIT                   WAIT();   /* wait here to save energy  */

0330 20 DF     BRA    $0311         }
0332 81        RTS                }
                                  void __TIMER(void)
07F8 03 33                        {
0333 1F 08     BCLR  7,$08          TCST.TOF=0;  /* reset the interrupt */
0335 1D 08     BCLR  6,$08          TCST.RTIF=0; /* flags  */
0337 3C C3     INC    $C3             if (++count==122)
0339 B6 C3     LDA    $C3
033B A1 7A     CMP    #$7A
033D 26 39     BNE    $0378
                                      {
033F 3C C2     INC    $C2                sec++;  /* increment seconds */
0341 3C C4     INC    $C4                if(++corr1==14)
0343 B6 C4     LDA    $C4
0345 A1 0E     CMP    #$0E
0347 26 2D     BNE    $0376
                                         {
0349 3F C4     CLR    $C4                  corr1=0;
034B 3C C5     INC    $C5                    if(++corr2==80)
034D B6 C5     LDA    $C5
034F A1 50     CMP    #$50
0351 26 1D     BNE    $0370
                                             {
0353 3F C5     CLR    $C5                      corr2=0;
0355 3C C6     INC    $C6                      if(++corr3==4)
0357 B6 C6     LDA    $C6
0359 A1 04     CMP    #$04
035B 26 0F     BNE    $036C
                                                    {
035D A6 01     LDA    #$01                             count=1;
035F B7 C3     STA    $C3
0361 B6 C6     LDA    $C6                              corr3==0;
0363 26 04     BNE    $0369
0365 A6 01     LDA    #$01
0367 20 01     BRA    $036A
0369 4F        CLRA
                                                    }
036A 20 02     BRA    $036E                         else
036C 3F C3     CLR    $C3                                count=0;
                                                }
036E 20 04     BRA    $0374                     else
0370 A6 FF     LDA    #$FF                         count=(-1);
0372 B7 C3     STA    $C3
                                         }
0374 20 02     BRA    $0378              else
0376 3F C3     CLR    $C3                    count=0;
                                     }
0378 80        RTI                }
07FE 03 00

SYMBOL TABLE
LABEL        VALUE  LABEL        VALUE  LABEL        VALUE  LABEL        VALUE

DDRA         0004 | DDRB         0005 | FALSE        0000 | PORTA        0000
PORTB        0001 | PORTD        0003 | RT0          0000 | RT1          0001
RTIE         0004 | RTIF         0006 | TCNT         0009 | TCST         0008
TOF          0007 | TOFE         0005 | TRUE         0001 | __COPSVS     07F0
__IRQ        07FA | __RESET      07FE | __STARTUP    0000 | __SWI        07FC
__TIMER      0333 | corr1        00C4 | corr2        00C5 | corr3        00C6
count        00C3 | hrs          00C0 | main         0300 | mts          00C1
sec          00C2 |

MEMORY USAGE MAP ('X' = Used, '-' = Unused)
0300 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
0340 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXX-------
0380 : ---------------- ---------------- ---------------- ----------------
03C0 : ---------------- ---------------- ---------------- ----------------
0700 : ---------------- ---------------- ---------------- ----------------
0740 : ---------------- ---------------- ---------------- ----------------
0780 : ---------------- ---------------- ---------------- ----------------
07C0 : ---------------- ---------------- ---------------- --------XX----XX

All other memory blocks unused.

Errors             :    0
Warnings           :    0





<a name="01cf_001b">
<a name="01cf_001c">
[LISTING FOUR]
<a name="01cf_001c">

#include "hc05c8.h"
#include "general.h"

int hrs, mts, sec;       /* global variables   */
long count=1000;

struct bothbytes         /* 16 bit int structure  */
{
      int hi;
      int lo;
};
union isboth              /* and union  */
{
      long  l;
      struct bothbytes b;
};
union isboth time_comp_count;
registera ac;
void main(void)
{
    int key;
    TCR.OCIE = 1; /* enable output compare interrupt */
    CLI();   /* enable all interrupts  */
    FOREVER
    {
        if(sec==60)  /* do clock things each minute  */
        {
            sec=0;
            if(++mts==60)
            {
                mts=0;
                if(++hrs==13)
                    hrs=1;
            }
        }
        WAIT();   /* wait here to save the energy  */
    }
}
void __TIMER(void)   /* time interrupt service routine  */
{
     /* the program gets here every millisecond  */
     time_comp_count.b.hi = TCHI;
     ac =TSR;           /* Clear the tof bit  */
     time_comp_count.b.lo = TCLO;
     time_comp_count.l += 500;  /* 500 counts per millisecond */
     OCHI = time_comp_count.b.hi;
     ac = TSR;           /* Clear ocf bit    */
     OCLO = time_comp_count.b.lo;
    if(--count)
         return ;
    else
    {
         sec++;    /* here every second  */
         count=1000;/* reset count to 1 second */
    }
}


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.