{\rtf1\ansi\deff0\deftab720{\fonttbl{\f0\fswiss MS Sans Serif;}{\f1\froman\fcharset2 Symbol;}{\f2\fmodern Courier New;}{\f3\froman Times New Roman;}{\f4\fmodern\fprq1 Courier New;}{\f5\fmodern Courier New;}}
{\colortbl\red0\green0\blue0;}
\deflang2057\pard\plain\f2\fs28\b EPE PIC TUTORIAL\plain\f2\fs20  \plain\f2\fs24\b - Extracts for Toolkit TK3 21JAN00
\par \plain\f2\fs20 
\par This text has been extracted from the original \plain\f2\fs20\i EPE PIC Tutorial \plain\f2\fs20 of
\par Mar-May 1998. Occasional notes relating to Toolkit TK3 have been added.
\par \plain\f5\fs20\b 
\par THIS IS NOT THE SAME TEXT AS USED WITH \plain\f5\fs20\b\i EPE PIC TUTORIAL V2 \plain\f5\fs20\b OF APR-JUN 03,
\par although there are some simularities.\plain\f5\fs20 
\par \plain\f2\fs20 
\par The software programs referred to are available from the EPE Editorial
\par office (a nominal handling charge applies) or free from the EPE FTP
\par site.
\par 
\par Note that the discussions and programming examples relate to TASM grammar
\par (MPASM grammar is slightly different - see later).
\par 
\par References to .OBJ files are made in this text - they are a form of code
\par file which is only required by TASM-type programmers prior to Toolkit TK3.
\par Files having .HEX extensions are the standard form generated by TK3 and
\par MPASM-type programmers. TK3 can use .OBJ or .HEX files when programming PICs.
\par 
\par This edited text refers to various schematic figures. These are not
\par supplied with TK3 and the original \plain\f2\fs20\i EPE\plain\f2\fs20  texts must be referred to if you
\par need to know the schematic details. Switches and LEDs referred to are
\par frequently numbered in reverse order reading left to right (in standard
\par binary bit format), e.g. switches SB7 to SB0 are on Port B lines of the
\par same number (RB7 to RB0). Many of the switch and LED functions you should
\par be able to glean from the discussion context, and you could attempt to
\par reconstruct some of the demos via the PCB for Toolkit MK3. For full
\par replication of the demos, though, only the original \plain\f2\fs20\i PIC Tutorial \plain\f2\fs20 PCB can be
\par used.
\par 
\par This text is not intended to be a replacement for the original!
\par 
\par \plain\f4\fs28\b SUBJECTS COVERED\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b TUTORIAL 1:\plain\f2\fs20 
\par 
\par Minimum commands needed
\par Port default values
\par Instruction .ORG
\par Instruction .END
\par Instruction .AVSYM
\par 
\par \plain\f2\fs24\b TUTORIAL 2:\plain\f2\fs20 
\par 
\par File registers
\par Bits
\par Bytes
\par Set
\par Clear
\par Command CLRF
\par Command CLRW
\par Command BSF
\par Command BCF
\par Ports and Port directions
\par Register STATUS
\par STATUS register bit 5
\par Pages 0 and 1
\par 
\par \plain\f2\fs24\b TUTORIAL 3:\plain\f2\fs20 
\par 
\par Names in place of numbers
\par Case sensitivity
\par Labels
\par Repetitive loop
\par Command GOTO
\par Instruction .EQU
\par 
\par \plain\f2\fs24\b TUTORIAL 4:\plain\f2\fs20 
\par 
\par Command MOVLW
\par Command MOVWF
\par Command RLF
\par Command RRF
\par Command BTFSS
\par Command BTFSC
\par Instruction #DEFINE
\par Instruction PAGE0
\par Instruction PAGE1
\par Register PORTA
\par Register TRISA
\par Register PORTB
\par Register TRISB
\par Register PCL
\par Double-naming numbers
\par Bit naming
\par Program counter
\par STATUS register bit 0
\par Carry flag
\par Bit codes C, F, W
\par Bit testing
\par Conditional loop
\par 
\par \plain\f2\fs24\b TUTORIAL 5:\plain\f2\fs20 
\par 
\par STATUS bit 2
\par Zero flag
\par Bit code Z
\par Command MOVF
\par 
\par \plain\f2\fs24\b TUTORIAL 6:\plain\f2\fs20 
\par 
\par Command INCF
\par Command DECF
\par Command INCFSZ
\par Command DECFSZ
\par Counting upwards (incrementing)
\par Counting downwards (decrementing)
\par Use of a file as a counter
\par 
\par \plain\f2\fs24\b TUTORIAL 7:\plain\f2\fs20 
\par 
\par Switch monitoring
\par Command ANDLW
\par Command ANDWF
\par Command ADDWF
\par Command ADDLW
\par Nibbles
\par STATUS bit 1
\par Digit Carry flag
\par Bit code DC
\par 
\par \plain\f2\fs24\b TUTORIAL 8:\plain\f2\fs20 
\par 
\par Increasing speed of TUT7
\par Bit testing for switch status
\par 
\par \plain\f2\fs24\b TUTORIAL 9:\plain\f2\fs20 
\par 
\par Responding to a switch press only at the moment of pressing
\par 
\par \plain\f2\fs24\b TUTORIAL 10:\plain\f2\fs20 
\par 
\par Performing functions dependent upon which switch is pressed
\par Use of a common routine serving two functions
\par 
\par \plain\f2\fs24\b TUTORIAL 11:\plain\f2\fs20 
\par 
\par Reflecting PORTA's switches on PORTB's l.e.d.s
\par Command COMF
\par Command SWAPF
\par Inverting a byte's bit logic
\par Swapping a byte's nibbles
\par 
\par \plain\f2\fs24\b TUTORIAL 12:\plain\f2\fs20 
\par 
\par Generating an output frequency in response to a switch press
\par The use of two port bits set to different input/output modes
\par Command NOP
\par 
\par \plain\f2\fs24\b TUTORIAL 13:\plain\f2\fs20 
\par 
\par Command CALL
\par Command RETURN
\par Command RETLW
\par 
\par \plain\f2\fs24\b TUTORIAL 14:\plain\f2\fs20 
\par 
\par Tables
\par Register PCL (again)
\par Register PCLATH
\par 
\par \plain\f2\fs24\b TUTORIAL 15:\plain\f2\fs20 
\par 
\par Using four switches to create four different notes
\par Use of a table to selectively route program flow
\par 
\par \plain\f2\fs24\b TUTORIAL 16:\plain\f2\fs20 
\par 
\par Indirect addressing
\par Using unnamed file locations
\par Port B internal ``light-pullups''
\par Register FSR
\par Register INDF
\par Register OPTION
\par 
\par \plain\f2\fs24\b TUTORIAL 17:\plain\f2\fs20 
\par 
\par Tone modulation
\par Command XORLW
\par Command XORWF
\par Command IORLW
\par Command IORWF
\par 
\par \plain\f2\fs24\b TUTORIAL 18:\plain\f2\fs20 
\par 
\par Register INTCON
\par Command OPTION
\par Register TMR0
\par Use of internal timer
\par 
\par \plain\f2\fs24\b TUTORIAL 19:\plain\f2\fs20 
\par 
\par BCD (Binary Coded Decimal) counting
\par 
\par \plain\f2\fs24\b TUTORIAL 20:\plain\f2\fs20 
\par 
\par Precision timing at 1/25th second
\par Counting seconds 0 to 60
\par 
\par \plain\f2\fs24\b TUTORIAL 21:\plain\f2\fs20 
\par 
\par Using 7-segment l.e.d. displays
\par Showing hours, minutes and seconds
\par Command IORLW (usage)
\par 
\par \plain\f2\fs24\b TUTORIAL 22:\plain\f2\fs20 
\par 
\par Using intelligent l.c.d.s
\par Setting l.c.d. contrast
\par Initialising the l.c.d.
\par Sending a message to the l.c.d.
\par 
\par \plain\f2\fs24\b TUTORIAL 23:\plain\f2\fs20 
\par 
\par Coding hours, minutes and seconds for an l.c.d.
\par Shortened clock monitoring code
\par Command SUBLW
\par Command SUBWF
\par 
\par \plain\f2\fs24\b TUTORIAL 24:\plain\f2\fs20 
\par 
\par Adding time-setting switches
\par 
\par \plain\f2\fs24\b TUTORIAL 25:\plain\f2\fs20 
\par 
\par Dual use of port pins for input and output
\par Configuring program for alarm system monitoring
\par 
\par \plain\f2\fs24\b TUTORIAL 26:\plain\f2\fs20 
\par 
\par Writing and reading EEPROM file data
\par Register EECON1
\par Register EECON2
\par Register EEDATA
\par Register EEADR
\par 
\par \plain\f2\fs24\b TUTORIAL 27:\plain\f2\fs20 
\par 
\par Integrating EEPROM data read/write into alarm system
\par 
\par \plain\f2\fs24\b TUTORIAL 28:\plain\f2\fs20 
\par 
\par Interrupts
\par Command RETFIE
\par 
\par \plain\f2\fs24\b TUTORIAL 29:\plain\f2\fs20 
\par 
\par Command SLEEP
\par 
\par \plain\f2\fs24\b TUTORIAL 30:\plain\f2\fs20 
\par 
\par Watchdog timer
\par Command CLRWDT
\par 
\par \plain\f2\fs24\b TUTORIAL 31:\plain\f2\fs20 
\par 
\par Misc Special Register bits
\par 
\par \plain\f2\fs24\b TUTORIAL 32:\plain\f2\fs20 
\par 
\par Programming
\par PICs vs. hardware
\par Summing-up
\par 
\par \plain\f2\fs28\b INTRODUCTION\plain\f2\fs20 
\par 
\par At the time that \plain\f2\fs20\i EPE PIC Tutorial \plain\f2\fs20 was published in March to May 1998,
\par letters and phone calls to \plain\f2\fs20\i EPE\plain\f2\fs20  had been showing that interest in
\par Microchip's PIC microcontrollers had become intense. Many readers were
\par asking for more information on how to use these devices in designs of their
\par own invention.
\par 
\par Whilst Microchip's detailed \plain\f2\fs20\i PIC16/17 Microcontroller Data Book \plain\f2\fs20 gives
\par all the technical data on the PIC programming commands, readers new to the
\par devices had reported that the book does not go into enough detail about how
\par the codes are actually used and how they can be arranged in meaningful
\par sequences to perform specific tasks. Conversely, Microchip's \plain\f2\fs20\i Microchip\plain\f2\fs20 
\par \plain\f2\fs20\i Embedded Control Handbook\plain\f2\fs20 , which gives detailed listings of extensive
\par programming examples, goes into too much detail for simple programming
\par information to be readily located.
\par 
\par In the words of one reader, "I find the PIC Data Book too skimping on
\par everyday detail, and the published software too complex. Please show me how
\par to get to grips with the essence of PICs. Tell me, step by step, how to get
\par started with writing simple programs, how to just turn on a single LED, for
\par example. Then take me forward from there."
\par 
\par That, then, is the objective of this article, to get you started with PICs.
\par 
\par \plain\f2\fs28\b What's a PIC?\plain\f2\fs20 
\par 
\par A PIC chip, in this context, is a microcontroller integrated circuit
\par manufactured by Arizona Microchip. A microcontroller is similar to a
\par microprocessor but it additionally contains its own program command code
\par memory, data storage memory, bidirectional (input/output) ports and a clock
\par oscillator. Many microprocessors require the use of additional chips to
\par provide these requirements; microcontrollers are totally self-contained.
\par 
\par The great advantage of microcontrollers is that they can be programmed to
\par perform many functions for which many other chips would normally be
\par required. This not only makes for simplicity in electronic designs, but
\par also allows some functions to be performed which could not be done using
\par normal digital logic chips - i.e. circuits for which, previously, a
\par microprocessor would have been required.
\par 
\par There are many families of PIC microcontroller available, ranging from
\par those which can only be programmed once, to those that can be repeatedly
\par reprogrammed. There are two basic families of the latter: those that
\par require an ultra-violet light unit to erase their previous data before
\par being reprogrammed, and those which are electrically erasable.
\par 
\par In the latter PIC microcontroller family is the PIC16F84, which is the
\par device we shall use. It has been chosen because of its ease of
\par reprogramming. It is an Eeprom (electrically erasable programmable read
\par only memory) device. This means that it can be rapidly reprogrammed as
\par often as you wish, without the need for ultra-violet erasing.
\par 
\par Microchip describe the PIC16F84 as their "flash" equivalent to the earlier
\par PIC16C84. As far as this tutorial is concerned, the two devices can be
\par regarded as being interchangeable. The 'C84, though is no longer
\par manufactured. From hereon they are jointly referred to in this article as
\par the PIC16x84.
\par 
\par Much of the information about the commands which we present here is, in
\par most instances, applicable to other members of the PIC family. Once you
\par understand a PIC16x84 you should have no difficulty applying your knowledge
\par to other PICs. Note, though, that the demo board used in the \plain\f2\fs20\i PIC Tutorial
\par \plain\f2\fs20 can only be used to program the PIC16x84.
\par 
\par There are two versions of these chips, having the suffix -04 and -10
\par respectively. The suffix indicates the maximum clock rate at which the chip
\par can be used: -04 means 4MHz, -10 means 10MHz. You may use either speed
\par rating for the \plain\f2\fs20\i PIC Tutorial\plain\f2\fs20 , although the -04 is likely to be stocked by
\par more suppliers.
\par 
\par \plain\f2\fs28\b What You Need\plain\f2\fs20 
\par 
\par There are really only five things that you need in order to program a PIC,
\par a PC-compatible computer, a standard (Centronics) parallel printer port
\par connecting lead, a power supply capable of delivering between 12V and 14V
\par d.c. (for the PIC Tutorial board - Toolkit TK3 runs from 9V) a
\par word-processing program, and a PIC assembly and send program.
\par 
\par Data is output from the computer to the PIC via the parallel printer port
\par (addresses 378h, 278h and 3BCh are supported). It is output serially, data
\par on port line D0, and a clock signal on line D1.
\par 
\par You must be able to use a word-processing program. This must produce a text
\par file that is totally without formatting and printer commands. That is, it
\par must be able to generate a pure ASCII text file (and to input one). It is
\par stressed that the text (.ASM) files MUST be in pure ASCII text formats
\par without printer or display format commands embedded in them. (Windows
\par Notepad is used with TK3.)
\par 
\par \plain\f2\fs28\b Assembly Software\plain\f2\fs20 
\par 
\par The PIC assembly and send software used with \plain\f2\fs20\i PIC Tutorial \plain\f2\fs20 is known as TASM
\par (which simply stands for Table ASseMbly) and originated from the Public
\par Domain Shareware Library. It is not to be confused with other software
\par which includes the word TASM in its description or title. (Toolkit TK3 is
\par fully self-contained and does not need additional software.)
\par 
\par You may well have heard of other PIC assembly programs, such as MPASM, for
\par example. Whilst MPASM is generally regarded as being the industry-standard
\par PIC assembly program, TASM is the assembly program that was given to the
\par author before he became familiar with using PICs. Consequently, it has
\par become the program that he has "grown-up" with and now knows pretty
\par intimately.
\par 
\par There is, in fact, very little difference in the way that programs are
\par written for TASM and MPASM. The PIC command codes used are identical. It is
\par simply a matter of there being very slight grammatical differences in the
\par way that each program line is written in the text editor. Anyone familiar
\par with TASM will readily be able to convert their thinking to MPASM, and vice
\par versa. (A list of TASM/MPASM differences is accessible via TK3's main
\par window).
\par 
\par Note, though, that software written for TASM cannot be loaded into a PIC
\par through MPASM, and vice-versa. The program text lines must conform to the
\par grammar of the assembling program (TK3 takes care of this).
\par 
\par A bit more information about MPASM will be given at the end of this Tutorial
\par series.
\par 
\par \plain\f2\fs28\b Another Need\plain\f2\fs20 
\par 
\par Throughout this article, we shall examine in a fair amount of detail the
\par commands which it is expected you will find most useful. It is hoped that
\par it will give you all the necessary information which will enable you to
\par conceive a design in which you can use the PIC16x84 to control whatever
\par situation you wish, and to write the code that will let it do so.
\par 
\par There is, though, much more to writing programs than you may at this moment
\par fully appreciate. Knowledge about individual commands and the way in which
\par they can be used is not enough in itself. Programming is a way of looking
\par at the world in ways that other people may not recognise. You must have the
\par mental ability to see each programming situation as a step by step
\par function, analysing in your mind exactly how it is that you need to specify
\par the complete program flow.
\par 
\par You have to write the sequence of events with the correct grammar, with the
\par correct spelling and in the correct order. Undoubtedly you will make
\par mistakes while you are writing the code, failing to see the correct
\par sequence of events and using incorrect command structures.
\par 
\par You require the ability to analyse what you have done wrong and to correct
\par it. You are likely to be confronted with an overall task that may, on
\par occasion, take you into several days or even weeks of dedicated
\par concentration.
\par 
\par Programming, to those who have the ability to see things "as they are" and
\par not "how they seem to be", can become extremely addictive. You could find
\par yourself compelled to get back to the keyboard and PIC programmer at any
\par conceivable hour. You had better have an understanding family!
\par 
\par \plain\f2\fs28\b First Things First\plain\f2\fs20 
\par 
\par To get readers started programming PICs, \plain\f2\fs20\i PIC Tutorial \plain\f2\fs20 used a dedicated
\par printed circuit board which helped to illustrate the program examples
\par discussed in this text. The board comprised a PIC16x84 microcontroller, 14
\par light emitting diodes, a 4 x 7-segment LED display and a 2-line
\par 16-character alphanumeric liquid crystal display.
\par 
\par Using the switches and the example simple programs, \plain\f2\fs20\i PIC Tutorial \plain\f2\fs20 readers
\par were encouraged to build up their experience of how the PIC16x84 can be
\par made to respond to different practical situations.
\par 
\par In this edited text, we shall start at the very beginning of programming.
\par The first lesson will be about the very minimum of information that needs
\par to be written into a program listing before the PIC can do anything else.
\par 
\par We shall then, "step by step", take a specific very simple task, such as
\par turning on an LED, and describe in detail each of the commands that are
\par required to do the task.
\par 
\par Having described one task, we then take that idea a stage further, adding a
\par few more commands that will enhance the capabilities of the program. Each
\par of these commands will similarly be discussed in detail. Thus we shall
\par progress, taking simple ideas, and illustrating how they can be achieved
\par and then enhanced.
\par 
\par We feel that this approach is far more useful than describing each and
\par every one of the commands in turn before we ever get to use them. Most
\par people learn by doing, reading about things in short sections and applying
\par the knowledge in practical bite-sized chunks. A complete chapter on all the
\par commands in sequence would be too much to remember and understand in one go.
\par 
\par With the exception of two commands, the complete list of commands for the
\par PIC16x84 is accessible via the Commands button in TK3's main window. All are
\par discussed and demonstrated. The reason for omitting the two (OPTION and TRIS)
\par is that Microchip recommend that they are not used in order to maintain
\par upward compatibility with later series of PICs; the PIC17xx series has
\par dropped them.
\par 
\par As we shall see, there are a lot of "bit-orientated" sub-commands available
\par as well as the main commands. Some of these are similar in operation and
\par close examination will be given only to the principle ones - once you
\par understand these, use of the similar bit-setting commands available will
\par become obvious from reading the PIC Data Book.
\par 
\par \plain\f2\fs28\b Tutorial Sections\plain\f2\fs20 
\par 
\par The Tutorials are split into 32 sections. Each deals with specific coding
\par topics but, in most cases, is a direct follow-on from the previous
\par Tutorial, and is nearly always visually illustrated by the displays on the
\par PCB. The exception to the latter is when sound is used as the output
\par medium, when a pair of personal stereo headphones is needed.
\par 
\par At the end of most Tutorials there are a few simple exercises which allow
\par you to experiment with the simple program presented in that section. You
\par will only be expected to use the commands that have already been introduced
\par to you. None of them should tax your brain too much, but they will,
\par hopefully, encourage you to think of alternative ways in which the same
\par basic task can be tackled, or to consider other tasks that can be achieved
\par using similar techniques.
\par 
\par By the end of the Tutorials, you will know how to get the PIC16x84 to
\par respond to switches and other external signal sources, send data to various
\par types of display, to create sound, to be the heart of a 24-hour clock, to
\par store data in its non-volatile Eeprom memory, and to monitor an intruder
\par alarm system.
\par 
\par Readers who have had experience of programming in Basic, or with other
\par types of microprocessor, will find that once a few commands have had their
\par functions explained, using them will rapidly become instinctive. The
\par author, having many years of such practice, effectively learned about PICs
\par and how to use them over a single weekend, just by doing a bit of
\par experimenting.
\par 
\par Other readers without such experience will, it has to be said, have to
\par become accustomed to understanding programming itself as a step by step
\par process. An analytical mind is required and there is no easy way in which
\par programming can be taught to those who lack this capability.
\par 
\par \plain\f2\fs24\b Demo Circuit\plain\f2\fs20 
\par 
\par (Description of \plain\f2\fs20\i PIC Tutorial \plain\f2\fs20 demo printed circuit board and its testing
\par omitted.)
\par 
\par \plain\f2\fs28\b PIC configuration\plain\f2\fs20 
\par 
\par (Some text omitted.)
\par 
\par All PICs must be "configured" for the application they are intended to
\par control. Such configuration includes the selection of oscillator type and
\par matters such as Watchdog timer and Code Protection bits use. Once the
\par configuration has been sent it is not normally necessary to change it for
\par the same application.
\par 
\par Note, though, that occasionally (and for a variety of reasons) the
\par configuration in the PIC can become corrupted. If you find that a program
\par which you believe should run does not, run the configuration program to
\par ensure that the PIC is correctly configured, and then re-send the program.
\par 
\par Sometimes corrupted configuration can prevent another program from
\par replacing an existing one, the latter continuing to run even though the new
\par one has been sent.
\par 
\par Be aware that the time taken to send a .OBJ or .HEX file to the PIC depends
\par on its length. A delay exists because the PIC requires a minimum amount of
\par time to store each command when received. The speed of operation has
\par nothing to do with the speed of your computer.
\par 
\par \plain\f2\fs28\b Tutorial Exercises\plain\f2\fs20 
\par 
\par (You will not be able to do the excercises unless you have the \plain\f2\fs20\i PIC Tutorial
\par \plain\f2\fs20 board.)
\par 
\par Throughout these Tutorials we present various programming exercises at
\par which to try your hand. In most cases, you are requested to modify an
\par existing tutorial program. This requires it to be saved, assembled and sent
\par to the PIC.
\par 
\par It is important that when you make these changes, you do not save the
\par variant under the same name as the original. It must have a different name.
\par If you do save under the same name, the original file will be replaced by
\par the new one. You can save each program variant by any name of your
\par choosing, but use the same .ASM extension as our examples use. You might
\par consider using TRY1.ASM, TRY2.ASM, etc. It is recommended that you save the
\par original file under the new name before you make any changes, to avoid
\par curse-worthy errors!
\par 
\par Having saved your changed file, you now assemble and send it to the PIC.
\par 
\par We shall not be giving possible solutions (of which there could be several)
\par to any of the exercise questions. It is expected that you will persevere
\par until you find a workable solution. Only in this way will you get into the
\par habit of being presented with a computing problem, which you have to
\par solve on your own, and then solving it.
\par 
\par We are now in a position to start telling you about writing programs for a
\par PIC16x84.
\par 
\par A summary of the Tutorial contents was shown earlier.
\par 
\par \plain\f2\fs28\b Tutorial 1
\par \plain\f2\fs20 
\par \plain\f2\fs24\b Concepts Examined\plain\f2\fs20 
\par 
\par Minimum software requirement
\par Port default values
\par Instruction .ORG
\par Instruction .END
\par Instruction .AVSYM
\par 
\par Note that a slow running RC oscillator is used for the first few Tutorials.
\par 
\par The absolute bare minimum requirements for any PIC16x84 program to be
\par compiled are shown in Listing 1.
\par 
\par \plain\f2\fs24\b Listing 1: Program TUT1\plain\f2\fs28\b 
\par \plain\f2\fs20 
\par ; TUT1.ASM - 28JUL97
\par ; minimum requirement
\par 
\par         .ORG $0004      ; Interrupt vector address
\par         .ORG $0005      ; Start of program memory
\par 
\par (your program goes in here)
\par 
\par         .END            ; final statement
\par 
\par In fact, none of the statements in this listing have anything directly to
\par do with a functioning software program. Three are aimed directly at the
\par assembler; the others are comments to the programmer, or other reader. Such
\par comments include program title and function, and notes about what
\par particular program instructions within the list are intended to perform.
\par Comments must always be preceded by a colon (;) so that the assembler does
\par not try to treat them as program commands. Comments may appear anywhere
\par within the program, and in any position where they do not interfere with a
\par program command. It is convenient, though, to place them tabulated a short
\par distance beyond the end of program command lines.
\par 
\par To take Listing 1 in detail, you will see that it starts with two comments,
\par identifying the listing and its function:
\par 
\par ; TUT1.ASM - 28JUL97
\par ; minimum requirement
\par 
\par Next come two commands which are aimed at the assembler and need not
\par normally concern you - repeating them parrot-fashion in any software you
\par write will normally suffice unless interrupts are involved:
\par 
\par .ORG $0004 ; Interrupt vector address
\par .ORG $0005 ; Start of program memory
\par 
\par These commands give the memory address (position) within the PIC at which a
\par particular set of subsequent commands is to be placed. Such commands
\par consist of the .ORG (origin) statement followed by a number, which may be
\par expressed in hexadecimal (as shown here - preceded by a $ sign), or in
\par decimal (4 and 5 in this case), or in binary (indicated by a % symbol
\par prefix, e.g. %00000100 and %00000101). With some assemblers, the numbers
\par can point to any memory address, but TASM programs require the use of
\par addresses $0004 and $0005. The Interrupt vector address at $0004, and
\par the subject of interrupts in general, will be dealt with in Tutorial 28.
\par Ignore the concept for the moment.
\par 
\par (Note that MPASM-type programmers often need the statement ORG 0 as the
\par first ORG statement of programs destined for PIC16x84 devices.)
\par 
\par The fifth statement in the listing is aimed at you, the reader: it tells
\par you where your program is to be written. This will become evident as we
\par progress through the example listings.
\par 
\par The final statement (.END) is another which is only required by the
\par assembler. It MUST always be placed at the very end of any listing.
\par 
\par So, of the six lines in Listing 1, the only three statements actually
\par needed are the two .ORGs and .END. Everything else is up to you.
\par 
\par \plain\f2\fs24\b Symbols and Lists\plain\f2\fs20 
\par 
\par There is another instruction which you may see used at the beginning of
\par some TASM program listings - .AVSYM. This tells the original TASM programmer
\par that while it is assembling the code it should also generate another disk
\par file (having a .SYM extension - Symbol) which shows the "alias" names used
\par and the values they represent (see Tutorial 3). (Toolkit and MPASM
\par programmers do not require AVSYM).
\par 
\par \plain\f2\fs24\b Incapable?\plain\f2\fs20 
\par 
\par You might think that when TUT1.ASM is compiled and loaded into the PIC as
\par TUT1.OBJ (Toolkit TK3 uses .HEX files), that the PIC will be incapable of
\par doing anything - it hasn't been told of anything to do. Almost true, but
\par not quite!
\par 
\par The PIC16x84s have been told in manufacture to adopt certain "default"
\par conditions when first switched on (these will be shown later). One of these
\par conditions is that Port A and Port B are configured (set) to act as inputs.
\par What is not configured at this time is the binary value which is available
\par to be output via those pins when they are first set as outputs from within
\par the program. At switch-on, any number could be set randomly within the
\par PIC's memory, of between 0 and 255 (%00000000 to %11111111) for Port B, and
\par 0 to 31 (%00000 to %11111) for Port A. It is often preferable, therefore,
\par to set port output values to a known number, usually zero, as part of the
\par opening program statements. This, too will become apparent as we progress.
\par 
\par \plain\f2\fs24\b Exercise 1\plain\f2\fs20 
\par 
\par 1.1. Assemble TUT1.ASM, load the resulting TUT1.OBJ into your PIC and run it.
\par Note that pushing the push-switches will have an effect on the LEDs, but not
\par because the PIC is causing a response, rather because current direct from the
\par power rails is able to pass through each LED, its associated resistors and
\par switches.
\par 
\par Note that whenever we tell you to load and run a file, you must have
\par previously assembled it to .OBJ from its .ASM code. 
\par 
\par (Also note that TK3 assembles to .HEX and this file may equally well be sent
\par to the PIC in place of the .OBJ file.)
\par 
\par \plain\f2\fs28\b Tutorial 2\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par File registers
\par Bits
\par Bytes
\par Set
\par Clear
\par Command CLRF
\par Command CLRW
\par Command BSF
\par Command BCF
\par Ports and Port directions
\par STATUS register bit 5
\par Pages 0 and 1
\par 
\par At this point in time, a PIC without program commands is of no benefit to
\par us! We shall now create a simple program which just turns on the series of
\par eight LEDs connected to Port B. Look at Listing 2.
\par 
\par \plain\f2\fs24\b LISTING 2 - PROGRAM TUT2
\par \plain\f2\fs20 
\par TUT2.ASM
\par 
\par ; setting Port B to output mode and turn on each LED
\par 
\par .ORG $0004      ; Interrupt vector address
\par .ORG $0005      ; Start of program memory
\par 
\par CLRF 6          ; set all Port B pins to logic 0
\par BSF 3,5         ; instruct program that a Page 1 command comes next
\par CLRF 6          ; set all Port B pins as outputs
\par BCF 3,5         ; instruct program that a Page 0 command comes next
\par BSF 6,0         ; set Port B pin 0 to logic 1
\par BSF 6,1         ; set Port B pin 1 to logic 1
\par BSF 6,2         ; set Port B pin 2 to logic 1
\par BSF 6,3         ; set Port B pin 3 to logic 1
\par BSF 6,4         ; set Port B pin 4 to logic 1
\par BSF 6,5         ; set Port B pin 5 to logic 1
\par BSF 6,6         ; set Port B pin 6 to logic 1
\par BSF 6,7         ; set Port B pin 7 to logic 1
\par .END            ; final statement
\par 
\par 
\par As discussed, you will see comments at the start of the listing and within
\par it, all preceded by a colon. You will also see the .ORG and .END
\par statements. Sandwiched between them are several lines of code. Ignoring the
\par numbers following the codes, there are only three different codes in use
\par here: CLRF, BSF and BCF. They simply stand for CLeaR File, Bit Set File and
\par Bit Clear File. An allied command to CLRF is CLRW (CLeaR Working register).
\par 
\par Load program TUT2.OBJ into the PIC. Having loaded it the program starts
\par running. There will be a few seconds wait before you see anything
\par happening.
\par 
\par At present, the oscillator which controls the PIC is only running at a very
\par slow speed, about 1Hz or so (depending on the tolerance of the C3 and VR1
\par values). The internal workings of the PIC16x84 automatically divide any
\par clock frequency by four, taking four counts to process a single command.
\par During each of the four intervening pulses, different aspects of the
\par command are processed. To all intents and purposes, each command takes four
\par clock pulses.
\par 
\par The results of the first four commands will not produce any visible result.
\par After a few seconds, though, each LED of Port B will come on in turn, in
\par order of LB0 to LB7, with a pause between each change. When the final LED
\par (LB7) has come on, there are no more instructions to perform, so nothing
\par more happens (in fact, the PIC is actually working its way through each of
\par the null commands which were programmed in from the PICCLR program used
\par when you tested the demo board, but you won't see any result from these
\par commands). To see the sequence again, press the Reset switch, whereupon
\par the program will restart from the beginning. The rate at which it occurs
\par can be changed by adjusting preset VR1.
\par 
\par But, how does the program do what you see it has done? First, let's find
\par out what File registers are and what commands CLRF, BSF and BCF do.
\par 
\par \plain\f2\fs24\b File Registers\plain\f2\fs20 
\par 
\par The PIC16x84 has five areas of memory:
\par 
\par 1. Program Memory (Eeprom - 1024 bytes), in which are stored the commands
\par that form the program, and where the program of TUT2 is now held.
\par 
\par 2. Data Memory (SRAM - 36 bytes for the 'C84, 68 for the 'F84), in which
\par you can temporarily store the results of any action that the program
\par performs.
\par 
\par 3. Data Memory (Eeprom - 64 bytes), in which you can indefinitely store any
\par data that you wish to retain after power has been switched off (it will be
\par discussed in Tutorial 26).
\par 
\par 4. Special Function Memory (SRAM - 15 bytes), whose attributes determine
\par what actions the PIC takes in respect of program commands.
\par 
\par 5. Working Memory (SRAM - 1 byte), through which many operations have to
\par pass during program performance (in other processors the Working
\par memory/register may be called the Accumulator).
\par 
\par The results (data) of any program action can be directed to be stored at
\par any of the memory areas numbered 2 to 5 above. With items 2, 3 and 4 the
\par destination is known as a File destination. With item 5, the Working Memory
\par or Register, the destination is known as, not surprisingly, the Working
\par destination. Strictly speaking, files should be known by their full name of
\par File Registers.
\par 
\par It would be tedious, though, to keep using the full name, and so the term
\par "file" will be used to mean File Register. (We also use the term "file" in
\par relation to disk files - the context should make the meaning clear.)
\par 
\par For those of you who are familiar with programming in Basic, files can be
\par regarded as the equivalent of variables. There is no direct Basic
\par equivalent of the Working register, though in a sense it can be regarded as
\par a special variable.
\par 
\par The program commands reflect the file and working destinations by the use
\par of F and W, respectively, in the code itself. For example, in the code
\par CLRF, the F indicates that the value in a particular file (memory data
\par byte) is to be CLeaRed (reset to zero) and the result is to be retained in
\par the file. On the other hand, in the code MOVLW (to be met in Tutorial 4)
\par the W stands for Working (in this case meaning that a Literal value is to
\par be MOVed into W). The use of both F and W in a command (e.g. MOVWF) will
\par become evident in due course.
\par 
\par \plain\f2\fs24\b Bits\plain\f2\fs20 
\par 
\par Having established what a file is, it is necessary (and easy) to understand
\par the concept of a "bit". You no doubt understand it already, but, just to
\par recap, a bit is a single part of an electronic memory which can be set to
\par one of two states: either to logic 1 ("on" - charged to a voltage which is
\par usually the same as the positive power rail that supplies the circuit, e.g.
\par +5V); or to logic 0 ("off" - discharged to a zero voltage). Logic 1 and
\par logic 0 are often referred to as high and low, respectively.
\par 
\par A memory i.c. can have any number of bits contained within it. A fixed
\par number of bits is known as a "byte". There is a common misconception that a
\par byte is only ever comprised of eight bits. Historically, this is not true,
\par any fixed number of bits which can be operated as a single unit is called a
\par byte. However, by current usage, a byte is usually taken to be comprised of
\par eight bits.
\par 
\par The status of the bits (high or low) within a byte is expressed as a binary
\par number reading from left to right, in order of the bit representing the
\par highest value first (most significant bit - MSB) down to the lowest value
\par (least significant bit - LSB). Thus decimal 128 is expressed as binary
\par 10000000, whereas binary 1 may be expressed as 00000001 (the preceding
\par number of zeros may not always be included - their presence being implied).
\par The bits of a byte are referred to by the position within a byte, with
\par position 0 at the right, ascending as high as necessary depending on the
\par byte length. Thus an 8-bit byte has its bits numbered as 7, 6, 5, 4, 3, 2,
\par 1, 0. A 16-bit byte (probably referred to as a word) would be numbered from
\par 15 to 0. The use of 0 (zero) as a bit number is essential to remember when
\par programming PICs.
\par 
\par Incidentally, terms MSB and LSB can also be used to mean Most Significant
\par Byte and Least Significant Byte. The appropriate meaning should be clear
\par from the context. There are also similar terms, NMSB and NLSB, in which the
\par N stands for Next.
\par 
\par \plain\f2\fs24\b Terms Set and Clear\plain\f2\fs20 
\par 
\par The concept of the terms "set" and "clear" is important to understand. In
\par program terms, to "set" a bit means to force it high, i.e. to logic 1; the
\par term "clear" is used to mean that a bit is forced low, i.e. to logic 0.
\par 
\par Note, however, that in textual terms (i.e. in articles such as this) you
\par are likely to come across the mixed use of the word "set", in that you
\par might be told to "set a bit low". In such cases, the implied meaning should
\par be obvious from the context. In this example, "low" is the important word
\par and "force" or "make" could have been used instead of "set".
\par 
\par \plain\f2\fs24\b Commands CLRF and CLRW\plain\f2\fs20 
\par 
\par Command CLRF stands for CLeaR File and is always followed by a single
\par number or name which indicates the file on which the action is to be
\par performed. The command instructs the PIC that all bits within the stated
\par file are to be cleared, i.e. it clears the whole byte. The allied command
\par CLRW (CLeaR Working register) simply clears the contents of the working
\par register and is used on its own without a subsequent number or name. In
\par practice, this command may be seldom used, the commands MOVLW 0 and RETLW 0
\par probably finding preference (these commands are discussed in Tutorials 4
\par and 13, respectively). Command CLRW has, in fact, been dropped from the
\par PIC17xx series of microcontrollers. There are no direct opposites of CLRF
\par and CLRW which will set all bits high; other techniques have to be used for
\par this action.
\par 
\par Using names for files instead of numbers will be dealt with in Tutorial 3.
\par 
\par \plain\f2\fs24\b Command BSF\plain\f2\fs20 
\par 
\par The PIC16x84 can have any individual bit of any file byte acted upon
\par directly. Each bit can be set high or low by a single command, and, as we
\par shall see later, a single command will also determine the status of any
\par individual bit. Command BSF translates as Bit Set File and is always
\par followed by two numbers or names separated by a comma. The first number or
\par name is the file byte whose bit is to be acted upon. The second number or
\par name is the number of the bit within the file byte (between 7 and 0,
\par reading from left to right). As an example, the command BSF 3,5 in Listing
\par 2 instructs the PIC to set (make high, force to logic 1!) bit 5 of file
\par number 3; command BSF 6,7 means that bit 7 of file number 6 is to be set.
\par 
\par \plain\f2\fs24\b Command BCF\plain\f2\fs20 
\par 
\par Command BCF stands for Bit Clear File and is the exact opposite of BSF. As
\par an example, the command BCF 3,5 in Listing 2 means that bit 5 of file 3 is
\par to be cleared (set low, made/forced to logic 0!).
\par 
\par \plain\f2\fs24\b Port File Numbers\plain\f2\fs20 
\par 
\par In the example of Listing 2, the files whose bits have been set or cleared
\par are those which control Port B. In terms of writing to or reading from Port
\par A and Port B, the two ports are treated as any other file destination.
\par However, Port A and Port B are different to other files in that they are
\par for communication with the outside world. There are, in fact, two file
\par registers associated with each port that are accessible to the programmer.
\par One is used to set the direction in which the bits are to act, i.e. as
\par inputs or outputs (it is also known as the Data Direction Register - DDR),
\par and the other deals with the data written to (for output to the world) or
\par read from (input from the world). Each bit of the port direction registers
\par can be individually set or cleared so that the same port may be used
\par simultaneously for data input and output via different bits.
\par 
\par The file which controls the direction in which Port B pins respond is named
\par in the PIC16x84 data sheet/book as TRISB. It is one of the named "special
\par function files" (see the Table folowing Listing 4) and is contained in
\par memory byte address $86 (we won't translate hexadecimal numbers into decimal
\par since the latter are irrelevant in the context of file addresses).
\par 
\par The file which holds Port B's data, whether as input or output, is at
\par memory byte address $06 and, helpfully, is known in the PIC book as PORTB.
\par 
\par A minor inconvenience exists in the PIC16x84 in that addresses $80 to $8B
\par can only be accessed by changing the value in another file, the STATUS
\par file. This file is held jointly at byte addresses $03 and $83. A single bit
\par within STATUS is used to direct the program to numbers either below $80
\par (known as Bank 0 or Page 0 addresses) or above $7F (known as Bank 1 or Page
\par 1 addresses); this bit is number 5. (Some PICs have more than two Banks.)
\par 
\par When STATUS bit 5 is set (logic 1), it effectively adds $80 to the memory
\par byte address being accessed. When the bit is clear (logic 0), addresses
\par below $80 are accessed. Thus, if you instruct that file 6 is to be accessed
\par when STATUS bit 5 is low, the file actually at address 6 will be accessed.
\par Conversely, if you instruct that file 6 is the subject when STATUS bit 5 is
\par high, the file at address $86 will be accessed.
\par 
\par Only files numbered $00 to $2F and $80 to $8B are available for use by the
\par programmer (for the 'C84 - the 'F84 has a few more). Also note that files
\par $07 and $87 have no function.
\par 
\par \plain\f2\fs24\b Listing 2 Commands\plain\f2\fs20 
\par 
\par We can now examine each command of Listing 2 in turn and describe its
\par purpose. When the PIC is first switched on, the STATUS file is set to a
\par default value with its bit 5 low. All file addresses are thus treated as
\par being below $80.
\par 
\par We have established that address 6 is that which holds the data for Port B.
\par The first command, CLRF 6, thus clears the data which is held in Port B as
\par a value available to be output, i.e. Port B is instructed to hold a value
\par of zero. The purpose of the program in Listing 2 is to output data to the
\par eight LEDs on Port B, and it has already been said that the default value
\par of Port B's direction register (at $86) is for all bits to be set for input
\par (all bits are high - 11111111). Consequently we must now set them all as
\par outputs, i.e. each to logic 0. To do this, first the STATUS register at
\par address 3 must have its bit 5 set high to point to addresses of $80 and
\par above; hence the command BSF 3,5.
\par 
\par Now we configure all of Port B for output mode with the command CLRF 6.
\par Yes, it's the same command as cleared Port B's data, but because STATUS bit
\par 5 is high, the value of 6 ($06) has $80 added to it, so the address
\par actually accessed is that at $86.
\par 
\par The commands which output data to the LEDs are all concerned with Port B's
\par data file at "real" address 6, so the addition of $80 is no longer needed.
\par The next command, BCF 3,5, thus clears bit 5 of the STATUS register at
\par address 3. All remaining commands in Listing 2 can now, in turn, set high
\par each data bit of Port B at address 6: BSF 6,0, BSF 6,1, etc., so turning on
\par the LEDs in sequence from LB0 to LB7. Simple!
\par 
\par \plain\f2\fs24\b Exercise 2\plain\f2\fs20 
\par 
\par 2.1. Using your text editor and .ASM software, experiment with the eight
\par commands relating to file 6, using different values (between 0 and 7) for
\par the number following the comma. Do not change the number before the comma.
\par Also experiment with changing BSF to BCF. Save under any name of your
\par choosing.
\par 
\par 2.2. Rewrite the program in Listing 2 so that it performs its actions on
\par Port A instead of Port B. The equivalent data and direction addresses for
\par Port A are 5 ($05) and $85, respectively. Note that Port A only has five
\par pins, not eight. What difference, if any, does this make to the program?
\par 
\par \plain\f2\fs28\b Tutorial 3\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Names in place of numbers
\par Labels
\par Case sensitivity
\par A repetitive loop
\par Command GOTO
\par Instruction .EQU
\par 
\par \plain\f2\fs24\b LISTING 3 - PROGRAM TUT3
\par \plain\f2\fs20 
\par ;TUT3.ASM
\par 
\par ;using names to ease writing of Listing 2
\par 
\par STATUS: .EQU 3          ;name program location 3 as STATUS
\par PORTB:  .EQU 6          ;name program location 6 as PORTB
\par 
\par         .ORG 4          ;Interrupt vector address
\par         .ORG 5          ;Start of program memory
\par 
\par         clrf PORTB      ;clear Port B data pins
\par         bsf STATUS,5    ;Page 1
\par         clrf PORTB      ;set all Port B as output
\par         bcf STATUS,5    ;Page 0
\par 
\par LOOPIT: bsf PORTB,0     ;set Port B pin 0 to logic 1
\par         bsf PORTB,1     ;set Port B pin 1 to logic 1
\par         bsf PORTB,2     ;set Port B pin 2 to logic 1
\par         bsf PORTB,3     ;set Port B pin 3 to logic 1
\par         bsf PORTB,4     ;set Port B pin 4 to logic 1
\par         bsf PORTB,5     ;set Port B pin 5 to logic 1
\par         bsf PORTB,6     ;set Port B pin 6 to logic 1
\par         bsf PORTB,7     ;set Port B pin 7 to logic 1
\par         clrf PORTB      ;clear all PORTB pins
\par         goto LOOPIT     ;goto address LOOPIT
\par         .END            ;final statement
\par 
\par In the previous section dealing with Listing 2, we were using numbers to
\par indicate which file was being referred to. That's fine if there are only a
\par few files whose address numbers can be easily remembered. As we progress
\par with examining the PIC16x84 commands and example listings, we shall be
\par using more and more files. If we continue to refer to them numerically,
\par we're going to get lost! (What's file $0C for? Is the data supposed to go
\par to file $1C or file $1E?)
\par 
\par Human memories with numbers are notoriously bad! But we are (usually) much
\par better with names. This fact was long ago recognised by program writers and
\par many types of software now allow the use of names in place of numbers.
\par Let's examine how names can be applied to the numbered files in Listing 2.
\par Have a look at Listing 3.
\par 
\par Any number written into a listing can be represented by a name. It can be
\par any name you like as long as you think you'll know in time to come what is
\par meant by that name (but some assemblers impose a limit on name lengths).
\par There are also some names which it is better to allocate according to their
\par function, especially those functions that already have names provided in
\par the PIC data books, such names as STATUS and PORTB, for example.
\par 
\par With some programmers (but not Toolkit TK3), the names are
\par "case-sensitive". In other words, once you have equated a name with a
\par number, further use of the name must be in exactly the same style as the
\par original with regard to the use of upper and lower case letters. For
\par example, PORTB and portb should not be used interchangeably. It is
\par recommended, though, that you do not use different upper and lower case
\par styles of the same word to mean different things. However, the commands
\par themselves may be in upper or lower case without (usually) causing
\par problems.
\par 
\par In Listing 3, three names have appeared: the aforementioned STATUS and
\par PORTB, plus LOOPIT. We'll keep LOOPIT a mystery for the moment (though
\par we're sure you know what it's for). All name allocations must appear at the
\par head of the program listing, in the initialisation block. Then the format
\par for allocating a name to a number is to state the name at the left of a
\par program line and immediately follow it with a colon (:). Now, with or
\par without a space (preferably with), the statement .EQU is made, followed by
\par the number you wish to name. In the case of the first line, STATUS is the
\par name to be given to the numeral 3 (which, you will remember, is the file
\par address number for the STATUS register). Hence, the statement:
\par 
\par STATUS: .EQU 3
\par 
\par This simply tells the Assembler that when it assembles the listing into
\par code, that each time it comes across the name STATUS, it is to replace it
\par in the code by the value of decimal 3.
\par 
\par The second line similarly allocates the name PORTB to file address 6:
\par 
\par PORTB: .EQU 6
\par 
\par Compare Listing 2 and Listing 3 side by side: you will see how the program
\par has been rewritten using the names in place of file numbers.
\par 
\par \plain\f2\fs24\b About .LST Files\plain\f2\fs20 
\par 
\par Having assembled TUT2.ASM and TUT3.ASM, examine both .LST disk files it
\par generates during assembly. You will see from the coding that the
\par substitution of numbers in place of the names has occurred, and that the
\par two .LST files are identical, except for the last two (new) commands of
\par TUT3. (Note that TK3 generates a single TK3TEMP.LST file and to examine the
\par .LST files of the two source code assemblies, it is necessary to print each
\par to paper once created.)
\par 
\par The .LST files will also include the programmer's notes at the right. They
\par have been omitted here to conserve space. The lefthand column holds the line
\par numbers as encountered from the text file listing (.ASM); these serve no
\par programming purpose and are simply there for the Assembler's benefit. They
\par are in decimal. (TK3 shows more columns than are stated here.)
\par 
\par The second column of four digits is the actual byte location number within
\par the PIC at which the command will be placed. Note that it starts at 0000,
\par which is repeated here three times simply because there is text on the rest
\par of the line which is not a program command.
\par 
\par Note that the address number then jumps straight to 0004, the address to
\par which the .ORG 4 statement refers. Code .ORG 5 states that the program
\par itself is to start at location 5, which it does (0005) but columns three
\par and four, which hold the 2-byte code, are not used until a command is
\par encountered. In this case, command CLRF PORTB generates the code 01 86.
\par 
\par Although it may not immediately be evident, the numbers in columns 2 to 4
\par are in hexadecimal. Also, note that the two bytes of columns 3 and 4 will
\par ultimately be combined into one 14-bit byte when the code is loaded into
\par the PIC16x84 by the software.
\par 
\par \plain\f2\fs24\b TUT3 Forever!\plain\f2\fs20 
\par 
\par Now load the code for TUT3.OBJ into your PIC and run it. It will be seen to
\par start off in the same way as TUT2. Now, though, when it gets to the end of
\par the code, the LEDs will all go out and the sequence will repeat,
\par indefinitely! You will find that a fully-clockwise setting of VR1 is
\par preferable, to increase the display rate.
\par 
\par The program difference now is that there are two extra commands and when
\par command BSF PORTB,7 has been performed, Port B is cleared and the program
\par follows the command GOTO LOOPIT. LOOPIT is the name given to the address at
\par which the command BSF PORTB,0 has been placed. During assembly that address
\par number has been noted and each time the assembler encounters a command
\par reference to the address named LOOPIT, it substitutes the number for that
\par address.
\par 
\par Names, when given to program listing addresses, as with LOOPIT here, are
\par commonly known as "labels". Labels must always be followed by a colon (:).
\par Referring to the .LST listing for TUT3, the last line reads:
\par 
\par 0017 0009 14 06 LOOPIT: BSF PORTB,0
\par 
\par Note the 0009 in column 2. Later there is another statement:
\par 
\par 0026 0012 28 09 GOTO LOOPIT
\par 
\par Now note the 09 in column 4. The two values are identical and intentional.
\par The address for which LOOPIT is the reference name is at location 0009; the
\par code 28 09 contains the instruction to GOTO (jump to), plus the address
\par number to which it is to jump.
\par 
\par In other instances, the addresses may be much greater than the one
\par illustrated here, and the two values will differ accordingly, but the point
\par is that the name LOOPIT is replaced by an address value during assembly and
\par as such is treated by the Assembler in the same way as were STATUS and
\par PORTB.
\par 
\par This fact has another important significance: when a number has a name
\par allocated to it, each time the Assembler encounters that name it
\par substitutes the appropriate number. Consequently, the same name could, if
\par you wished, be used for different purposes in the same program. It could be
\par used as an address, or it could even be used as a bit number. It would be
\par legitimate, for example, to replace the command BSF PORTB,3 by the
\par statement BSF PORTB,STATUS, because STATUS is the name allocated to be used
\par as a substitute for numeral 3.
\par 
\par This particular example might never have relevance in practice, and could
\par even be misleading to anyone reading the program, but a similar situation
\par might be beneficially used in other programs. We shall see other examples
\par of names being used as pointers to addresses when the address required may
\par depend on a particular value established as a result of calculation, i.e.
\par in the case of Indirect Addressing, which will be examined in Tutorial 16.
\par 
\par \plain\f2\fs24\b Exercise 3\plain\f2\fs20 
\par 
\par 3.1. Do the same sort of program modifications that you did with Exercise
\par 2, examining the results achieved. Also try changing the position of the
\par LOOPIT address in the lefthand column, putting it alongside BSF PORTB,2 for
\par example.
\par 
\par 3.2. The command GOTO LOOPIT can also be put elsewhere; try putting it
\par between BSF PORTB,4 and BSF PORTB,5 and see what the result is.
\par 
\par There is an easy way of moving it in this instance without actually doing
\par so: put a semicolon (;) in front of the three lines following BSF PORTB,4.
\par The Assembler will then treat these lines as comments and ignore them. The
\par use of a semicolon is a handy way to temporarily omit commands when
\par debugging programs.
\par 
\par 3.3. What happens if a semicolon is put in front of any of the three CLRF
\par PORTB commands?
\par 
\par 3.4. What happens if the GOTO LOOPIT command is placed before the LOOPIT
\par address?
\par 
\par \plain\f2\fs28\b TUTORIAL 3
\par \plain\f2\fs20 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Double-naming numbers
\par Bit naming
\par Bit codes C, F, W
\par Bit testing
\par Conditional loop
\par Instructions PAGE0 and PAGE1
\par Instruction #DEFINE
\par Command MOVLW
\par Command MOVWF
\par Command RLF
\par Command RRF
\par Command BTFSS
\par Command BTFSC
\par Command PCL
\par Program counter
\par Register PORTB
\par Register TRISB
\par 
\par \plain\f2\fs24\b LISTING 4 - PROGRAM TUT4
\par \plain\f2\fs20 
\par TUT4.ASM
\par 
\par ; using aliases, bit names and conditional loops
\par 
\par #DEFINE PAGE0 BCF STATUS,5
\par #DEFINE PAGE1 BSF STATUS,5
\par \tab .AVSYM
\par 
\par STATUS:\tab .EQU $03\tab \tab ; STATUS register
\par TRISB:\tab .EQU $06\tab \tab ; Port B direction register
\par PORTB:\tab .EQU $06\tab \tab ; Port B data register
\par C:\tab       .EQU 0\tab       ; Carry flag
\par W:\tab       .EQU 0\tab \tab ; Working register flag
\par F:\tab \tab .EQU 1\tab       ; File register flag
\par \tab       .ORG 4            ; Interrupt vector address
\par \tab       .ORG 5            ; Start of program memory
\par \tab \tab CLRF PORTB\tab \tab ; clear Port B data register
\par \tab       PAGE1       \tab ; PAGE1
\par \tab       CLRF TRISB      \tab ; set all Port B as output (clear direction reg)
\par \tab       PAGE0\tab \tab       ; PAGE0
\par LOOP1:\tab MOVLW 1\tab       ; load value of 1 into Working register
\par \tab       MOVWF PORTB       ; load this value as data into Port B
\par \tab \tab BCF STATUS,C\tab ; clear Carry flag
\par LOOP2:\tab RLF PORTB,F       ; rotate value of PORTB left by 1 logical place
\par \tab       BTFSS STATUS,C    ; check if the Carry flag (bit 0) of the STATUS
\par \tab       GOTO LOOP2        ; command is actioned only if PORTB is not yet 0
\par \tab                         ; the program jumping back to address LOOP2
\par \tab       GOTO LOOP1        ; command is actioned only when PORTB now = 0
\par \tab      .END               ; final statement
\par 
\par \plain\f2\fs24\b Table: Special Function Registers\plain\f2\fs20 
\par 
\par REGISTER  ADDRESS   BANK    TUTORIAL
\par EEADR     09        0       26
\par EECON1    08        1       26
\par EECON2    09        1       26
\par EEDATA    08        0       26
\par FSR       04        0,1     16
\par INDF      00        0,1     16
\par INTCON    0B        0,1     18
\par OPTION    01        1       16
\par PCL       02        0,1      4
\par PCLATH    0A        0,1     14
\par PORTA     05        0        4
\par PORTB     06        0        4
\par STATUS    03        0,1      2
\par TMR0      01        0       18
\par TRISA     05        1        4
\par TRISB     06        1        4
\par 
\par \plain\f2\fs24\b Instructions #DEFINE and PAGE
\par \plain\f2\fs20 
\par One concept that you are likely to see in PIC software is that of defining
\par a frequently used command format as a single name. Each time the Assembler
\par encounters that name during assembly, the defined command will be
\par substituted in the coding. Two such definitions appear in Listing 4:
\par 
\par #DEFINE PAGE0 BCF STATUS,5
\par #DEFINE PAGE1 BSF STATUS,5
\par 
\par The command PAGE0 is then used each time the programmer would otherwise key
\par in BCF STATUS,5. Likewise with PAGE1. It's not only shorter, but conveys
\par another concept more clearly than would direct manipulation of STATUS bit
\par 5, that of Pages (or Banks), which were referred to in passing earlier. We
\par have seen that STATUS bit 5 switches between addresses $00 to $7F and $80
\par to $8C. In fact, the latter extends to $FF, but addresses greater than $8B
\par are not available to the programmer. Writing to them simply wraps them back
\par to an address $80 bytes earlier.
\par 
\par All of the Special Function registers, of which so far we have met five,
\par are held between $00 to $0B and $80 to $8B (see Table).
\par 
\par It makes for an easy shorthand way of defining which group is which if we
\par refer to the lower group as being in PAGE0, and those of the second group
\par as being in PAGE1. In fact, any other names could be used, as with named
\par numerals, but page numbering from zero is a common situation in computing
\par generally. This, then, is why the terms PAGE0 and PAGE1 have been defined
\par as above: it's simply an easy to remember convenience.
\par 
\par In Listing 4, following the two definitions we see STATUS and PORTB being
\par nominated (equated) as in Listing 3, representing $03 and $06 respectively.
\par The name TRISB has crept in, though, and it also relates to $06. Why two
\par names for the same number? It is done for the convenient reason that we
\par know address $06 relates to registers which appear in PAGE0 and PAGE1, but
\par which have different functions, Port B's data and direction registers,
\par respectively. It saves confusion, therefore, to have a different name for
\par each, even though their address numeral is the same. The name TRISB is
\par given to Port B's direction register since this is the name given to that
\par address in the PIC Data Book. The name PORTB now simply refers to Port B's
\par data register. Exactly the same convention can be applied to Port A, using
\par PORTA and TRISA as the names.
\par 
\par (Incidentally, as mentioned earlier, there is a command TRIS available as
\par part of the PIC16x84's command set, as shown in the PIC Data Book.
\par Microchip recommend that it should not be used since it has been deleted
\par from chips later than the PIC16x84. The same applies to the command OPTION.
\par Neither of these commands will be discussed here. You will see the use of
\par TRISA, TRISB and OPTION in this Tutorial, but the terms are used as
\par Register file names, not as commands.)
\par 
\par Where Special Function registers have had their functions equated to a name
\par that is similar, henceforth the new name will be used. For example, Port B
\par will be referred to as PORTB, Port A as PORTA and Status as STATUS.
\par Additionally, in order to avoid repetition of comments made in earlier
\par listings, from now on listing comments will not always be shown here for
\par situations that have previously been discussed. The listings on the disk,
\par however, show comments where appropriate. You will have noticed that the
\par use of hexadecimal notation is gradually being introduced for some numbers.
\par 
\par \plain\f2\fs24\b Bit Names\plain\f2\fs20 
\par 
\par All the numerals to which names have been allocated so far have been
\par related to file byte addresses. It is equally possible to allocate names to
\par particular bits in a file byte. This is especially useful when individual
\par bits of particular files perform specific functions. We could, for example,
\par call STATUS bit 5, PAGE or bit P, equating it to 5 at the head of the
\par listing, as with other names:
\par 
\par P: .EQU 5
\par 
\par In this instance, however, it is of no benefit to do so because an
\par equivalent shorthand now exists through the PAGE definition. It is
\par beneficial, though, to several other file bit uses, three of which are
\par shown in Listing 4:
\par 
\par C: .EQU 0
\par W: .EQU 0
\par F: .EQU 1
\par 
\par \plain\f2\fs24\b Bit Names F and W\plain\f2\fs20 
\par 
\par We have already said that data can be routed either to files or retained in
\par the Working register. A single bit code, either 0 or 1, determines which
\par destination. This bit value statement is required following the comma used
\par with some commands. For example, take the two similar commands RLF PORTB,0
\par and RLF PORTB,1, the command RLF (which is discussed in a moment) tells the
\par PIC that the value within the file then stated (in this case the file is
\par PORTB) is to be rotated left (multiplied by two). The result of this
\par rotation can either be put back into PORTB, using the 1 suffix, or held in
\par the Working register for further use, using the 0 suffix. If the Working
\par register is chosen, the value in PORTB remains as it was.
\par 
\par Again for easy human understanding, it is more convenient to give a name to
\par the different conditions than having to remember numbers. So the file
\par destination 1 is called F for File, and the Working destination 0 is called
\par W for Working. All very logical and clear! The two example commands thus
\par become RLF PORTB,W and RLF PORTB,F.
\par 
\par \plain\f2\fs24\b Carry Flag Bit C\plain\f2\fs20 
\par 
\par One bit of STATUS, bit 0, is the bit which indicates whether a Carry or a
\par Borrow has occurred during some commands. (It is, incidentally, common to
\par refer to such bits as being "flags": the flag is then said to be set or
\par cleared by any action which affects it.) The Carry flag is frequently
\par required to be read in most programs and it is convenient to also give it a
\par name, in this case C, hence the setting-up statement:
\par 
\par C: .EQU 0
\par 
\par The bit can be manipulated or tested by commands such as BCF STATUS,C or
\par BTFSS STATUS,C (discussion of BTFSS comes in a jiffy or two).
\par 
\par Before going any further with the contents of Listing 4, load its code into
\par your PIC and run it (TUT4.OBJ). What you will see is that individual LEDs
\par on PORTB are being turned on at the same time the preceding one is turned
\par off. The movement will appear to be going from right to left, from bit 0 to
\par bit 7 (LB0 to LB7), and restarting at bit 0. There are several ways of
\par doing this (and many reasons why you should need to). Two programming
\par techniques are discussed here, the one in Listing 4, and then a much
\par shorter one later in Listing 5. The one in Listing 4 demonstrates the use
\par of the commands MOVLW, MOVWF, RLF, BTFSS, and how two loops can be "nested"
\par and made dependent upon each another.
\par 
\par \plain\f2\fs24\b Commands RLF and RRF\plain\f2\fs20 
\par 
\par Many of you will be familiar with the electronic concept of shift register
\par chips. Data can be loaded into the register either serially (bit entry) or
\par in parallel (byte entry). The data can be shifted to the "left" or "right"
\par in the chip, in response to a clock signal. The shifted data can then be
\par made available either serially as bits, or in parallel as a byte. When data
\par is shifted left and read as a byte (parallel output), each shift has the
\par effect of multiplying the data by two. Shifting to the right divides it by
\par two.
\par 
\par Take the 8-bit binary code 00000100 (decimal 4), for example. If this is
\par shifted left by one place, the result is 00001000 (decimal 8). If the code
\par had been shifted right by one place, the result would be 00000010 (decimal
\par 2). Most files within the PIC16x84 are capable of having their data shifted
\par (rotated) to the left or to the right (although doing so on the special
\par registers may sometimes produce unpredictable results). The two commands
\par are RLF and RRF (Rotate Left File and Rotate Right File). Both commands
\par have to be followed by the file which is to have its data rotated, then a
\par comma and then the destination, either F or W. For example: RLF PORTB,F or
\par RLF PORTB,W. If the W destination is chosen, the original contents of the
\par file remain intact (the result going into W); they are only changed if the
\par F suffix is used.
\par 
\par There are two problems associated with rotating a file's contents left or
\par right. For the first, consider the situation when a file (for the sake of
\par example, call it PORTB) contains a value such as 11010111 (decimal 215);
\par there are many numbers that could illustrate the point about to be made.
\par Suppose the rotate left command RLF PORTB,F is given, all bits are rotated
\par left by one place. The value retained in PORTB becomes 10101110 (decimal
\par 174) which is definitely not 2 x 215; the original lefthand bit has
\par vanished from this 8-bit byte - a 9-bit byte would be needed to show the
\par correct answer.
\par 
\par \plain\f2\fs24\b Right and Carry\plain\f2\fs20 
\par 
\par Alternatively, suppose the rotate right command RRF PORTB,F is given, all
\par bits are rotated right by one place. The value retained in PORTB becomes
\par 01101011 (decimal 107), which is definitely not 215/2; the original
\par right-hand bit has vanished from this 8-bit byte. In some cases, of course,
\par the intention of rotating left or right may have nothing to do with
\par multiplying a value by 2. It may be that we simply want to change the
\par position of the bits for another purpose, such as changing the commands
\par sent to the outside world to turn equipment on or off. In this case, the
\par arithmetic accuracy of the rotate result would be immaterial.
\par 
\par The other problem (although it can be used beneficially) is that bits
\par rotated out from either end of the byte are rotated into the Carry bit of
\par STATUS. Simultaneously, the previous bit held in the Carry bit is rotated
\par into the byte at the other end. Supposing that the Carry bit is initially
\par zero, in the first RLF example above, the original value of 11010111 would
\par be rotated left and the result would be correct as shown (10101110) because
\par the 0 has come in to the right from the Carry bit. However, the last
\par lefthand bit of the original value (which is a 1) would now be in the Carry
\par bit.
\par 
\par Suppose then that another rotate left is made. The bits within PORTB would
\par be rotated left but, at the same time, the Carry bit from the previous
\par rotation would now be rotated into PORTB from the right. The value held in
\par PORTB thus becomes 01011101 (decimal 93), and again the Carry bit now holds
\par the 1 from PORTB bit 7. Therefore, the next rotation will result in an
\par answer of 10111011 (decimal 187).
\par 
\par To avoid a set Carry bit (which retains the status last acquired anywhere
\par in the program) being rotated automatically into a file byte from the other
\par end, the Carry bit can be cleared by the command BCF STATUS,C prior to each
\par rotate command, unless, of course you want a set Carry bit rotated into a
\par byte.
\par 
\par Referring again to the display you see on the LEDs at the moment,
\par controlled by TUT4, the Carry bit clearing technique is being used
\par immediately prior to the RLF command. We shall see what happens if the
\par Carry is not cleared when we view TUT5 later.
\par 
\par \plain\f2\fs24\b Command MOVLW\plain\f2\fs20 
\par 
\par In Listing 4 is the command MOVLW 1. The MOVLW command (MOVe Literal value
\par into W) is the command which allows literal values (numbers) contained
\par within the program itself to be moved (copied) into the Working register
\par for further manipulation. The range of values is from 0 to 255, i.e. an
\par 8-bit byte. Command MOVLW 1 instructs that the value of 1 is to be moved
\par into W. Literal values may be expressed in decimal, hexadecimal or binary.
\par The following statements are equivalent in their result:
\par 
\par MOVLW 73 (decimal)
\par MOVLW $49 (hexadecimal)
\par MOVLW %01001001 (binary)
\par 
\par Literals may also be the address values of other files whose names have
\par been specified at the head of the program, or they may be the values
\par assigned to be represented by other words or letters. The following are all
\par legal commands:
\par 
\par MOVLW STATUS
\par MOVLW PORTB
\par MOVLW W
\par MOVLW LOOP1
\par 
\par Respectively, the commands would move into W the address value of STATUS
\par (which we have specified as $03), the address value of PORTB ($06), the
\par value assigned to be represented by W (0), the address within the program
\par at which the command line prefaced by LOOP1 resides (a value known only to
\par the program - unless you examine the .LST file).
\par 
\par An important point about any of the Move commands, such as MOVLW, MOVWF and
\par MOVF is that the original value (source value) itself remains where it is
\par and is unchanged. The value is simply "copied" into the destination
\par specified. Having moved a literal value into W it can then be immediately
\par moved into a specified file destination, or it can be used as part of a
\par further manipulation.
\par 
\par \plain\f2\fs24\b Command MOVWF\plain\f2\fs20 
\par 
\par Following the MOVLW 1 command in Listing 4 is the command MOVWF PORTB.
\par Command MOVWF (MOVe W into File) simply copies the contents of the W
\par register into the file specified, in this case PORTB. Apart from the
\par destination statement, no commas or other statements are needed (or
\par allowed) with this command. The MOVWF command is the only way in which full
\par bytes of data can be copied from W into other destinations. As used in
\par Listing 4, it is the value of 1 which is copied.
\par 
\par \plain\f2\fs24\b Command BTFSS\plain\f2\fs20 
\par 
\par Another command we are introducing in Listing 4 is BTFSS, Bit Test File
\par Skip if Set. What BTFSS does is to examine the status of the file bit
\par specified in the remainder of the command (bit C of STATUS in this case:
\par BTFSS STATUS,C). The word Set now becomes the important one. The PIC is
\par being asked to test if the bit specified is Set (i.e. is it logic 1?).
\par There can only be one of two answers, either "yes" or "no". In programming
\par (and digital electronics too) if the answer is "yes", then the answer is
\par said to be "true". If the answer is "no", then the answer is said to be
\par "false" (not true).
\par 
\par Now we come to a situation which some find difficult to grasp until they
\par understand "what" happens when the validity of the question has been
\par established. It's simple, though, once the facts are known!
\par 
\par The convention is that if a situation is "true" then it can be represented
\par by logic 1, Conversely, if the situation is "false" it can be represented
\par by logic 0. Logic 1 and logic 0 are, of course, the two states in which a
\par binary bit can be. Hold this idea in your mind for a moment and consider
\par the next fact.
\par 
\par We have seen that programs are stored as instructions in consecutive bytes.
\par It has been shown that these bytes are numbered from zero upwards (Listing
\par 3A). Microcontrollers such as the PIC16x84 keep track of which program byte
\par number is currently being processed, and there is a counter which holds
\par this information - the Program Counter (PCL, as it is named for the PIC,
\par Program Counter Literal). Unless told otherwise, when one instruction has
\par been performed, the program counter is simply incremented (a value of 1
\par added to it) and the next consecutive command is performed.
\par 
\par \plain\f2\fs24\b Change of Address\plain\f2\fs20 
\par 
\par The program address number held by the PCL can be changed, either when the
\par instruction is one such as GOTO or CALL, or by the user telling it to add
\par another literal value to itself. The next instruction performed is that at
\par the address pointed to by the new value. It will be seen, then, that if the
\par value of 0 is added to the PCL, the next instruction is simply the next one
\par on. If, however, the value of 1 is added to the PCL, then the next
\par consecutive instruction is bypassed (skipped) and the one beyond it is
\par performed instead.
\par 
\par For example, if the program counter is at 12, then normally it will
\par automatically add one to itself and the next instruction will be that at
\par 13, and the one after that will be at 14, etc. If, somehow, we intervene
\par and add 1 to the counter while it's still 12, the counter will become 13
\par but will still add its own value of 1 to itself, making 14. The program
\par will thus jump straight from 12 to 14, omitting the instruction at 13.
\par Should the value of 0 be added, then, of course, the program will go
\par straight from 12 to 13.
\par 
\par Coming back to BTFSS, we know that the answer will be either 0 or 1. When
\par the PIC performs the BTFSS command, the answer is automatically added to
\par the PCL. Therefore, still assuming a PCL starting value of 12, if the
\par answer is true (1), the PCL has 1 added to it and so the next instruction
\par performed is that at 14, as above. If the answer is false (0), then zero is
\par added to PCL and so the instruction at 13 is performed, again as above.
\par 
\par Look again at Listing 4: we see the command BTFSS STATUS,C, i.e. we are
\par checking if bit C of STATUS is set (true). If if is true that the bit is
\par set, then the 1 of the truth answer is added to PCL and so the command GOTO
\par LOOP2 is bypassed and that which says GOTO LOOP1 is performed. If STATUS
\par bit C is not set (false) then the program simply takes GOTO LOOP2 as the
\par next command because the 0 of the false answer is added to PCL. OK so far?
\par 
\par \plain\f2\fs24\b Command BTFSC\plain\f2\fs20 
\par 
\par While we still have this concept in our minds, let's look at the command
\par which is the opposite of BTFSS, namely BTFSC (Bit Test File Skip if Clear).
\par What this command does is to check if it is true that the bit being tested
\par is clear (0). If it is true that the bit is clear, then the answer is 1. If
\par it is false that the bit is clear (that the bit is not 0, but 1), then the
\par answer is 0.
\par 
\par Let's see what happens in Listing 4 if we replace BTFSS by BTFSC. BTFSC
\par STATUS,C tests the C bit to find out if it is true that it is clear. If it
\par is true, 1 is added to PCL and command GOTO LOOP1 is performed. If it is
\par false that bit C is clear, then 0 is added to PCL and so GOTO LOOP2 is
\par performed.
\par 
\par We have, perhaps, somewhat laboured this explanation, but the concept of
\par bit testing and the resulting action is one which causes some people
\par problems, especially when testing for a bit being clear. Why, they ask, is
\par it that the answer is 1 if the tested bit is zero? Why does 1 equal 0? It
\par doesn't, what you are looking for is the truthful answer to the question
\par posed. Think about the question, think about the answer to it. It is an
\par important concept to grasp, and there are other situations where it occurs:
\par when testing the digit carry and zero flags of the STATUS register (bits 1
\par and 2, respectively). We shall encounter those situations in Tutorials 5
\par and 7.
\par 
\par \plain\f2\fs24\b Listing 4 Again\plain\f2\fs20 
\par 
\par What you see the program of Listing 4 doing is the simple action of
\par repeatedly "moving" an LED from right to left. There are only seven
\par commands involved, yet, as witnessed by the length of discussion so far,
\par there are several important commands and their concepts to be fully
\par understood.
\par 
\par Let's relate those commands in simple terms to what is happening in the
\par program. First, at label LOOP1 the value of 1 is moved into W, this is then
\par moved into PORTB, setting its bit 0 to 1 and clearing bits 1 to 7. As a
\par result, the first LED at the right is turned on (LB0) and the others (LB1
\par to LB7) are turned off. In binary, PORTB's value is now 00000001. Next, the
\par Carry bit of STATUS is cleared to prevent it from interfering with the
\par results of the rotate-left command that follows at label LOOP2 (as
\par discussed earlier). You will see that this command is RLF PORTB,F. The F
\par suffix means that the result of the rotation is retained in PORTB, and the
\par contents of PORTB will have shifted so that the second LED (LB1) has come
\par on because the 1 previously set by the MOVWF command has shifted from
\par PORTB's bit 0 to its bit 1. Since the Carry bit was previously cleared, 0
\par is moved into PORTB bit 0, turning off LED LB0. The binary value has become
\par 00000010.
\par 
\par Now the value of the Carry bit in STATUS is checked to see if a 1 has been
\par shifted out from PORTB bit 7. In fact, it cannot have occurred yet since it
\par takes eight shifts to bring the 1 from the right and into Carry. However,
\par the PIC is not aware of that fact, so the Carry bit has to be checked
\par following each shift left. If the Carry bit is not yet set, the command
\par GOTO LOOP2 is performed, the program jumps back to that position and the
\par RLF command is again actioned. As a result the third LED (LB2) will come on
\par and the second LED (LB1) will go out, binary 00000100.
\par 
\par Eventually, after eight shifts, the 1 will have shifted through all eight
\par bits of PORTB and into the Carry bit. At this point, there will be no bits
\par set in PORTB, and so no LEDs will be on. Now, on the test for the Carry bit
\par being set, the answer will be true, command GOTO LOOP2 will be bypassed and
\par the command GOTO LOOP1 will be performed, the program jumping to that
\par label. The whole sequence then recommences by a 1 again being loaded into
\par PORTB bit 0. As written, the program will repeat until the PIC is switched
\par off or the Reset switch is used.
\par 
\par \plain\f2\fs24\b Exercise 4\plain\f2\fs20 
\par 
\par 4.1. What do you think will be the LED display sequence if another value is
\par loaded into PORTB via the MOVLW command? Try any multiple of 2; then try
\par any value that has more than one bit set, using the binary format, e.g.
\par %01001100.
\par 
\par 4.2. Also see what happens if the command RRF PORTB,F is used instead of
\par RLF PORTB,F. What do think will happen if you replace PORTB,F by PORTB,W?
\par Then see what happens if BTFSC is used instead of BTFSS? (It is a common
\par mistake to use the wrong command in this sort of situation.) Now swap the
\par two commands GOTO LOOP2 and GOTO LOOP1.
\par 
\par 4.3. Just out of interest, also try deleting the command BCF STATUS,C (just
\par put a semicolon in front of it).
\par 
\par \plain\f2\fs24\b LISTING 5 - PROGRAM TUT5\plain\f2\fs20 
\par 
\par ; TUT5.ASM
\par 
\par ; Showing how Carry bit rotates into register
\par 
\par #DEFINE PAGE0 BCF STATUS,5
\par #DEFINE PAGE1 BSF STATUS,5
\par 
\par STATUS:\tab .EQU $03
\par TRISB:\tab .EQU $06
\par PORTB:\tab .EQU $06
\par W:\tab \tab .EQU 0
\par F:\tab \tab .EQU 1
\par C:\tab \tab .EQU 0
\par \tab \tab .ORG 4
\par \tab \tab .ORG 5
\par 
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISB
\par \tab \tab PAGE0
\par \tab \tab BSF STATUS,C
\par LOOP:\tab \tab RLF PORTB,F
\par \tab \tab GOTO LOOP
\par \tab \tab .END
\par 
\par \plain\f2\fs24\b A Simpler Rotation\plain\f2\fs20 
\par 
\par Load TUT5.OBJ and run it. It will be seen to be shifting the LED display to
\par the left, as occurred when TUT4.ASM was first run as TUT4.OBJ (before you
\par started changing it - though, hopefully, you saved each variant under a
\par different name). You should notice that TUT5 is running a bit faster than
\par TUT4 did. This is because there are now fewer commands to process for the
\par same result. Simplicity of code usually makes for faster processing speeds
\par (or, rather, the fewer commands that need to be processed to perform a
\par particular function, will result in a faster processing speed). Look at
\par Listing 5 and you will see how few commands there are in the loop, just
\par two. Let's examine the program flow.
\par 
\par Everything up to the statement PAGE0 is the same as in TUT4. Then advantage
\par is taken of the fact that a set Carry bit will be shifted into a file when
\par it is rotated left or right; the command BSF STATUS,C is given before the
\par loop, so setting the Carry bit. Now when PORTB is rotated left with the
\par command RLF PORTB,F, the Carry bit comes straight into PORTB bit 0, turning
\par on LED LB0. Simultaneously, the Carry bit is cleared (remember why?). The
\par next command is GOTO LOOP, which the program does, again to rotate PORTB,
\par causing LB1 to come on and LB0 to go out. For eight rotations left, the
\par Carry bit remains clear, then on the ninth rotation the original 1 that has
\par traversed PORTB will drop into the Carry bit, to be rotated back into PORTB
\par on the next rotation. And so it goes on, indefinitely.
\par 
\par There are numerous situations in which rotation occurs and when the setting
\par of the Carry bit is desirable. In this way, several files can be coupled as
\par a very long shift register, e.g.:
\par 
\par BSF STATUS,C
\par RLF FILE1,F
\par RLF FILE2,F
\par RLF FILE15,F
\par 
\par \plain\f2\fs24\b Exercise 4 Continued\plain\f2\fs20 
\par 
\par 4.4. What happens if you add another RLF PORTB,F after the first? And if
\par you add a third RLF PORTB,F?
\par 
\par 4.5. What happens if you substitute a W for the F in one of the statements?
\par 
\par 4.6. What happens if you put the command RLF PORTA,F after the RLF PORTB,F
\par command? Why does the sequence not repeat with PORTA rotating back into
\par PORTB?
\par 
\par \plain\f2\fs28\b Tutorial 5
\par \plain\f2\fs20 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par STATUS bit 2
\par Zero flag
\par Command MOVF
\par 
\par \plain\f2\fs24\b LISTING 6 - PROGRAM TUT6
\par \plain\f2\fs20 
\par ; TUT6.ASM
\par ; Using RRF and Z
\par ; Status bit 2
\par ; Zero flag use
\par ; Command MOVF
\par 
\par #DEFINE PAGE0 BCF STATUS,5
\par #DEFINE PAGE1 BSF STATUS,5
\par 
\par STATUS:\tab .EQU $03
\par TRISB:\tab .EQU $06
\par PORTB:\tab .EQU $06
\par W:\tab \tab .EQU 0
\par F:\tab \tab .EQU 1
\par C:\tab \tab .EQU 0
\par Z:\tab \tab .EQU 2
\par 
\par \tab \tab .ORG 4
\par \tab \tab .ORG 5
\par 
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISB
\par \tab \tab PAGE0
\par LOOP1:\tab MOVLW 128
\par \tab \tab MOVWF PORTB
\par \tab \tab BCF STATUS,C
\par LOOP2:\tab RRF PORTB,F
\par \tab \tab MOVF PORTB,F
\par \tab \tab BTFSS STATUS,Z
\par \tab \tab GOTO LOOP2
\par \tab \tab GOTO LOOP1
\par \tab \tab .END
\par 
\par It is appropriate at this moment to introduce a command allied to the Carry
\par bit tests, testing the Zero flag bit of the STATUS register. This is bit 2
\par and in the heading of program TUT6, shown in Listing 6, the letter Z has
\par been equated to it:
\par 
\par Z: .EQU 2
\par 
\par The two opposite commands for zero testing are BTFSS STATUS,Z and BTFSC
\par STATUS,Z, identical to the Carry checking commands except for the change of
\par final letter.
\par 
\par We also take the opportunity to formally demonstrate command RRF (Rotate
\par Right File). It was described in Tutorial 4, but not shown. You probably
\par used it, though, when experimenting with Exercise 4. Thirdly, the command
\par MOVF is introduced and demonstrated. Run TUT6.OBJ and refer to Listing 6.
\par 
\par The LED display controlled under TUT6 should be seen to be rotating right,
\par but otherwise the display repetition should be as seen in TUT4 and TUT5.
\par The program opens up with the necessary initialisation commands. The
\par command at LOOP1 is then seen to be MOVLW 128 instead of the previous MOVLW
\par 1. Decimal value 128 is 10000000 in binary and thus the 1 is at the left of
\par the byte. This is moved into PORTB and the Carry bit is cleared, both
\par commands as in Listing 4. At LOOP2, command RRF PORTB,F now replaces RLF
\par PORTB,F, instructing the program to rotate to the right, the 1 moving
\par progressively from bit 7 to bit 0 and then into the Carry bit. Next comes
\par MOVF PORTB,F. Let's examine it.
\par 
\par \plain\f2\fs24\b Command MOVF\plain\f2\fs20 
\par 
\par Whereas MOVLW means moving a literal value into W, MOVF means MOVe File
\par value. The file (PORTB in this case) is named following the command, but
\par the command itself does not say where the value is to be moved (unlike
\par MOVLW, where W as the destination is included in the command). The
\par destination is stated by adding a comma after the file name and then adding
\par either W or F, e.g.:
\par 
\par MOVF PORTB,W or
\par MOVF PORTB,F
\par 
\par Normally, the command would be used with W, so that the contents of the
\par file are brought into W for presumed further use. At first, then, the
\par concept of using F as the destination seems strange. Why move the file
\par value back into the file without the value having undergone some sort of
\par manipulation? The reason is that many commands automatically affect various
\par flags in the STATUS register (see Table 1), setting or clearing them as
\par appropriate. We have already seen the Carry flag being affected by RLF and
\par RRF, but the Zero flag is not affected by these two commands (regrettably),
\par so a different technique has to be used to check for zero.
\par 
\par When command MOVF is performed, irrespective of the W or F destination, the
\par Zero flag is affected. It is set if the file value is zero, cleared if the
\par file value is greater than zero. So, if we wish to know whether or not the
\par file value is zero, we can use the MOVF command to affect the zero flag,
\par and do so without changing the file value. That is what is happening in
\par Listing 6, moving F back into F to affect the Z flag, which is about to be
\par tested in the next command, BTFSS STATUS,Z. What is being looked for is
\par PORTB's value becoming zero after the 1 has exited from the right of the
\par file (from bit 0).
\par 
\par The logic of BTFSS STATUS,Z is the same as that for the Carry flag. We are
\par looking for the truthful answer to a question, in this case is it true (1)
\par that the Zero flag is set (1)? The answer will only be true if the file
\par value is zero (0) - another of those concepts which some people may find
\par difficult to comprehend, a 1 being used to mean the presence of 0. If the
\par file value is greater than zero, i.e. does not equal 0, then the answer is
\par false (0) and so the Zero flag is cleared (0). As with Carry testing, the
\par result of the Zero test (1 or 0) is added to the program counter (PCL) and,
\par depending on the result, either GOTO LOOP2 (Z = 0) or GOTO LOOP1 (Z = 1) is
\par the command actioned. Consequently, LOOP2 commands will be cycled through
\par eight times before a jump is again made to LOOP1.
\par 
\par \plain\f2\fs24\b Exercise 5\plain\f2\fs20 
\par 
\par 5.1. Prove that the Zero flag is affected by the command MOVF PORTB,W as
\par well as MOVF PORTB,F (the proof is that the rotation is the same as
\par before).
\par 
\par 5.2. What happens if the 128 of command MOVLW 128 is replaced by another
\par number? Experiment with different values.
\par 
\par \plain\f2\fs28\b Tutorial 6\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED
\par \plain\f2\fs20 
\par Command INCF
\par Command DECF
\par Command INCFSZ
\par Command DECFSZ
\par Counting upwards (incrementing)
\par Counting downwards (decrementing)
\par Use of a file as a counter
\par 
\par \plain\f2\fs24\b LISTING 7 - PROGRAM TUT7
\par \plain\f2\fs20 
\par BEGIN:\tab CLRF COUNT
\par LOOP1:\tab MOVF COUNT,W
\par \tab \tab MOVWF PORTB
\par \tab \tab INCFSZ COUNT,F
\par \tab \tab GOTO LOOP1
\par LOOP2:\tab MOVF COUNT,W
\par \tab \tab MOVWF PORTB
\par \tab \tab DECFSZ COUNT,F
\par \tab \tab GOTO LOOP2
\par \tab \tab GOTO LOOP1
\par 
\par From hereon we shall usually omit the program initialisation commands that
\par have up to now been shown at the top of each listing. Some will be included
\par where they help to clarify the program. Otherwise, assume that any name
\par used in the listing extracts shown will have been defined or equated in the
\par headings. The commands are included in full on the disk file program
\par listings. Load TUT7.OBJ and run it, then refer to Listing 7.
\par 
\par There are now two new commands illustrated in TUT7, INCFSZ (INCrement File
\par Skip if Zero) and DECFSZ (DECrement File Skip if Zero). Two allied
\par commands, INCF (INCrement File) and DECF (DECrement File), will also be
\par examined. The ability to increment (add one to) a value, or decrement it
\par (subtract one from) has wide benefits in programming. Two such instances
\par are keeping track of events through the use of counters, and of changing
\par the values of flag bits (when in bit 0).
\par 
\par \plain\f2\fs24\b Commands INCF and DECF\plain\f2\fs20 
\par 
\par The concept of commands INCF and DECF are extremely easy to follow. The
\par first simply adds 1 to a file value, the other simply deducts 1 from a file
\par value. If the file value is 255 (11111111 binary) when INCF is called, the
\par value rolls over to zero. If the file value is zero when DECF is called,
\par the value rolls over to 255. Whenever the result of INCF or DECF is zero,
\par the Zero flag is set, otherwise it is cleared. Testing of the Zero flag can
\par be performed using BTFSS or BTFSC as discussed in Tutorial 5.
\par 
\par Taking PORTB again as the example file, the command formats are INCF
\par PORTB,W or INCF PORTB,F, and DECF PORTB,W or DECF PORTB,F. As previously
\par discussed, the result of either command with a W suffix is that the new
\par value is held in W, the file itself remaining unchanged. Conversely, the F
\par suffix returns the new value to the file stated. Both F and W suffixes
\par affect the Zero flag response.
\par 
\par \plain\f2\fs24\b Commands INCFSZ and DECFSZ\plain\f2\fs20 
\par 
\par There are two commands which, respectively, can replace the INCF and DECF
\par commands and which automatically test the Zero flag, taking the appropriate
\par route depending on the truth of the answer. These commands are INCFSZ and
\par DECFSZ, as defined at the start of this section.
\par 
\par Using PORTB as the example file, the command formats are INCFSZ PORTB,W or
\par INCFSZ PORTB,F and DECFSZ PORTB,W or DECFSZ PORTB,F. If the result of any
\par of these commands is zero, the Zero flag is automatically set, otherwise
\par the Zero flag is cleared. The status of the flag determines the program
\par routing in the same way as if the flag had been tested using BTFSS STATUS,Z
\par or BTFSC STATUS,Z.
\par 
\par \plain\f2\fs24\b Counting Up and Down\plain\f2\fs20 
\par 
\par Listing 7 illustrates two loops, one counting up, the other down,
\par alternating between the two after each 256 steps. INCFSZ is used in the
\par first, DECFSZ in the second. Before entering the loops, at the label BEGIN
\par the counter (COUNT) is cleared. Then at LOOP1 the command MOVF COUNT,W is
\par given, followed by MOVWF PORTB. You should now recognise what the actions
\par do: they cause the value of COUNT to be output to PORTB. Next, the command
\par INCFSZ COUNT,F is given, adding 1 to the value of COUNT, simultaneously
\par checking if it has reached zero. An answer of not-zero (Z = 0) causes
\par command GOTO LOOP1 to be performed.
\par 
\par Eventually, when COUNT has rolled over to zero, after 256 increments, GOTO
\par LOOP1 is skipped (bypassed) and LOOP2 is entered where the command MOVF
\par COUNT,W is performed, followed by MOVWF PORTB. These two lines are repeats
\par of those at the start of LOOP1. We shall see later how duplicated lines of
\par code can be avoided by using the code once in a sub-routine, calling it
\par from any other routine that we wish.
\par 
\par Next, DECFSZ COUNT,F is performed, decrementing COUNT from the entry value
\par of zero. COUNT thus rolls back to 255. Simultaneously, the command checks
\par if COUNT has reached zero. If it has not, GOTO LOOP2 is performed. When
\par COUNT has decremented to zero, command GOTO LOOP1 is performed and the
\par cycle restarts, and so on.
\par 
\par It will be spotted that the use of a separate counter is not actually
\par required in this example. We could increment or decrement the value of
\par PORTB directly, but we are using COUNT instead to illustrate the use of a
\par separate file to store data. We might, for example, want to increment
\par COUNT, and then go off and do some other processing using COUNT's value,
\par outputting that answer to PORTB instead.
\par 
\par \plain\f2\fs24\b Exercise 6\plain\f2\fs20 
\par 
\par 6.1. If you were to use INCF and DECF instead of INCFSZ and DECFSZ, what
\par would be the necessary changes to the program?
\par 
\par 6.2. What extra commands would be needed to start each loop with a non-zero
\par value, while still counting until zero occurs?
\par 
\par 6.3. What would happen if you had erroneously used W instead of F in one or
\par other of the INC/DEC statements?
\par 
\par \plain\f2\fs28\b Tutorial 7\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Switch monitoring
\par Command ANDLW
\par Command ANDWF
\par Command ADDWF
\par Command ADDLW
\par Nibbles
\par STATUS bit 1
\par Digit Carry flag
\par 
\par \plain\f2\fs24\b LISTING 8 - PROGRAM TUT8
\par \plain\f2\fs20 
\par BEGIN:\tab CLRF COUNT
\par LOOP:\tab \tab MOVF PORTA,W
\par \tab \tab ANDLW %00000001
\par \tab \tab ADDWF COUNT,F
\par \tab \tab MOVF COUNT,W
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO LOOP
\par 
\par We now turn to looking at how data is input via switches and shall continue
\par to show the results on individual LEDs. In Tutorials 21 and 22 we shall
\par look at 7-segment LEDs and intelligent LCDs as the output displays. Load
\par TUT8.OBJ and run it. Pushing switch SA0 on and off, PORTB's LEDs will be
\par seen to go on and off in a binary sequence when the switch is on (pressed),
\par but will remain in the last condition when the switch is off (released). In
\par this example, the program tests whether the status of switch SA0, which is
\par connected to PORTA RA0 (bit 0), is on or off. If it is on then the counter
\par variable, COUNT, is repeatedly added to (by 1 in this example). A value of
\par zero is added to the count if the switch is off. The count value is output
\par to PORTB.
\par 
\par First let's look at two of the commands introduced here, ANDLW and ADDWF.
\par Their counterparts ANDWF and ADDLW will also be examined.
\par 
\par \plain\f2\fs24\b Commands ANDLW and ANDWF
\par \plain\f2\fs20 
\par As no doubt most of you are aware, if one binary number is ANDed with
\par another, then only if the same bits of both numbers are set (1) will the
\par answer also have a 1 in that position. Any zeros on either or both sides
\par for any bit will automatically produce a result of 0, e.g.:
\par 
\par First number:   01110010
\par Second number:  01011001
\par ANDed answer:   01010000
\par 
\par This technique is widely used in electronics and computing, the final
\par answer determining the subsequent action to be taken by a circuit or
\par software routine. There are two ANDing commands available with the
\par PIC16x84, ANDLW (AND Literal to W), and ANDWF (AND W with File value).
\par Suppose that the first number above (01110010) is already contained within
\par W, we then wish to AND it with a fixed number as stated in a program
\par command. Assuming that the fixed number is the second number above, the
\par command is:
\par 
\par ANDLW %01011001
\par 
\par The PIC ANDs the second (literal) number with that already held in W. The
\par answer (01010000) is retained by W and is available to be further
\par manipulated or copied into any file as specified by the command which
\par follows ANDLW. You could, for example, use the command MOVWF PORTB which
\par will turn on LEDs LB6 and LB4 (01010000).
\par 
\par Any of the three numerical formats may be used with ANDLW, e.g. %00011111
\par (binary), $1F (hexadecimal), 31 (decimal), are all legitimate and equal. It
\par is also legitimate to use a name that has been equated with a value, e.g.
\par ANDLW PORTB (which would AND 6 with W since we have previously specified
\par that the name PORTB represents the value 6).
\par 
\par The command ANDWF is used to AND an existing value within W to a value
\par within a named file, either retaining the answer in W (ANDWF FILENAME,W) or
\par back in the named file (ANDWF FILENAME,F). It is not possible to directly
\par AND the contents of two files together, the value of one or other file must
\par have already been moved into W before the ANDing can take place. With both
\par commands ANDLW and ANDWF, if the answer is zero, the Zero flag of STATUS is
\par set. If the answer is greater than zero, the Zero flag is cleared. Zero is
\par the only flag affected by an AND command.
\par 
\par \plain\f2\fs24\b Commands ADDLW and ADDWF\plain\f2\fs20 
\par 
\par There are two ADDing commands available with the PIC16x84, ADDLW (ADD
\par Literal to W), and ADDWF (ADD W to a File value). Command ADDLW is used
\par where a fixed number (literal) within a program is to be added to an
\par existing value within W and which has been obtained by a previous
\par operation. Suppose that W holds the answer produced in the previous
\par ANDing example, 01010000 (decimal 80), and you wish to add a fixed value to
\par it, 53 decimal (00110101), for instance. The command would be:
\par 
\par ADDLW 53 (or ADDLW $35 - hexadecimal, or ADDLW %00110101 - binary).
\par 
\par The answer in this instance is 10000101 (decimal 133) and is retained in W
\par for further use or copying into a file, e.g. MOVWF PORTB.
\par 
\par Command ADDWF adds the contents of W to the value within a stated file. The
\par answer can be held in W (ADDWF PORTB,W) or put back into the named file
\par (ADDWF PORTB,F).
\par 
\par Three flags within STATUS are affected by any ADD command, Carry, Zero and
\par Digit Carry. If the answer to an addition is greater than 255, the Carry
\par flag is set, otherwise it is cleared. If the answer equals zero, the Zero
\par flag is set, otherwise it is cleared. The third flag, Digit Carry, we have
\par not encountered yet. Although the concept is not illustrated until later
\par (Tutorial 19), it is appropriate to describe it now.
\par 
\par If you imagine that an 8-bit binary number (e.g. 10110110) is split into
\par two halves (known as "nibbles"), 1011 and 0110, the righthand nibble is
\par monitored by the PIC as a separate digit and it is served by its own flag,
\par the Digit Carry flag. If an addition takes place which produces a result
\par greater than 15 (1111) for that nibble, the Digit Carry flag is set,
\par otherwise it is cleared.
\par 
\par \plain\f2\fs24\b Listing 8 Flow
\par \plain\f2\fs20 
\par Having described the new terms, we shall now see what happens in Listing 8.
\par Note that PORTA push-switches are biassed so that PORTA pins are normally
\par at 0V (low) but go high when pressed. In this example program, at the label
\par LOOP the contents of PORTA are copied into W (MOVF PORTA,W), which then
\par holds the status of all five usable bits of that port. We are only
\par interested, though, in the status of the switch on PORTA bit 0, switch SA0.
\par Therefore, in the next command (ANDLW %00000001) bit 0 is ANDed with 1 to
\par isolate its value, the other seven bits in W being cleared by the
\par respective zeros of the ANDed value. The answer in W is then added to the
\par contents of the counter (ADDWF COUNT,F). Next, the contents of the counter
\par are brought back into W (MOVF COUNT,W) and then copied into PORTB (MOVWF
\par PORTB), whose LEDs are turned on or off depending on the binary count
\par value.
\par 
\par With the command GOTO LOOP, the sequence is repeated. It will be seen that
\par there is only an increase in the count value if PORTA bit 0 holds a 1,
\par therefore the count will only change if the switch is on (pressed).
\par Pressing any other PORTA switch has no effect. When the counter passes 255,
\par its value rolls over to zero and starts counting upwards again.
\par 
\par \plain\f2\fs24\b Exercise 7\plain\f2\fs20 
\par 
\par 7.1. Can you see another way of writing the first two lines using MOVLW and
\par ANDWF?
\par 
\par 7.2. Can you see how the BTFSS or BTFSC commands might be used to achieve
\par the same output result; the use of MOVLW 1 or ADDLW 1 could be useful here.
\par 
\par 7.3. There is also the opportunity to use INCF in this type of situation.
\par Try rewriting to include this command.
\par 
\par \plain\f2\fs28\b Tutorial 8\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Increasing processing speed of TUT8
\par Bit testing for switch status
\par 
\par \plain\f2\fs24\b LISTING 9 - PROGRAM TUT9
\par \plain\f2\fs20 
\par LOOP: BTFSS PORTA,0
\par \tab GOTO LOOP
\par \tab INCF COUNT,F
\par \tab MOVF COUNT,W
\par \tab MOVWF PORTB
\par \tab GOTO LOOP
\par 
\par In TUT8 we saw that the count adding commands etc. were performed even if
\par the count value was zero. This is a waste of processing speed, why bother
\par to add zero to a count? The program in Listing 9 shows a faster
\par alternative. Run TUT9.OBJ.
\par 
\par By using the command BTFSS to check the status of a switch (in this case
\par still SA0 on PORTA bit 0), if the switch is not pressed we can avoid the
\par count incrementing procedure, jumping immediately to a further switch
\par status test. Alternatively, in another program, by substituting another
\par destination instead of LOOP, we could jump to a totally different routine
\par and perform some other procedure. Another choice is to use the command
\par RETURN instead of GOTO LOOP to return to another routine which had called
\par this one. Commands CALL and RETURN will be covered in Tutorial 13. It is
\par expected that you will recognise from Listing 9 what the program does and
\par how it does it. If you don't, re-read Tutorial 4 and the section on BTFSS.
\par 
\par \plain\f2\fs24\b Exercise 8\plain\f2\fs20 
\par 
\par 8.1. What happens if you use BTFSC instead of BTFSS?
\par 
\par 8.2. Could one of the Zero flag testing commands be used instead of BTFSS?
\par If so, how, and would an AND command be useful? (Remember that PORTA has
\par more bits than just bit 0).
\par 
\par \plain\f2\fs28\b Tutorial 9\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPT EXAMINED\plain\f2\fs20 
\par 
\par Responding to a switch press only at the moment of pressing
\par 
\par \plain\f2\fs24\b LISTING 10 - PROGRAM TUT10
\par \plain\f2\fs20 
\par BEGIN:\tab CLRF COUNT
\par \tab \tab CLRF SWITCH
\par TESTIT:\tab BTFSC PORTA,0
\par \tab \tab GOTO TSTPRV
\par \tab \tab BCF SWITCH,0
\par \tab \tab GOTO TESTIT
\par TSTPRV:\tab BTFSC SWITCH,0
\par \tab \tab GOTO TESTIT
\par \tab \tab INCF COUNT,F
\par \tab \tab MOVF COUNT,W
\par \tab \tab MOVWF PORTB
\par \tab \tab BSF SWITCH,0
\par \tab \tab GOTO TESTIT
\par 
\par In the switch press examples of Listings 8 and 9, we saw that the counter
\par was incremented for the entire duration of the switch being on. Often, only
\par a single response to a change of switch status might be required. This
\par entails testing the switch status and comparing it with a previous test.
\par Only if the switch is on and if that on condition has not yet been
\par responded to will the next action be performed.
\par 
\par Load TUT10.OBJ and run it. We are still monitoring PORTA bit 0 for the
\par switch press (SA0), responding to it via the LEDs on PORTB. Observe the
\par LEDs while pressing SA0 on and off. For each pressing, only one change of
\par the LED count will occur. Study Listing 10: the entry to the routine is at
\par BEGIN where two variables, COUNT and SWITCH are cleared. At the label
\par TESTIT, the command is BTFSC PORTA,0, testing the status of PORTA bit 0 (is
\par it clear?). Remember that we are only interested in the bit being set. If
\par it is false that bit 0 is clear (i.e. that it is set \\- the switch is
\par pressed) the command GOTO TSTPRV is performed and then the status of SWITCH
\par bit 0 is tested, BTFSC SWITCH,0. This bit serves as the flag to keep track
\par of the previous status of the switch. At this moment, the bit will be clear
\par because the whole byte was cleared on entry to the routine. Consequently,
\par the GOTO TESTIT command is skipped, the count is incremented and its value
\par output to PORTB.
\par 
\par Now SWITCH bit 0 is set (BSF SWITCH,0) to indicate that the count has been
\par incremented for this switch press (i.e. the flag is set), and the program
\par jumps back to TESTIT. If the switch is still pressed, then at TSTPRV the
\par BTFSC SWITCH,0 command will produce a false answer and the command GOTO
\par TESTIT will be performed, thus preventing the counter from being further
\par incremented at this time. What is now needed is for the switch to be
\par released so that the two commands BCF SWITCH,0 (clear the flag) and GOTO
\par TESTIT can occur. The stage is then once again set for the next switch
\par press to be responded to by the counter.
\par 
\par \plain\f2\fs24\b Exercise 9\plain\f2\fs20 
\par 
\par 9.1. In Listing 10, AND and MOV commands could have been used instead of
\par BTFSC and BCF. How, and with what other command?
\par 
\par 9.2. Would using BTFSS instead of BTFSC involve more commands and labels
\par having to be used as well?
\par 
\par \plain\f2\fs28\b Tutorial 10\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Performing different functions dependent upon which of two switches is pressed
\par The use of a common sub-routine serving two other routines
\par 
\par \plain\f2\fs24\b LISTING 11 - PROGRAM TUT11
\par \plain\f2\fs20 
\par BEGIN:\tab CLRF COUNT
\par \tab \tab CLRF SWITCH
\par TEST1:\tab BTFSC PORTA,0
\par \tab \tab GOTO TSTPR1
\par \tab \tab BCF SWITCH,0
\par \tab \tab GOTO TEST2
\par TSTPR1:\tab BTFSC SWITCH,0
\par \tab \tab GOTO TEST2
\par \tab \tab BSF SWITCH,0
\par \tab \tab INCF COUNT,F
\par \tab \tab GOTO OUTPUT
\par TEST2:\tab BTFSC PORTA,2
\par \tab \tab GOTO TSTPR2
\par \tab \tab BCF SWITCH,2
\par \tab \tab GOTO TEST1
\par TSTPR2:\tab BTFSC SWITCH,2
\par \tab \tab GOTO TEST1
\par \tab \tab BSF SWITCH,2
\par \tab \tab DECF COUNT,F
\par OUTPUT:\tab MOVF COUNT,W
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO TEST1
\par 
\par (With the PIC Tutorial board, the response of the programs would by now
\par seem to be getting a bit slow, so the RC clock rate was increased.)
\par 
\par Load TUT11.OBJ, run it and experiment with the switches on PORTA bits 0 and
\par 2 (SA0 and SA2). You will discover that switch SA0 causes the count
\par displayed on the LEDs to be increased, and that switch SA2 decreases the
\par count. The basic logic flow is the same as that in Listing 10, except that
\par two switches are used and each switch is responsible for a different
\par routine. Note that whilst each switch could have had its own routine to
\par output to PORTB, the two routines would be the same. Consequently, each
\par switch routine is routed into a common output sub-routine (OUTPUT). At the
\par end of SA0's routine, the command GOTO OUTPUT needs to be given, but at the
\par end of SA2's routine, no GOTO OUTPUT command is needed because OUTPUT
\par follows immediately after it.
\par 
\par \plain\f2\fs24\b Exercise 10\plain\f2\fs20 
\par 
\par 10.1. How do you think a single test for neither of the switches being
\par pressed could be introduced, shortening the testing time? Could an AND be
\par used with a STATUS check, or can a STATUS check be used on its own without
\par an AND? (Think carefully about the latter.)
\par 
\par 10.2. How would you increase the count by more than one, say two, at each
\par press of switch SA0? With the knowledge you've gained so far, three ways
\par should come to mind, one of them including the use of a new named variable.
\par 
\par 10.3. If you want to add 255 each time a switch SA0 press occurs, do you
\par need an ADD command, or is there another command which will do the same
\par job?
\par 
\par \plain\f2\fs28\b Tutorial 11\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par The ease of reflecting PORTA's switches on PORTB's LEDs!
\par Command COMF
\par Command SWAPF
\par Inverting a byte's bit logic
\par Swapping a byte's nibbles
\par 
\par \plain\f2\fs24\b LISTING 12 - PROGRAM TUT12
\par \plain\f2\fs20 
\par LOOP:\tab MOVF PORTA,W
\par \tab ANDLW %00011111
\par \tab MOVWF PORTB
\par \tab GOTO LOOP
\par 
\par Load and run TUT12.OBJ; experiment with pressing any combination of the
\par switches on PORTA (SA0 to SA4) while observing the LEDs on PORTB. This
\par routine should need no further comment. Another way of expressing the first
\par two commands is:
\par 
\par LOOP: MOVLW %00011111
\par       ANDWF PORTA,W
\par 
\par Now load TUT13.OBJ and run it, again experimenting with pressing any
\par combination of the switches on PORTA (SA0 to SA4) and observing the LEDs on
\par PORTB.
\par 
\par \plain\f2\fs24\b LISTING 13 - PROGRAM TUT13
\par \plain\f2\fs20 
\par LOOP:\tab SWAPF PORTA,W
\par \tab ANDLW %11110001
\par \tab MOVWF PORTB
\par \tab GOTO LOOP
\par 
\par You will see while you press PORTA's switches, that its four righthand
\par switches (SA0 to SA3) are having their status displayed on PORTB's four
\par lefthand LEDs. (LB7 to LB4). The fifth switch (SA4) will be affecting the
\par first LED on the right (LB0). What is happening is that the software has
\par been told to swap and move into W (SWAPF PORTA,W) the left and righthand
\par four bits of PORTA (its nibbles, as introduced in Tutorial 7). The answer
\par is then ANDed with bits that reflect the swapped status in order to remove
\par any possibility of influence by the original three unused bits of PORTA.
\par 
\par The SWAPF command is especially useful if the values of the two nibbles are
\par required separately as values of up to 15 (00001111). A good example of its
\par use will be seen in Tutorial 21. It is illustrated now because of its
\par programming similarity to TUT12 and TUT14. The F suffix can be used with
\par SWAPF instead of W, as with other files discussed. There is no command
\par which allows nibbles to be swapped once the byte is in W. If a byte within
\par W needs swapping, it must be put out to a file, and then the SWAPF FILE,W
\par command given to bring it back into W.
\par 
\par Let's look now at another command which uses a similar demonstration
\par routine to TUT12 and TUT13. Load and run TUT14.OBJ. Once more, experiment
\par with pressing any combination of switches SA0 to SA4 while watching PORTB's
\par LEDs.
\par 
\par \plain\f2\fs24\b LISTING 14 - PROGRAM TUT14
\par \plain\f2\fs20 
\par LOOP:\tab COMF PORTA,W
\par \tab ANDLW %00011111
\par \tab MOVWF PORTB
\par \tab GOTO LOOP
\par 
\par You will now discover that instead of LEDs being turned on when a switch is
\par pressed, they are turned off, and vice versa. This is due to the command
\par COMF, which automatically inverts each bit of a byte, 1s becoming 0s, 0s
\par becoming 1s, i.e. it performs a task known as 2's complement, hence COMF,
\par which means COMplement File. There are several uses for this command, one
\par of which is the situation when all the switches are biased to the 0V line
\par instead of the positive. In that instance, and using the switch testing
\par techniques shown earlier, pressing the switches would produce the wrong bit
\par levels for the commands shown: switches would need to be held pressed for
\par off, releasing them for on. Not an easy thing to do with push-switches!
\par 
\par Switch down PORTA switches SA20 and SA21 so that PORTA pins RA0 to RA4 are
\par biassed to +VE, going low when switches SA0 to SA4 are pressed (as in
\par Fig.14 and as you did in Exercise 7.4). Run the program again. You will
\par find that the LEDs respond as they did for TUT12. Now run TUT12 again and
\par confirm that the LED results are the inverse of that previously seen with
\par it.
\par 
\par Another use for COMF is in subtraction. This is a concept for experienced
\par programmers and will not be illustrated here. However, in a nutshell, the
\par use of COMF allows addition to be used instead of subtraction while still
\par achieving the desired objective. This technique can be easier in some
\par instances than using the available subtraction commands.
\par 
\par The F suffix can be used with COMF instead of W, as with other files
\par discussed. There is no command which allows the inversion of a byte once it
\par is in W. If a byte within W needs inversion, it must be put out to a file,
\par and then the COMF (FILENAME),W command given to bring it back into W.
\par 
\par \plain\f2\fs24\b Exercise 11
\par \plain\f2\fs20 
\par With these exercises, switches SA20 and SA21 should be restored to their
\par previous settings, i.e. up, as in Fig.13.
\par 
\par 11.1 If SWAPF was not available as a command, how would you write a routine
\par which produced the same result (would RLF or RRF be suitable commands)?
\par 
\par 11.2 Rewrite TUT13 and TUT14, putting the contents of W out to a file of
\par any name (which you must equate at the beginning of the program),
\par performing another COMF or SWAPF action, and then bringing it back into W
\par for output to PORTB. Can PORTB be used as the temporary file store in these
\par rewrites?
\par 
\par 11.3. Write a routine that allows the nibbles of a byte to be put into
\par separate files and each having a value no greater than $0F (decimal 15);
\par there are several ways of doing it.
\par 
\par \plain\f2\fs28\b Tutorial 12\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Generating an output frequency in response to a switch press
\par The use of two port bits set to different input/output modes
\par Command NOP
\par 
\par \plain\f2\fs24\b LISTING 15 - PROGRAM TUT15
\par \plain\f2\fs20 
\par SOUND:\tab MOVLW 80
\par \tab \tab MOVWF NOTE
\par \tab \tab MOVWF FREQ
\par GETKEY:\tab BTFSC PORTA,0
\par \tab \tab GOTO GETKEY
\par \tab \tab DECFSZ NOTE,F
\par \tab \tab GOTO GETKEY
\par \tab \tab MOVF FREQ,W
\par \tab \tab MOVWF NOTE
\par \tab \tab MOVLW 16
\par \tab \tab ADDWF PORTA,F
\par \tab \tab GOTO GETKEY
\par 
\par So far we have been outputting data to LEDs, and at a comparatively slow
\par rate. We have also been using one port as a switch input and the other port
\par as the output. Here we examine how the same port can be used simultaneously
\par for input and output via different bits. In doing so, we use sound as the
\par medium by which we indicate the status of a switch, generating an audible
\par frequency when it is pressed.
\par 
\par Change the switch settings as shown in Fig.15. Also, via jack socket SK1,
\par connect a pair of high impedance headphones or plug the circuit into the
\par input of an amplifier system. Do not connect a loudspeaker directly to this
\par circuit; there is insufficient power to drive it. Note that switches SA20
\par and SA21 are set down in order to bias pin RA4 (open-collector) so that an
\par output can be generated on it. Load TUT15.OBJ and run it, pressing switch
\par SA0 on and off - a frequency tone will be heard.
\par 
\par In the initialising statements at the head of the full TUT15.ASM program,
\par PORTA has been set with bits 0 to 3 as inputs and bit 4 as an output (MOVLW
\par %00001111, MOVWF TRISA). You should now recognise all the commands
\par given in the heart of the program shown. Only a general commentary on what
\par happens is now given.
\par 
\par On entry into the routine headed SOUND, a value of 80 is loaded into the
\par files named NOTE and FREQ. The value is arbitrary as far as this
\par demonstration is concerned. You may choose any other from 1 up to 255; the
\par lower the value, the higher the frequency generated. PORTA's status is
\par monitored at GETKEY and the clearance (logic 0) of PORTA bit 0 by switch
\par SA0 is being looked for. Switch testing is repeated until SA0 is pressed.
\par When that occurs, file NOTE is decremented and its zero status tested. If
\par it is not yet zero, the routine jumps back to the switch test. When the
\par switch has been pressed for long enough (mere thousandths of a second),
\par NOTE will eventually reach zero, at which point the command MOVF FREQ,W is
\par reached, followed by the fixed value of FREQ being reset into NOTE. Next,
\par the value in PORTA has 16 added to it to increment the count at bit 4
\par (alternating between 0 and 1), and then there is a jump back to further
\par switch testing.
\par 
\par For as long as switch SA0 is pressed, PORTA bit 4 will be periodically
\par incremented. The speed at which the routine runs causes this bit to change
\par at the audio frequency rate to which you are listening. If you adjust the
\par rate setting preset (VR1) on the PCB you will hear the change in the
\par resulting frequency. In a real-life situation, of course, the operating
\par frequency of the system would normally be fixed. The only frequency
\par correction choice then is to change the value of FREQ.
\par 
\par There is, though, another factor that will affect the resulting audio
\par frequency: the number of commands within the controlling loops. To
\par illustrate the point, let's change the number of commands involved. You may
\par think that to add more commands would be difficult, what would they do
\par which would not interfere in the completion of the loop? Well, there are
\par several options, such as repeating some of the existing commands, MOVF
\par FREQ,W for example, or MOVWF NOTE. None of these commands would actually
\par change anything, except for the rate of operation. However, a tailor-made
\par command is already available in the PIC's vocabulary which is intended for
\par use where delay tactics are needed, command NOP.
\par 
\par \plain\f2\fs24\b Command NOP
\par \plain\f2\fs20 
\par Command NOP simply stands for No OPeration. Responding to this command
\par takes the PIC just as long as responding to any other but its response is
\par to just do nothing! This command, then, can be used here to slow down the
\par resulting note frequency. Insert it immediately before DECFSZ NOTE,F. When
\par running the amended program you will notice that a change in the output
\par frequency has occurred.
\par 
\par \plain\f2\fs24\b Exercise 12\plain\f2\fs20 
\par 
\par 12.1. Experiment with different values for FREQ. What happens if you set
\par FREQ to zero - does it stop a note being generated? Explain the result.
\par 
\par 12.2. Experiment with more than one NOP command in the loop.
\par 
\par 12.3. At which other places can you alternatively insert NOP, and is the
\par frequency change still noticeable?
\par 
\par 12.4. Are there any places where you cannot use NOP?
\par 
\par \plain\f2\fs28\b Tutorial 13\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Command CALL
\par Command RETURN
\par Command RETLW
\par 
\par \plain\f2\fs24\b LISTING 16 - PROGRAM TUT16
\par \plain\f2\fs20 
\par LOOP:\tab \tab CALL PROG1
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO LOOP
\par PROG1:\tab MOVF PORTA,W
\par \tab \tab RETURN
\par 
\par Before looking further into sound generation, there are several commands
\par that we should examine. Three of those are associated with calling
\par sub-routines: CALL, RETURN and RETLW. Remove your audio connection for the
\par moment, and set the switches as in Fig.13. Load TUT16.OBJ, run it and
\par experiment with pressing different combinations of switches SA4 to SA0
\par while observing PORTB's LEDs.
\par 
\par \plain\f2\fs24\b Commands CALL, RETURN and RETLW
\par \plain\f2\fs20 
\par Programs can be written as a series of sub-routines which can be reached in
\par one of three ways, directly by default, via a GOTO command, or by a CALL
\par command. (Routing following automatic detection of an interrupt event is
\par another matter and is discussed later.) We have seen several examples of
\par the first two. Program TUT4 (Tutorial 4) uses them both: the sub-routine
\par LOOP1 is entered directly following the initialisation routine. LOOP2 is
\par also entered directly from the end of LOOP1. Both LOOP1 and LOOP2 are then
\par further accessed by GOTO commands. However, a CALL command can be used if
\par one routine needs to make use of another and then once that has been
\par completed, for the program to jump back to where it was before the other
\par routine was Called. The use of sub-routines allows the same routine to be
\par accessed from many other areas within the overall program, so saving on
\par program space.
\par 
\par A second command always has to be used before the program returns to the
\par calling origin. That command takes one of two forms, RETURN (which is an
\par obvious command - RETURN to where you came from) or RETLW (RETurn to where
\par you came from with a Literal value held in W). There is a third return
\par command, RETFIE, which we shall meet later in connection with interrupts.
\par 
\par A GOTO command can never be used to end a sub-routine call - the PIC will
\par continue to expect a return command and, if repeated calls to a sub-routine
\par are made without a RETURN or RETLW command, it will become confused and
\par unpredictable results could occur. For example, the following is "illegal":
\par 
\par PROG1: CALL PROG2
\par .      GOTO PROG1
\par PROG2: GOTO PROG1
\par 
\par This is `legal', though:
\par 
\par PROG1: CALL PROG2
\par .      GOTO PROG1
\par PROG2: RETURN
\par 
\par When the program returns from a call following a RETURN command, the
\par contents of W are those which were put there by the last command which used
\par W. Consequently, you can perform a complex sub-routine, end up with an
\par answer in W and, using the RETURN command, return to the main program with
\par that result still retained in W.
\par 
\par Command RETLW, though, returns to the main program with W holding the value
\par which RETLW has acquired as part of that command. A literal value is always
\par specified as part of the RETLW command, e.g. RETLW 127 or RETLW 0. That
\par value replaces any other value within W and is the one which is held in W
\par on the return to the calling point. The value may be expressed in decimal,
\par hexadecimal, binary or as a "named" value equated during program
\par initialisation.
\par 
\par To explain Listing 16 then, at LOOP the sub-routine at PROG1 is called from
\par where the value held in PORTA is moved into W. A return is made to the loop
\par where the next command to be performed is MOVWF PORTB, after which the GOTO
\par LOOP command again takes us back to CALL PROG1 again.
\par 
\par \plain\f2\fs24\b Exercise 13\plain\f2\fs20 
\par 
\par 13.1. Rearrange TUT16 so that reading PORTA is in the main loop and
\par outputting data to PORTB is in the called routine.
\par 
\par 13.2. Try adding other commands in the subroutine, such as AND or ADD.
\par 
\par 13.3. Use RETLW as the final statement in the subroutine, using any literal
\par value of your choice, verifying its operation!
\par 
\par \plain\f2\fs28\b Tutorial 14\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Tables
\par Register PCL
\par Register PCLATH
\par 
\par \plain\f2\fs24\b LISTING 17 - PROGRAM TUT17
\par \plain\f2\fs20 
\par \tab \tab PAGE0
\par \tab \tab GOTO LOOP
\par 
\par TABLE:\tab ANDLW 15\tab \tab ; AND W with 15
\par \tab \tab ADDWF PCL,F\tab \tab ; ADD to PCL
\par \tab \tab RETLW 255\tab \tab ; 0  11111111
\par        \tab RETLW 1\tab       ; 1  00000001
\par \tab \tab RETLW '5'\tab \tab ; 2  00110101
\par \tab       RETLW 0\tab       ; 3  00000000
\par \tab \tab RETLW 31\tab \tab ; 4  00011111
\par \tab \tab RETLW 193\tab \tab ; 5  11000001
\par \tab \tab GOTO OTHER\tab \tab ; 6  00011111
\par \tab       RETURN            ; 7  00000111
\par \tab \tab RETLW %10101010\tab ; 8  10101010
\par \tab \tab RETLW $C7\tab \tab ; 9  11000111
\par \tab       RETLW 'A'\tab       ; 10 01000001
\par \tab       RETLW 65\tab       ; 11 01000001
\par \tab       RETLW 'B'         ; 12 01000010
\par \tab       RETLW 'x'         ; 13 01111000
\par \tab       GOTO OTHER1       ; 14 10001110
\par \tab                         ; or 10011110
\par \tab       MOVF STORE,W      ; 15 00000000
\par \tab \tab RETURN
\par 
\par LOOP:\tab \tab MOVF PORTA,W
\par \tab \tab CALL TABLE
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO LOOP
\par OTHER:\tab RETLW STORE
\par 
\par OTHER1:\tab MOVLW 128
\par \tab \tab ADDWF PORTA,W
\par \tab \tab RETURN
\par 
\par The use of look-up tables, whose tabulated commands or values are
\par determined by a value set elsewhere in a program, is of enormous benefit.
\par Tables depend on the use of the Program Counter (PCL - discussed in
\par Tutorial 4) and the commands CALL, RETLW, RETURN and GOTO. They can be used
\par with other calls within them, but this usually requires making additional
\par commands prior to accessing the table. When a table is accessed, the value
\par already held in W is added to PCL and causes the program to jump forward by
\par the same number of program commands as are in W. The command at the jump
\par address is then performed.
\par 
\par Load TUT17.OBJ, run it and experiment by pushing switches SA0 to SA4 in any
\par combination while observing the LEDs. on PORTB. The LEDs should come on
\par according to the binary value shown in the comments column of Listing 17,
\par i.e. all LEDs will be on if no switch is pressed.
\par 
\par In TUT17, the instruction PAGE0, although individually stated in the
\par extract shown here, follows the initialisation in the normal way. After
\par initialisation, and before any tables are encountered, the command GOTO
\par LOOP bypasses the table commands. Failure to bypass them would cause
\par confusion to the PIC. At the first command of LOOP, switch data from PORTA
\par is brought into W. The CALL TABLE command then routes the program to the
\par first command within the table, ANDLW 15.
\par 
\par The AND command is essential here to limit the possible value which can be
\par added to the Program Counter (PCL). Because all five switches SA0 to SA4
\par are in use, we know that the binary value at PORTA could be greater than 15
\par (all five switches on = 11111 binary = 31 decimal) and we also know that
\par the number of "routing" commands within the table is 16 (0 to 15). If the
\par table were to be given a value greater than 15, the additive PCL address
\par jump would cause the program to jump beyond the boundary permitted, with
\par unpredictable results. The ANDing could, alternatively, have been done
\par immediately prior to CALL TABLE.
\par 
\par \plain\f2\fs24\b Omitting the AND Command\plain\f2\fs20 
\par 
\par There are circumstances when the AND statement is not needed. For example,
\par if it is known that the value present in W on the call can never be greater
\par than five, AND would not be needed and the table could be limited to six
\par jump options only (remember that 0 counts as a jump value). However, if in
\par doubt about the maximum value that could be in W, always use a value
\par limiter of some sort (techniques other than AND can be used). This limiting
\par is especially necessary when a program is being developed since errors in
\par other regions of the program could result in an excessive W value,
\par resulting in a system "crash". When consequential crashes of this type
\par occur, it can be difficult sometimes to establish the primary cause of the
\par problem which is elsewhere.
\par 
\par At the command ADDWF PCL,F the ANDed value remaining in W is added to the
\par Program Counter and the command within the table which corresponds to the
\par new address is performed. For clarity, W's entry value is shown alongside
\par each of the 16 table jumps. If the W value is 0, then the command performed
\par within the table is the first one (0), RETLW 255. As instructed, the
\par program now returns to the calling point with 255 in W. If the value added
\par to PCL is 5, the command performed is RETLW 193. In all instances of the
\par RETLW command within the table, the stated literal value is copied into W
\par and the return is made. You will see that, as with other xxxLW commands,
\par the literal value can be expressed in decimal, hexadecimal, binary or
\par equated name values.
\par 
\par What you have not encountered yet is the use of characters in single
\par quotes. Any standard ASCII character from the full 0 to 255 set can be
\par entered in this way, numbers, upper or lower case letters, symbols, etc.
\par (This may not be true with Assemblers other than those referred in this
\par text.) During assembly by TASM, any character within the quotes is
\par translated into its ASCII value and it is that value which is returned in
\par W. (In reality, a lot of the ASCII codes will not be available on your
\par keyboard.) Note that only the "apostrophe" type of quote is permitted ('),
\par that normally residing on your keyboard between the semicolon (;) and the
\par hash symbol (#). The double-quote symbol (") is not permitted, nor is the
\par "lefthand" single quote (`) found on many keyboards (to the left of numeral
\par 1 and the exclamation mark). Four examples of "quoted" characters are shown
\par in the table. Quoted '5' will be translated as ASCII 53 (not as the value
\par 5); 'A' and 'B' will become ASCII 65 and 66 respectively; lower case 'x'
\par will be returned as ASCII 120. You will find this conversion technique
\par invaluable when compiling tables of messages for output to an intelligent
\par LCD (Tutorial 22).
\par 
\par The simple command RETURN at jump 7 will cause the current value already
\par within W to be returned; i.e. the value on the switches after it has been
\par ANDed with 15. It may not be immediately clear what this action would
\par achieve, but an example is given in Tutorial 15.
\par 
\par \plain\f2\fs24\b Tabled GOTO\plain\f2\fs20 
\par 
\par There are two examples of a tabled GOTO command in Listing 17, at jumps 6
\par and 14. These cause the program to jump to the sub-routines named, OTHER
\par and OTHER1. At OTHER, the command MOVLW STORE is executed, after which the
\par program returns to the calling program (not back into the table) with the
\par equated address value of STORE. The routine at OTHER1 shows how a table
\par jump can go to a routine in which more than one action can be performed, in
\par this case adding 128 to the value at PORTA, then returning as usual. Any
\par action can be performed here, on any file, for any purpose, and there is no
\par limit to the number of commands performed before the final RETURN (within
\par the program space available, of course).
\par 
\par The command at table jump 15 is interesting. It looks as though a command
\par other than GOTO, RETURN or RETLW is being performed. However, this jump is
\par the last in the table and so it is perfectly legitimate to perform any
\par other action(s) here since the program will automatically follow them
\par through without interfering with the normal table action. Here the simple
\par action of getting the value held in STORE is performed, immediately
\par followed by a RETURN.
\par 
\par What would cause table difficulties is if the command at a mid-table jump
\par did not allow an immediate exit from the table. For example, consider the
\par following mid-table jump commands:
\par 
\par RETLW 0        ; 3
\par MOVF STORE,W   ; 4
\par RETLW 193      ; 5
\par 
\par Jump 3 would be OK, so would jump 5. Jump 4, though, would perform MOVF
\par STORE,W (bringing the value within STORE into W), but the exit route for
\par that command is via the address of jump 5, which is RETLW 193, immediately
\par replacing the value acquired in jump 4 with the value 193. Not very
\par helpful! Mind you, the commands GOTO or RETURN could be at jump 5, which
\par would be fine for jump 4, but what of the result of actually jumping to
\par jump 5, would you necessarily want to just RETURN or GOTO? One could,
\par perhaps, envisage a table consisting only of INCF STORE,F commands, for
\par example, in which the number of increments generated would be the
\par equivalent of the entry point value of W. But the use of a loop or an
\par addition would, though, probably be more appropriate to that requirement.
\par 
\par It is legitimate to GOTO a table, or arrive at it from the end of another
\par routine, but in this case it may be necessary to only exit the table by
\par GOTO commands. Unless you are already in the middle of a call, "return"
\par commands will cause a program crash.
\par 
\par \plain\f2\fs24\b Table Span\plain\f2\fs20 
\par 
\par There is a significant restriction on tables which must not be overlooked.
\par Because of the way in which the Program Counter handles the calls to and
\par from tables, all of the tabulated data must be contained within the first
\par 256 bytes of the program (0 to 255). Not a single jump address must fall
\par outside this block (except as discussed in a moment).
\par 
\par When writing software, it can sometimes be difficult, depending on program
\par structure, to ascertain from the code editing program (word-processing
\par software) whether or not the tables overlap beyond the block. If this is
\par the case, come out of the WP package and assemble the code. Don't send it
\par to the PIC, but come back into the WP and examine the .LST file that has
\par been generated for the program as it now stands. Look at the address
\par numbers (in the second column as you saw earlier in Listing 3A) and see if
\par any part of the table(s) occurs beyond the $00FF hex address (decimal 255).
\par Any overlap beyond (even $0100 - 256 decimal) is unacceptable.
\par 
\par \plain\f2\fs24\b PCLATH\plain\f2\fs20 
\par 
\par Advanced programmers do have a way round the table block limit should they
\par need to find one. It is through the use of the PCLATH register which allows
\par the 256 byte block restriction to be moved into another area of the
\par program. This command will, of course, be useful if the total number of
\par tabulated items is greater than 256. Being an advanced programmer's
\par command, we shall not illustrate PCLATH here. Interested readers are
\par referred to application note AN556 in the Microchip Embedded Control
\par Handbook.
\par 
\par With both the "normal" and PCLATH modified table areas there is no limit on
\par the number of tables within them, and the calling routines can be anywhere
\par within the program, start, middle or end. It is perfectly legitimate to
\par have sub-routines placed between different tables, but remember that their
\par length also consumes part of the 256 byte block.
\par 
\par \plain\f2\fs24\b Exercise 14\plain\f2\fs20 
\par 
\par 14.1. Write a routine that calls a table which multiplies a binary number
\par by seven. Use the switches as the source of that number and restrict it to
\par between 0 and 7, showing the results on PORTB's LEDs.
\par 
\par 14.2. Using the first four switches of PORTA, create a table to convert the
\par binary numbers generated by them to a BCD (binary code decimal) format;
\par tens of units in the left four LEDs, units in right four LEDs. (If you are
\par not familiar with BCD, think about what it might mean and how it might be
\par shown on LEDs. The use of BCD formats is discussed in Tutorial 19.)
\par 
\par \plain\f2\fs28\b Tutorial 15\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Using four switches to create four different notes
\par Use of a table to selectively route program flow
\par 
\par \plain\f2\fs24\b LISTING 18 - PROGRAM TUT18
\par \plain\f2\fs20 
\par TABLE:\tab ANDLW 15
\par \tab \tab ADDWF PCL,F
\par \tab       RETURN      ; 0
\par \tab \tab GOTO PLAY1\tab ; 1
\par \tab \tab GOTO PLAY2\tab ; 2
\par \tab       RETURN      ; 3
\par \tab \tab GOTO PLAY3\tab ; 4
\par \tab       RETURN      ; 5
\par \tab       RETURN      ; 6
\par \tab       RETURN      ; 7
\par \tab \tab GOTO PLAY4\tab ; 8
\par \tab       RETURN      ; 9
\par \tab       RETURN      ; 10
\par \tab       RETURN      ; 11
\par \tab       RETURN      ; 12
\par \tab       RETURN      ; 13
\par \tab       RETURN      ; 14
\par \tab       RETURN      ; 15
\par 
\par PRESET:\tab MOVLW 80
\par \tab \tab MOVWF NOTE1
\par \tab \tab MOVWF FREQ1
\par \tab \tab MOVLW 110
\par \tab \tab MOVWF NOTE2
\par \tab \tab MOVWF FREQ2
\par \tab \tab MOVLW 140
\par \tab \tab MOVWF NOTE3
\par \tab \tab MOVWF FREQ3
\par \tab \tab MOVLW 160
\par \tab \tab MOVWF NOTE4
\par \tab \tab MOVWF FREQ4
\par \tab 
\par GETKEY:\tab COMF PORTA,W
\par \tab \tab CALL TABLE
\par \tab \tab GOTO GETKEY
\par 
\par PLAY1:\tab DECFSZ NOTE1,F
\par \tab \tab RETURN
\par \tab \tab MOVF FREQ1,W
\par \tab \tab MOVWF NOTE1
\par \tab \tab GOTO OUTPUT
\par 
\par (PLAY2 to PLAY4 are similar to PLAY1)
\par 
\par OUTPUT:\tab MOVLW 16
\par \tab \tab ADDWF PORTA,F
\par \tab \tab RETURN
\par 
\par The program in Listing 18 allows any one of four notes to played by the
\par switches on PORTA RA0 to RA3 (SA0 to SA3). As with Tutorial 12, the audio
\par output is on RA4. Reconnect your audio monitor, load TUT18.OBJ, run it and
\par press some switches. You will immediately notice that the "note"
\par frequencies belong to no musical scale known to man. There is nothing we
\par propose to do about that, we are interested in more mundane matters! The
\par object of this program is to show the use of a table and several
\par sub-routines which allow four notes to be played (singly) depending on the
\par switch presses. Multiple pressing of switches is ignored.
\par 
\par To conserve page space only one note routine is shown. The others are
\par identical except that they process different notes and PLAY4 omits the GOTO
\par OUTPUT command since OUTPUT immediately follows its final command. You will
\par see the now-familiar commands in the GETKEY and PLAY1 routines. The table
\par should seem recognisable as well. As in Listing 17, when the program first
\par starts, the table is bypassed and the first main command is at PRESET. Here
\par the frequency values for the four notes are set up as NOTE and FREQ
\par variables.
\par 
\par Switches are monitored as before and calls made to the table. There,
\par routing to different notes occurs only if individual switches are pressed
\par (jumps 1, 2, 4, 8). Any other switch setting, including none, results in a
\par return to the calling point. When the selected note routine has been
\par processed, a jump to OUTPUT occurs from where the output pin RA4 is
\par toggled, causing a note to be heard. A RETURN command follows, returning
\par the program to the calling point.
\par 
\par Even from this cut-down version of the program, it is apparent that a lot
\par of commands are involved and that many of them are similar (PRESET) or even
\par identical (PLAY by four). You will also see that only five calls to the
\par table achieve useful results. The others are wasted but have to be included
\par because four switches can generate 16 permutations of settings. You can't
\par just say to the musician "never press more than one key at once", you have
\par to allow for human fallibility. If an error can be made by the program
\par user, it will at some time BE made - Murphy's Law. Programmers must always
\par think about what MIGHT happen and write the software accordingly (making it
\par "user-friendly" is another way of putting it!).
\par 
\par The programmer must usually also think about program speed and program
\par compactness. Sometimes they can both achieve the same result, but not
\par always. However, for the sake of discussing program options available, in a
\par moment we'll look at how TUT18.ASM could be written in another way. First
\par an exercise for you:
\par 
\par \plain\f2\fs24\b Exercise 15
\par \plain\f2\fs20 
\par 15.1. Change the frequency values in TUT18.ASM to produce notes that are
\par somewhat more harmonically related! What problems do you come up against?
\par 
\par \plain\f2\fs28\b Tutorial 16\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Indirect addressing
\par Using unnamed file locations
\par Port B internal pull-ups
\par Command FSR
\par Command INDF
\par Register OPTION
\par 
\par \plain\f2\fs24\b LISTING 19 - PROGRAM TUT19
\par \plain\f2\fs20 
\par TABLE:\tab ANDLW 7
\par \tab \tab ADDWF PCL,F
\par \tab \tab RETLW 80
\par \tab \tab RETLW 110
\par \tab \tab RETLW 140
\par \tab \tab RETLW 160
\par \tab \tab RETLW 180
\par \tab \tab RETLW 200
\par \tab \tab RETLW 220
\par \tab \tab RETLW 240
\par 
\par PRESET:\tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab BCF TRISA,4
\par \tab \tab MOVLW 255
\par \tab \tab MOVWF TRISB
\par \tab \tab BCF OPTION,7
\par \tab \tab PAGE0
\par 
\par SETUP:\tab MOVLW 8
\par \tab \tab MOVWF LOOPA
\par \tab \tab CLRF COUNT
\par \tab \tab MOVLW NOTE1
\par \tab \tab MOVWF FSR
\par 
\par SETUP1:\tab MOVF COUNT,W
\par \tab \tab CALL TABLE
\par \tab \tab MOVWF INDF
\par \tab \tab INCF FSR,F
\par \tab \tab INCF COUNT,F
\par \tab \tab DECFSZ LOOPA,F
\par \tab \tab GOTO SETUP1
\par 
\par GETKEY:\tab MOVF PORTB,W
\par \tab \tab MOVWF STORE
\par \tab \tab MOVLW 8
\par \tab \tab MOVWF LOOPA
\par 
\par ROTATE:\tab BTFSS STORE,7
\par \tab \tab GOTO PLAY
\par \tab \tab BSF STATUS,C
\par \tab \tab RLF STORE,F
\par \tab \tab DECFSZ LOOPA,F
\par \tab \tab GOTO ROTATE
\par \tab \tab GOTO GETKEY
\par \tab 
\par PLAY:\tab DECF LOOPA,W
\par \tab \tab ADDLW NOTE1
\par \tab \tab MOVWF FSR
\par \tab \tab DECFSZ INDF,F
\par \tab \tab GOTO GETKEY
\par \tab \tab DECF LOOPA,W
\par \tab \tab CALL TABLE
\par \tab \tab MOVWF INDF
\par 
\par OUTPUT:\tab MOVLW 16
\par \tab \tab ADDWF PORTA,F
\par \tab \tab GOTO GETKEY
\par \tab 
\par Time now to examine a concept that allows us to access generalised routines
\par which can manipulate file values without actually specifying the file names
\par within them. This concept is called "Indirect Addressing". It also has
\par profound implications for the ability to minimise the number of
\par sub-routines required by a program. Program TUT19, which uses the
\par technique, will then be discussed and demonstrated. Indirect Addressing
\par allows the use of generalised routines which do not apply to any specific
\par files. The file(s) which the routine accesses are specified prior to entry
\par into the routine and can be changed at will to suit different aspects of
\par the program.
\par 
\par \plain\f2\fs24\b Commands FSR and INDF
\par \plain\f2\fs20 
\par The two key commands (or, rather, "file registers") in Indirect Addressing
\par are FSR (File Special Register) and INDF (INDirect File). The idea of
\par Indirect Addressing is that you place the address of the file that you wish
\par to access in file FSR. Commands to access the specified file address are
\par then made via file INDF. Not only does this facility allow the same routine
\par to be applied to different calling routines, it also allows a loop to
\par access a sequence of files without having to specify their individual
\par addresses other than that for one of them in the sequence.
\par 
\par In the following example, assume that we have a sequence of files between
\par address $10 and $1F (eight files). Let's call the first file NOTE0. Its
\par address will have been equated at the head of the program in the usual way.
\par However, provided we assume the next seven addresses to be reserved for
\par seven files which are consecutive to NOTE0, we do not have to give them
\par names unless we actually need to use the names in the body of the program.
\par Even then the names could be anything we like; they do not have to called
\par NOTE1, NOTE2 etc., unless we wish to.
\par 
\par Suppose, for example, we wished to clear all eight of these files prior to
\par another routine and that we shall do it in ascending order using a loop.
\par Prior to entering the loop we get the address of the first file, in this
\par case NOTE0, copy it into FSR and reset the loop counter, let's call it
\par LOOPA:
\par 
\par MOVLW NOTE0
\par MOVWF FSR
\par CLRF LOOPA
\par 
\par Now all we need to do is use the following simple routine:
\par 
\par RESET: CLRF INDF,F
\par        INCF FSR,F
\par        INCF LOOPA,F
\par        BTFSS LOOPA,3
\par        GOTO RESET
\par 
\par Command CLRF INDF,F clears the file whose address is held in FSR. Next,
\par INCF FSR,F increments the value held by FSR, in other words FSR is
\par incremented to point to the next file we wish to clear (NOTE0 in the first
\par instance of the loop, NOTE1 in the next). Next, we increment the loop
\par counter, INCF LOOPA,F, and test its bit 3 (BTFSS LOOPA,3) to see if a count
\par value of 8 (00001000) has been reached (remember we started at 0). If the
\par count is not yet 8, the loop is repeated, GOTO RESET. If the count equals
\par 8, the next command after GOTO RESET is performed, whatever that might be
\par in a full program. Another way of doing it (and there are several ways) is:
\par 
\par        MOVLW NOTE0
\par        MOVWF FSR
\par        MOVLW 8
\par        MOVWF LOOPA
\par RESET: CLRF INDF,F
\par        INCF FSR,F
\par        DECFSZ LOOPA,F
\par        GOTO RESET
\par 
\par You can also use similar constructions to access a sequence of table values
\par (from anywhere within that table) and add them to the values within a
\par sequence of indirectly addressed files, keeping the maximum resulting
\par addition to less than 32.
\par 
\par In the following example (nothing directly to do with TUT19), the first
\par address required in the table is at jump 3. We want to start adding the
\par acquired table value to the file starting six bytes beyond NOTE0 (note how
\par the value of 6 is added to the known address of NOTE0) and that we want to
\par perform the action five times (once for each note).
\par 
\par          MOVLW 3
\par          MOVWF COUNT
\par          MOVLW 6
\par          ADDLW NOTE0
\par          MOVWF FSR
\par          MOVLW 5
\par          MOVWF LOOPA
\par GETVAL:  MOVF COUNT,W
\par          CALL TABLE
\par          ADDWF INDF,W
\par          ANDLW 31
\par          MOVWF INDF
\par          INCF FSR,F
\par          INCF COUNT,F
\par          DECFSZ LOOPA,F
\par          GOTO GETVAL
\par 
\par \plain\f2\fs24\b Indirect Addressing Demonstrated\plain\f2\fs20 
\par 
\par In the following worked example, part of whose program is shown in Listing
\par 19, we demonstrate how Indirect Addressing allows generalised file
\par accessing routines to be used, how a table can help in that process, and
\par how it helps code to be compacted to achieve more actions within the space
\par available. With your audio monitor still connected, load TUT19.OBJ and run
\par it, playing with the eight push-switches on PORTB (SB0 to SB7). You will
\par find that all eight switches produce "notes", but not musically tuned,
\par though! They are also much lower in pitch because of the extra number of
\par commands being processed. The technique used is, in effect, the same as
\par that demonstrated in TUT18. There are, though, some notable (no pun!)
\par differences:
\par 
\par First, if you look at the full listing on your disk, you will see that in
\par the initialisation, we have only equated NOTE1 (NOTE0 is not used this
\par time) there is no mention of FREQ1 etc. Yet, we are actually using eight
\par files to behave as NOTE1 to NOTE8 and we use a table instead of FREQ1 to
\par FREQ8. What we have done (as discussed a moment ago) is to consider a block
\par of consecutive file addresses to be allocated to NOTE1/NOTE8, starting at
\par $10. To remind us at some future time, there is a comment alongside NOTE1
\par to this effect in the full disk listing. The next address which we specify
\par cannot, therefore, occur until eight bytes later, at $18, where LOOPA is
\par equated. Any consecutive block of eight bytes could have been used.
\par 
\par As seen in the full program and the extract listed here, a table has eight
\par values in it and an AND command limits the jump span from zero to seven.
\par The values shown are the tuning values which will be accessed periodically
\par throughout the program while it is running.
\par 
\par \plain\f2\fs24\b OPTION Bit 7\plain\f2\fs20 
\par 
\par In the PRESET routine, note the command BCF OPTION,7. This statement
\par activates PORTB's internal "light pull-ups". This facility allow switches
\par to be used without biassing resistors (assume as a rule-of-thumb that they
\par have an equivalent resistance of about 100k ohms). Note that the default
\par value for each bit in OPTION at power-up and reset is 1 (i.e. 11111111).
\par 
\par Making use of the light pull-ups, however, means that PORTB's input pins
\par are active low, rather than active high as in the previous switch
\par monitoring examples. This means that the port pins to which the switches
\par are connected are normally held high, a switch press then taking them low.
\par Consequently, it is the low condition for which we shall be looking.
\par 
\par Routines SETUP and SETUP1 make use of the indirect addressing facility to
\par set the initial (FREQ) values into the eight notes. Next comes routine
\par GETKEY in which the status of the eight switches is obtained in the usual
\par way. Now, though, we have to get round a table problem. There are 256
\par possible combinations of the switches and we only want eight of them, those
\par for any single switches being pressed. To use a 256 jump table would be
\par extravagant with program space, besides which the jump addresses alone
\par would occupy the entire table block allowable (without compensatory action
\par through PCLATH).
\par 
\par We could, of course, not use a table but simply test each bit of PORTB in
\par turn, and use GOTO statements to obtain data about which note should be
\par played and which note reset value is needed. Instead, though, for the sake
\par of demonstration, a different technique is used, converting the 8-bit PORTB
\par value to a 3-bit value, covering eight possible combinations rather than
\par 256. PORTB's value is copied into STORE and a loop set for a maximum of
\par eight operations. Up to eight rotate left (RLF) actions can then be called
\par in routine ROTATE, and the value of STORE bit 7 tested. Each bit of STORE
\par corresponds to a separate switch, so the rotation allows all eight switches
\par to be tested. The switches have been set to be active-low, i.e. when "on" a
\par zero occurs on its PORTB bit. If a zero is found during the rotation, the
\par value of the loop corresponds to the switch in question and a jump is made
\par to the play routine. If no zero is found, then no switches are pressed and
\par no note play action occurs.
\par 
\par In the PLAY routine, the loop value (LOOPA) is decremented while being
\par moved into W (the loop value will be between 8 and 1 but for program ease
\par we need a value between 7 and 0). The value of W is added to the address of
\par NOTE1 and the answer is put into the indirect address register FSR. The
\par note now pointed to by FSR is decremented via INDF and if the result is not
\par zero, a return to GETKEY is made. A zero result causes the value of LOOPA
\par to again be decremented into W and then the table is called, returning with
\par the reset value for the note in use, which is put into it via INDF. As we
\par have seen before, the output value at PORTA RA4 is then incremented and a
\par jump back to GETKEY occurs.
\par 
\par Had this whole operation been programmed as separate routines for each
\par note, its length would have been considerably greater; indirect addressing,
\par bit rotation and a table have changed that. We shall use indirect
\par addressing again later.
\par 
\par \plain\f2\fs24\b Exercise 16\plain\f2\fs20 
\par 
\par 16.1. In Program TUT19, priority has been given to switches in descending
\par order (test bit 7). How would you rewrite to give priority in ascending
\par order?
\par 
\par 16.2. If you wanted one of the switches to be ignored, what extra
\par command(s) would be needed, and where? When considering where, think of the
\par number of times the situation has to be checked for between each input of
\par PORTB's value, remembering that each command processed unnecessarily wastes
\par valuable time.
\par 
\par 16.3. In this program, is the AND command at the head of the table actually
\par necessary?
\par 
\par 16.4. As the program stands, there is one extra file name used than needs
\par to be; which file could be used in two situations?
\par 
\par 16.5. Also, with careful thought, parts of the program could be slightly
\par rewritten to save at least seven commands. Can you spot how this could be
\par done? Question all aspects, from initialisation downwards (see also the
\par full listing).
\par 
\par (Whilst the SETUP routine could be heavily rewritten to save four of these
\par commands, in a real programming situation, unless you are extremely short
\par of program space, it is better to concentrate on saving commands in
\par routines that are being called frequently, so significantly increasing the
\par speed of operation. SETUP is only used once, and so has no affect on the
\par loop speed.)
\par 
\par \plain\f2\fs28\b Tutorial 17\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Command XOR
\par Command IOR
\par Tone modulation
\par 
\par In a moment, we shall come down to a somewhat simpler audio program, in
\par which we illustrate how two tones can be created, one modulated, the other
\par fixed. Both tones could find use in, for example, a simple intruder alarm.
\par Also to be illustrated is how the combined status of two or more switches
\par on a port can be tested using the XOR (Exclusive-OR) command. This allows
\par us to take one action only if all the specified switches are on
\par simultaneously, otherwise taking another action. First, let's examine the
\par XOR command on its own.
\par 
\par \plain\f2\fs24\b Command XOR\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b LISTING 20 - PROGRAM TUT20
\par \plain\f2\fs20 
\par GETKEY:\tab MOVF PORTA,W
\par \tab \tab ANDLW %00011111
\par \tab \tab XORLW %11110010
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO GETKEY
\par 
\par The command XOR checks for "equality" between two numbers. There are two
\par commands, XORLW (XOR Literal with W) and XORWF (XOR W with value in
\par specified File). The latter is followed by the file name, a comma, and the
\par destination (W or F), e.g. XORWF STORE,W and XORWF STORE,F. Probably you
\par know that in electronics there are XOR gates included in the digital logic
\par chip families, and you will no doubt have read descriptions of truth tables
\par relating to just two inputs of an XOR gate (two bits):
\par 
\par 0 XOR 0 = 0
\par 0 XOR 1 = 1
\par 1 XOR 0 = 1
\par 1 XOR 1 = 0
\par 
\par As far as the PIC16x84 XOR function is concerned, the result of XORing two
\par bytes of eight bits is the condition being checked. It is easier here to
\par show the principle by means of switches and LEDs rather than by truth
\par tables. To do this we should really use eight switches on one port and
\par eight LEDs on the other. However, since PORTA has only five I/O pins, we
\par shall just use a 5-bit number to illustrate the principle, with five
\par switches on PORTA and five LEDs on PORTB.
\par 
\par The basic program we shall use is shown in Listing 20. Disconnect the audio
\par output, load program TUT20.OBJ, run it and play with PORTA's push-switches.
\par You will find that when no switches are pressed, PORTB LEDs LB0, LB2 and
\par LB3 are off, and LB1 and LB4 are on. When switches SA1 and SA4 are pressed,
\par they turn off their respective LEDs (LB1 and LB4). Switches SA0, SA2 and
\par SA3 turn on their LEDs (LB0, LB2 and LB3) when pressed. You will also see
\par that the lefthand three LEDs (LB5 to LB7) are always on.
\par 
\par In this listing, the value on PORTA is input as usual. The next command
\par (ANDLW %00011111) is necessary to this demonstration since we can only use
\par the first five bits of PORTA. If all eight bits could be used, the AND
\par command would be omitted. The status of each switch is being XORed with the
\par respective bit in the statement XORLW %11110010; switch 0 with bit 0,
\par switch 1 with bit 1, etc. If any bit of PORTA is equal to that of the same
\par bit in the XOR command, the same bit in the W register will be cleared.
\par Thus two zeros will produce a 0, and two 1s will produce a 0. If the bits
\par are dissimilar (1 and 0) the W bit is set (1). The reason that the three
\par lefthand LEDs are on is that bits 5 to 7 from the AND command and bits 5 to
\par 7 from the XOR command have resulted in three non-equalities.
\par 
\par Suppose that the switches produce binary number 00111, the ANDed result in
\par W is 00000111, the sequence of events is:
\par 
\par MOVF PORTA,W      answer = xxx00111
\par ANDLW %00011111   answer = 00000111
\par XORLW                      11110010
\par                   answer = 11110101
\par 
\par Bits that are equal to their counterparts have their corresponding LEDs
\par turned off, those that are NOT equal have their LEDs turned on. Take
\par another example:
\par 
\par MOVF PORTA,W      answer = xxx10010
\par ANDLW %00011111   answer = 00010010
\par XORLW                      00010010
\par                   answer = 00000000
\par 
\par Here each bit is equal to its counterpart, therefore all LEDs are turned
\par off, i.e. a zero result has occurred and, importantly, the Zero flag will
\par have been set accordingly. Therefore, we can check for equality by checking
\par the Zero flag following an XOR command. Non-equality clears the flag,
\par equality sets it. Consequently, following an XOR command you simply check
\par STATUS,Z and route accordingly.
\par 
\par \plain\f2\fs24\b LISTING 21 - PROGRAM TUT21
\par \plain\f2\fs20 
\par GETKEY:\tab MOVF PORTA,W
\par \tab \tab ANDLW %00011111
\par \tab \tab XORLW %00010010
\par \tab \tab MOVWF PORTB
\par \tab \tab BTFSC STATUS,Z
\par \tab \tab BSF PORTB,7
\par \tab \tab GOTO GETKEY
\par 
\par Let's use LED LB7 to illustrate this, turning it on if equality exists,
\par turning it off if it doesn't. Any bit between 0 and 4 which is equal to the
\par same XOR bit will have its corresponding LED turned off, otherwise its LED
\par will be on. Load and run TUT21.OBJ, pressing PORTA switches SA0 to SA4 to
\par observe this in action. Pressing SA4 and SA1 together causes LB7 to come
\par on. The commands are shown in Listing 21.
\par 
\par \plain\f2\fs24\b Command IOR
\par \plain\f2\fs20 
\par Although we shall not meet it until later (Tutorial 21), it is opportune to
\par mention now that there is an "ordinary" OR command available. It is more
\par correctly termed "Inclusive-OR" (as opposed to Exclusive-OR). It has two
\par versions, IORLW (Inclusive OR Literal with W) and IORWF (IOR W with value
\par in specified File). The latter is followed by the file name, a comma, and
\par the destination (W or F), e.g. IORWF STORE,W and IORWF STORE,F.
\par 
\par \plain\f2\fs24\b Modulation
\par \plain\f2\fs20 
\par \plain\f2\fs24\b LISTING 22 - PROGRAM TUT22
\par \plain\f2\fs20 
\par ENTRY:\tab MOVLW 80
\par \tab \tab MOVWF NOTE
\par \tab \tab MOVWF FREQ
\par \tab \tab MOVLW 16
\par \tab \tab MOVWF MODLAT
\par 
\par GETKEY:\tab MOVF PORTA,W
\par \tab \tab ANDLW %00000011
\par \tab \tab XORLW %00000011
\par \tab \tab BTFSC STATUS,Z
\par \tab \tab GOTO GETKEY
\par \tab \tab DECFSZ NOTE,F
\par \tab \tab GOTO GETKEY
\par \tab \tab MOVF FREQ,W
\par \tab \tab BTFSC PORTA,1
\par \tab \tab GOTO OUTPUT
\par \tab \tab DECFSZ MODLAT,F
\par \tab \tab GOTO GK2
\par \tab \tab MOVLW 16
\par \tab \tab MOVWF MODLAT
\par GK2:\tab \tab ADDWF MODLAT,W
\par 
\par OUTPUT:\tab MOVWF NOTE
\par \tab \tab MOVLW 16
\par \tab \tab ADDWF PORTA,F
\par \tab \tab GOTO GETKEY
\par 
\par The use of XOR in a practical situation is illustrated in Listing 22.
\par Reconnect the audio output. Load and run TUT22.OBJ, pressing any switches
\par SA0 to SA4, but principally use switches SA0 and SA1 since these are the
\par ones coded to be active. Listening to the output from PORTA, you will find
\par that switch SA0 controls a static tone and SA1 controls a modulated
\par (ramped) tone. As you will have heard, the tone starts at a high pitch,
\par descends and then jumps back high again, repeatedly (adjust VR1 until this
\par fact is more obvious). All other switches are ignored. Look at the
\par program's listing.
\par 
\par As with earlier tone generation examples, a starting value is loaded into
\par NOTE and FREQ, then a modulation starting value is loaded into MODLAT,
\par after which the GETKEY loop is entered. Here the switch settings on PORTA
\par are read (normally both RA0 and RA1 are biassed high), and ANDed with
\par 00000011 to isolate switches SA0 and SA1. The answer is XORed with the same
\par value to check for equality. If neither switch is pressed, no further
\par action is required and the routine jumps back to GETKEY.
\par 
\par We are looking for the situation in which either of the two switches is
\par pressed. We could do it simply by bit testing (indeed, it would be
\par easier!), but part of the aim of this demo is to show a use of XOR. When
\par either switch is pressed, NOTE is decremented and checked for zero and
\par reset as appropriate, as before. When zero is encountered, if switch SA1 is
\par pressed, the value of MODLAT is added to the NOTE reset value and the value
\par of MODLAT itself is then decremented. When MODLAT reaches zero, it is reset
\par to 16. The OUTPUT routine is common to both switch routings.
\par 
\par \plain\f2\fs24\b Exercise 17\plain\f2\fs20 
\par 
\par 17.1. Experiment with different settings for FREQ and MODLAT
\par 
\par 17.2. How would you change the coding to respond to two other switches
\par instead, e.g. SA2 and SA4?
\par 
\par 17.3. How would you reverse the ramp to create a rising tone rather than a
\par falling one?
\par 
\par 17.4. The addition of a third switch would allow tones to be switched for
\par rising, falling or fixed. Can you write the program for it?
\par 
\par 17.5. Can you add another routine which would create a triangular
\par modulation pattern (rising tone, followed by falling, followed by rising,
\par and so on)?
\par 
\par \plain\f2\fs28\b Tutorial 18\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Command INTCON
\par Command OPTION
\par Command TMR0
\par Use of internal timer
\par 
\par \plain\f2\fs24\b LISTING 23 - PROGRAM TUT23
\par \plain\f2\fs20 
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISB
\par \tab \tab MOVLW %00000000
\par \tab \tab MOVWF OPTION
\par \tab \tab PAGE0
\par \tab \tab CLRF RATE
\par \tab \tab MOVLW 8
\par \tab \tab MOVWF COUNT
\par \tab \tab BCF INTCON,2
\par 
\par INTRPT:\tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab MOVLW %00001000
\par \tab \tab ADDWF PORTB,F
\par \tab \tab BTFSS STATUS,C
\par \tab \tab GOTO INTRPT
\par \tab \tab DECFSZ COUNT,F
\par \tab \tab GOTO INTRPT
\par \tab \tab BSF COUNT,3
\par \tab \tab INCF RATE,W
\par \tab \tab ANDLW 7
\par \tab \tab MOVWF RATE
\par \tab \tab MOVWF PORTB
\par \tab \tab PAGE1
\par \tab \tab MOVWF OPTION
\par \tab \tab PAGE0
\par \tab \tab GOTO INTRPT
\par 
\par The PIC16x84 has one special register reserved for use as an 8-bit timer,
\par TMR0 (Timer 0). It divides its input frequency by 256 and can be both
\par written to and read from. In most situations, though, it is unlikely that
\par you will need to use the read/write facility, but note that if TMR0 is
\par written to, the timer is inhibited from counting for two clock cycles.
\par Probably more useful than writing to TMR0 is to use its output as it occurs
\par naturally at the 1:256 division rate, and then to use the prescaler to
\par subdivide that rate as required. The prescaler divides its input pulses by
\par presettable powers of two. There are eight possible division ratios which
\par are set via bits 0, 1 and 2 of the OPTION register. When used with TMR0,
\par the prescaler division ratios are 1:2, 1:4, 1:8, 1:16, 1:32, 1:64, 1:128
\par and 1:256. The prescaler can alternatively be allocated for use with the
\par Watchdog Timer (WDT), in which mode each of these ratios is halved (minimum
\par is thus 1:1 and maximum is 1:128) - more on this later.
\par 
\par We noted earlier that the PIC effectively runs at one quarter of the input
\par clock frequency at pin 16 (OSC1/CLKIN). When TMR0 is used as an internal
\par timer, the pulses it counts also occur at one quarter of the clock
\par frequency. So, if the clock frequency (set by a crystal oscillator,
\par perhaps) is running at 3.2768MHz, TMR0 will count at 819200Hz and its 1:256
\par roll-over rate will be 3200Hz. This rate is then divided by the ratio set
\par into the prescaler. If we divide by 32, for example, we obtain the
\par convenient rate of 100Hz.
\par 
\par In TMR0 mode, when the prescaler rolls-over to zero, a flag is set in the
\par INTCON register, at bit 2. The setting of this bit can be used as an
\par interrupt which automatically routes the program to another specified
\par routine, irrespective of which routine is currently being processed,
\par returning to the same point after the interrupt procedure has been
\par finished. The interrupt can also be turned off and INTCON bit 2 read by the
\par program to establish its status, taking action accordingly.
\par 
\par \plain\f2\fs24\b Timer Sub-division\plain\f2\fs20 
\par 
\par Using the timer and the prescaler, you can specify that some actions will
\par only be performed at specified sub-divided values of the clock frequency.
\par Amongst other things, this allows the PIC16x84 to be used as a real-time
\par clock, a function towards which we now progress.
\par 
\par First, let's illustrate the effect of setting different prescaler ratios
\par and, using the LEDs on PORTB, see what happens. Load TUT23.OBJ and run it.
\par Set VR1 to full anticlockwise rotation (slowest rate). In this program we
\par read the status of INTCON bit 2 rather than using the interrupt facility
\par (interrupts are discussed in Tutorial 28). Initially, you will see a fairly
\par fast binary count occurring on PORTB's LEDs, LB3 to LB7. It is created with
\par the timer "in-circuit" with the prescaler set for a minimum division ratio
\par of 1:2. This is because OPTION bits 0 to 2 are set to 000, a value which is
\par shown on LB0, LB1 and LB2 - all off at the moment.
\par 
\par This rate of counting continues for eight cycles of 32 increments
\par (incrementing PORTB's count in steps of eight). The ratio is then set at
\par 1:4 (prescaler value 001), and again another eight cycles occur. Similarly,
\par the other ratios are set. The difference in the resulting LED count rates
\par will be obvious. Adjust the setting of preset VR1 if the slowness becomes
\par tedious in later ratios. After the eight ratios, the whole cycle restarts
\par from 1:2.
\par 
\par Looking at Listing 23, you will see that the TMR0 rate is set into the
\par OPTION register while in PAGE1 mode, along with the port direction
\par registers. The initial value of zero is put into OPTION, shown here as a
\par binary number. Although, in this instance, CLRF OPTION could have been used
\par instead, you should be aware that OPTION bits 2 to 7 have other functions
\par and in some programs it will be necessary to set these at the same time, so
\par CLRF OPTION would not always be an appropriate command. More on other
\par functions later. It is best to stick with the binary format in such
\par listings so that the settings of all the bits are immediately obvious.
\par 
\par \plain\f2\fs24\b Exercise 18\plain\f2\fs20 
\par 
\par 18.1. Study TUT23.ASM, note the comments and see if you understand what is
\par happening at each stage. Note the detection and resetting of the INTCON,2
\par flag and the need to go via PAGE1 when changing the prescaler rate.
\par 
\par \plain\f2\fs28\b Tutorial 19\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPT EXAMINED\plain\f2\fs20 
\par 
\par BCD (Binary Coded Decimal) counting
\par 
\par \plain\f2\fs24\b LISTING 24 - PROGRAM TUT24
\par \plain\f2\fs20 
\par INTRPT:\tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab INCF COUNT,F
\par \tab \tab MOVF COUNT,W
\par \tab \tab ADDLW 6
\par \tab \tab BTFSS STATUS,DC
\par \tab \tab GOTO OUTPUT
\par \tab \tab MOVWF COUNT
\par \tab \tab ADDLW 96
\par \tab \tab BTFSC STATUS,C
\par \tab \tab CLRF COUNT
\par OUTPUT:\tab MOVF COUNT,W
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO INTRPT
\par 
\par Having established the use of the timer, we now work towards its use as the
\par pulse source for a real-time clock. There are a few bridges to be crossed
\par yet, though. The first is counting in decimal rather than binary,
\par facilitating the eventual output to a 7-segment LED or a liquid crystal
\par display. We could keep the counted units in one byte, tens in another,
\par hundreds in another, and so on, but, to conserve precious byte space, it is
\par equally possible to use each byte as two 4-bit nibbles, keeping units in
\par bits 0 to 3, and tens in bits 4 to 7. Hundreds units and tens would be
\par treated similarly in a second byte.
\par 
\par For simplicity now, we concentrate on counting up to 99, first considering
\par the use of two bytes. In 8-bit binary, a value of decimal 9 is expressed as
\par 00001001, decimal 10 is 00001010, decimal 16 is 00010000. It is obvious
\par that with decimal values we have no single symbol for a number greater than
\par nine. When a value one greater than nine occurs, what we do is reset the
\par units digit to 0 and add one to the next digit, i.e. ten is written as 10.
\par While counting in binary coded decimal (BCD), we can do a similar thing.
\par When the byte holding the units reaches ten, we reset that byte to zero and
\par add one to the next byte. In 8-bit BCD and at a count of nine, the two
\par bytes would read 00000000 (tens) and 00001001 (units). At the count of ten,
\par the bytes become 00000001 (tens) and 00000000 (units).
\par 
\par When using two nibbles of an 8-bit byte (instead of the above two bytes), a
\par BCD value of nine reads as 00001010, but a BCD value of ten reads as
\par 00010000. And, for example, a BCD value of 37 reads as 00110111, i.e. the
\par lefthand nibble (MSN - Most Significant Nibble) holds a value of 3 and the
\par righthand nibble (LSN - Least Significant Nibble) holds 7. A value of 99 is
\par expressed as 10011001. For a value of 100, both nibbles are reset to zero
\par (00000000) and if there is a byte for hundreds and tens of hundreds, its
\par righthand nibble (LSN) would be incremented, and so on.
\par 
\par Thus, when counting in BCD, we have to check the four bits of the LSN on
\par their own and see if their value is greater than nine. If it is, that
\par nibble is reset and the MSN incremented. The MSN is then taken on its own
\par as a 4-bit value and checked if it is greater than nine. If so, this nibble
\par is reset and the LSN of the next byte incremented accordingly.
\par 
\par \plain\f2\fs24\b Checking for Excess Values\plain\f2\fs20 
\par 
\par There are (as in many programming matters) several ways of checking the
\par nibbles for excess values, of which we shall describe one: an additive
\par checking routine. We discovered earlier (Tutorial 7) that there is a Digit
\par Carry (DC) flag which signals if the binary value of the LSN has become
\par greater than 15 following an addition. We can use this fact by adding a
\par number to the LSN which will make the answer greater than 15 if the basic
\par value of the LSN is greater than 9. The number to be added is 6, e.g. 10
\par + 6 = 16 with DC flag set; 9 + 6 = 15 with DC flag clear. Therefore, to
\par check if an LSN value is greater than 9, we temporarily add 6 to it and
\par check the DC flag. If the flag is clear, the LSN is left as it is. If the
\par flag is set, we increment the MSN and clear the LSN.
\par 
\par There is a short cut to doing this, taking advantage of the fact that 10
\par + 6 = 16, being 00010000 in binary. If you look at this answer, the LSN is
\par now zero, while the MSN has been incremented automatically, thus
\par representing decimal 10 in BCD. Thus, when we add 6 to the byte as a whole,
\par if the DC flag is clear, no further action on that byte is needed (or on
\par any subsequent bytes for that matter). If, though, the DC flag is set, we
\par simply replace the existing value in the byte with the value now stored
\par temporarily. These commands do the job:
\par 
\par MOVLW 1         ; move value to be added into W
\par ADDWF COUNT,F   ; add it to file value
\par MOVLW 6         ; move 6 into W
\par ADDWF COUNT,W   ; add it to new file value but keep answer in W
\par BTFSC STATUS,DC ; is the Digit Carry flag clear?
\par MOVWF COUNT     ; no, so move W into file, replacing previous value
\par (next command)
\par 
\par Whilst any value can be added to COUNT in the first instance and set the DC
\par flag accordingly, the DC flag is unaffected by an INCF or INCFSZ command
\par (neither is the Carry flag). Consequently, in the above example, the
\par opening two commands cannot be replaced by INCF COUNT,F. When the DC flag
\par is set, the resulting action changes the value of the MSN, which then has
\par to be checked to see if it (as a 4-bit nibble) is greater than 9, i.e. is
\par the BCD value of the whole byte now equal to or greater than decimal 100?
\par 
\par Again there is an easy additive technique. If we translate BCD 100
\par (10100000) into a binary value the answer is 160. If we temporarily add 96
\par (256 - 160) to the whole byte, we can then check the Carry flag (C) to see
\par if it has been set, which it will be if the binary answer has rolled over
\par beyond 255. As before, if the flag is clear, the byte can remain as is; if
\par the flag is set, we replace the value with the temporary one.
\par 
\par Here's the extended routine. Note the inverted logic for checking Digit
\par Carry, BTFSS STATUS,DC in the first instance, BTFSC STATUS,DC in the
\par second.
\par 
\par               MOVLW 1
\par               ADDWF COUNT,F
\par               MOVLW 6
\par               ADDWF COUNT,W
\par               BTFSS STATUS,DC
\par               GOTO ENDADD
\par               MOVWF COUNT
\par               MOVLW 96
\par               ADDWF COUNT,W
\par               BTFSC STATUS,C
\par               MOVWF COUNT
\par ENDADD:
\par 
\par Let's look at the BCD additive technique in practice, triggering it from
\par the timer routine. In Listing 24, note the use of CLRF COUNT before OUTPUT
\par at the end. This can be used here since we know that adding 1 to the count
\par is occurring, rather than adding values of 2 or greater. In the latter
\par instance, the resulting temporary answer must be MOVed into COUNT, as in
\par the above examples. Load TUT24.OBJ, run it and observe the count
\par incrementing on the LEDs. The prescaler is now run at a fixed ratio of
\par 1:128. Try adjusting VR1 so that an LED count rate of one per second (1Hz)
\par occurs.
\par 
\par \plain\f2\fs24\b Exercise 19
\par \plain\f2\fs20 
\par 19.1. Suppose our counting system was not decimal but quinary, i.e. no
\par digit greater than 5, rather than no digit greater than 9. How would you
\par change the additive values in the above examples (you can use decimal,
\par binary or hexadecimal for those!).
\par 
\par 19.2. Checking for excess BCD values can be done using an XOR technique
\par which is valid if the count is being incremented rather than added to.
\par Adding to the BCD value cannot be used with XOR since the answer could be
\par to either side of the equality being checked for. Can you write an XORed
\par BCD incrementing program?
\par 
\par \plain\f2\fs28\b Tutorial 20\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Precision timing at 1/25th second
\par Counting seconds 0 to 60
\par 
\par \plain\f2\fs24\b LISTING 25 - PROGRAM TUT25
\par \plain\f2\fs20 
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISB
\par \tab \tab MOVLW %00000110
\par \tab \tab MOVWF OPTION
\par \tab \tab PAGE0
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab \tab CLRF CLKSEC
\par \tab \tab BCF INTCON,2
\par 
\par INTRPT:\tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab DECFSZ CLKCNT,F
\par \tab \tab GOTO INTRPT
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab \tab INCF CLKSEC,F
\par \tab \tab MOVF CLKSEC,W
\par \tab \tab ADDLW 6
\par \tab \tab BTFSS STATUS,DC
\par \tab \tab GOTO OUTPUT
\par \tab \tab MOVWF CLKSEC
\par \tab \tab MOVLW %01100000
\par \tab \tab XORWF CLKSEC,W
\par \tab \tab BTFSC STATUS,Z
\par \tab \tab CLRF CLKSEC
\par \tab 
\par OUTPUT:\tab MOVF CLKSEC,W
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO INTRPT
\par \tab 
\par Moving on from decade counting between 0 and 99, it is an easy step to
\par count in BCD from 0 to 59, accurately simulating the seconds count of a
\par real-time clock. In doing so, though, it can be useful to actually increase
\par the count rate available via the prescaler from 1Hz to 25Hz, 50Hz or even
\par 100Hz. Indeed, if a crystal oscillator running at the convenient rate of
\par 3.2768MHz is used, it is actually easier to work with one of these three
\par rates. This is due to the sub-division values available from a crystal of
\par this frequency which can used in conjunction with the TMR0. Prescaler
\par division ratios of 1:128, 1:64 or 1:32 respectively produce these rates.
\par 
\par (With the Tutorial board, the PIC16x84's operational mode is changed at
\par this point from RC to XT, allowing a 3.2768MHz crystal to be the clock
\par generator. To use the crystal, the PIC has its configuration set for XT,
\par WDT off and POR off.)
\par 
\par All of the programs you have used so far, with the exception of TUT1 and
\par TUT2, can be run under crystal control. Consequently, if you want to go
\par back and look at some of them again, you do not need to reset the PIC for
\par RC mode.
\par 
\par Load TUT25.OBJ, run it and observe PORTB's LEDs. You will see them
\par incrementing at a rate of one per second, and the twin-nibble BCD count
\par will be seen to progressively step from zero to 59, then restart again at
\par zero, just as would an ordinary seconds clock and, indeed, it should take
\par one minute for the full cycle to occur. There are two differences between
\par this program (see Listing 25) and the previous one (apart from the precise
\par clock frequency that is being generated):
\par 
\par First, the prescaler rate has been set for 1:128, providing an INTCON,2
\par pulse rate of 1/25th of second. A counter, CLKCNT, counts down from 25 in
\par response to the pulses. When it reaches zero, it is reset to 25 and a
\par seconds counter, CLKSEC is incremented in BCD. Checking for the BCD count
\par becoming ten is performed by the additive (+ 6) technique we have already
\par seen. However, checking for the count being at BCD 60 is done using the XOR
\par equality testing method (XOR 01100000 = BCD 60). If equality exists, the
\par CLKSEC counter is reset to zero.
\par 
\par There are no exercises for Tutorial 20.
\par 
\par \plain\f2\fs28\b Tutorial 21\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Using 7-segment LED displays
\par Showing hours, minutes and seconds
\par 
\par \plain\f2\fs24\b LISTING 26 - PROGRAM TUT26
\par \plain\f2\fs20 
\par TABSEG:\tab ADDWF PCL,F
\par \tab \tab RETLW %11000000 ; 0
\par \tab \tab RETLW %11111001 ; 1
\par \tab       RETLW %10100100 ; 2
\par \tab       RETLW %10110000 ; 3
\par \tab       RETLW %10011001 ; 4
\par \tab       RETLW %10010010 ; 5
\par \tab       RETLW %10000011 ; 6
\par \tab       RETLW %11111000 ; 7
\par \tab       RETLW %10000000 ; 8
\par \tab       RETLW %10011000 ; 9
\par \tab       ; common anode codes
\par 
\par INTRPT:\tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab DECFSZ CLKCNT,F
\par \tab \tab GOTO INTRPT
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab \tab INCF CLKSEC,F
\par \tab \tab MOVF CLKSEC,W
\par \tab \tab ADDLW 6
\par \tab \tab BTFSC STATUS,DC
\par \tab \tab CLRF CLKSEC
\par \tab 
\par OUTPUT:\tab MOVF CLKSEC,W
\par \tab \tab ANDLW 15
\par \tab \tab CALL TABSEG
\par \tab \tab MOVWF PORTB
\par \tab \tab GOTO INTRPT
\par \tab 
\par Obviously it is not feasible to show hours, minutes and seconds by just
\par using BCD formatted values on individual LEDs. We need a display which is
\par more suited to being understood. Such a display could be via intelligent
\par liquid crystal displays (LCDs) and a typical routine using them will be
\par shown later on. Another choice is the use of 7-segment LED displays, and
\par that is the route we now take. First, though, we must examine how the
\par output from PORTB needs to be coded to drive a single 7-segment LED. We
\par shall then extend the principle to multiplexing four such displays to show
\par a full 24-hour clock.
\par 
\par Each segment of a 7-segment LED display has to be controlled by individual
\par PIC data lines. It does not matter in which order the data lines are
\par connected to the display since the way that they are activated can be set
\par from within the PIC's controlling program. For convenience, here we use
\par PORTB lines RB0 to RB6 connected in their natural order to segments A to G.
\par 
\par In Fig.22 are shown the segments and code letters required to form the ten
\par numerals 0 to 9. Also shown are two lines of binary code. The first one
\par shows the bits which need to be taken high if a common cathode display is
\par used. The second is for a common anode display, each line being taken low
\par to turn on the segment. It is a common anode display that we use here; its
\par pinouts are shown in Fig.23. Connect it to the PCB as shown in Fig.24. But,
\par for the moment, connect LED line D1 to +5V, not to TR1. The equivalent
\par circuit diagram for this single digit is shown in Fig.25.
\par 
\par Now load TUT26.OBJ and run it. You will see the individual numerals being
\par shown on the left-hand digit on a cyclic basis from 0 to 9. The rate of
\par display is at one unit per second. In other words, it can be regarded as
\par being a seconds counter. Referring to Listing 26, you will see that the
\par counting routine is very similar to that in Listing 25, but only dealing
\par with units of seconds. Now, though, instead of the count being sent to
\par individual LEDs it is converted in the TABSEG table to the requisite
\par 7-segment code for that numeral when used with a common anode display.
\par Since we know that the value held in W when the table is called can never
\par be greater than nine, an AND command is not needed with this table.
\par 
\par Obviously, to show the tens of seconds as well we need a second 7-segment
\par display. However, it is not possible, of course, to use the same PORTB data
\par lines to control both displays simultaneously. Nor can we use PORTA for the
\par second display, it hasn't enough lines. What we can do, though, is to
\par connect PORTB to both display digits and then alternate the data being
\par output between units and tens values, turning on each (via their common
\par anode pin) only when the relevant data is being sent to them. If this
\par is done at a fast enough rate, the eye is fooled into thinking that both
\par displays are on simultaneously - persistence of vision.
\par 
\par This technique is known as multiplexing, and what we do in this instance is
\par to put the common anode of each display under control of two separate data
\par lines on PORTA, RA0 and RA1. To provide enough current to drive the
\par displays (PORTA lines on their own do not have enough current available to
\par achieve a sufficiently bright display), the port lines are buffered by
\par transistors TR1 and TR2 configured as emitter followers. The collectors are
\par connected to the +5V line and the emitters are connected to the common
\par anodes of the displays. The displays are turned on when PORTA lines go
\par high.
\par 
\par Disconnect LED line D1 from the +5V pin and connect it to TR1 emitter, as
\par shown in Fig.24. The equivalent circuit diagram of Fig.24 is shown in
\par Fig.26, but note that for this example the active digits are the lefthand
\par two, D1 and D2. Digits D3 and D4 are ignored.
\par 
\par The program which is now required to drive the two displays is shown in
\par Listing 27. Load TUT27.OBJ and run it.
\par 
\par \plain\f2\fs24\b LISTING 27 - PROGRAM TUT27
\par \plain\f2\fs20 
\par INTRPT:\tab CALL DIGSEL
\par \tab \tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab DECFSZ CLKCNT,F
\par \tab \tab GOTO INTRPT
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab \tab INCF CLKSEC,F
\par \tab \tab MOVF CLKSEC,W
\par \tab \tab ADDLW 6
\par \tab \tab BTFSS STATUS,DC
\par \tab \tab GOTO ENDTIM
\par \tab \tab MOVWF CLKSEC
\par \tab \tab MOVLW %01100000
\par \tab \tab XORWF CLKSEC,W
\par \tab \tab BTFSC STATUS,Z
\par \tab \tab CLRF CLKSEC
\par ENDTIM:\tab GOTO INTRPT
\par 
\par SECTEN:\tab SWAPF CLKSEC,W
\par \tab \tab GOTO OUTPUT
\par 
\par SECONE:\tab MOVF CLKSEC,W
\par 
\par OUTPUT:\tab ANDLW 15
\par \tab \tab CALL TABLE
\par \tab \tab MOVWF PORTB
\par \tab \tab INCF DIGIT,W
\par \tab \tab MOVWF PORTA
\par \tab \tab RETURN
\par 
\par DIGSEL:\tab INCF DIGIT,W
\par \tab \tab ANDLW 1
\par \tab \tab MOVWF DIGIT
\par \tab \tab ADDWF PCL,F
\par \tab \tab GOTO SECTEN
\par \tab \tab GOTO SECONE
\par \tab 
\par Studying Listing 27, first note that (for the sake of demo) an XOR command
\par is used to check for a count value equal to 60 (BCD). Next, and
\par significantly for two displays, digit alternating commands have been
\par introduced. At label INTRPT, the command CALL DIGSEL is given. In DIGSEL, a
\par digit counter (DIGIT) is incremented, ANDed with 1, and the result of this
\par increment is carried by W into the table that immediately follows. There
\par are only two jumps in this table, GOTO SECTEN and GOTO SECONE.
\par 
\par Routine SECTEN extracts the tens of units value. Command SWAPF CLKSEC,W
\par swaps the nibbles of the seconds and holds the result in W, putting the
\par tens of seconds into the LSN position. The routine then jumps to OUTPUT,
\par where command ANDLW 15 isolates that nibble, zeroing the MSN bits now in W.
\par Next, the TABSEG table (as in Listing 26) is called to obtain the 7-segment
\par code for that number, which is output to PORTB. Now the digit counter value
\par is obtained (INCF DIGIT,W) and output to PORTA to turn on that digit of the
\par display. The INCF command is used because DIGIT only alternates between 0
\par and 1, whereas PORTA needs to be alternated between 1 and 2 (binary 01 and
\par 10). Routine SECONE is similar, dealing with the units of seconds. Here we
\par can simply get the LSN by using MOVF CLKSEC,W, ANDing it with 15 at OUTPUT.
\par The rate of alternation between the two digits is several kilohertz,
\par slowing down briefly each time a time-out is detected.
\par 
\par Whilst one would like to use six digits in order to display a full 24-hour
\par clock showing hours, minutes and seconds simultaneously, this is not
\par convenient since we only have five lines on PORTA which can control
\par individual digits. Therefore, we must compromise and continue to use a
\par 4-digit display but which can now have its data sources changed when a
\par switch is pressed. In this way, we can show either hours and minutes
\par together, or minutes and seconds. The program which does this is TUT28,
\par part of which is shown in Listing 28. Load TUT28.OBJ and run it, then look
\par at its listing.
\par 
\par \plain\f2\fs24\b LISTING 28 - PROGRAM TUT28
\par \plain\f2\fs20 
\par DIGSEL:\tab INCF DIGIT,W
\par \tab \tab ANDLW 3
\par \tab \tab MOVWF DIGIT
\par \tab \tab BTFSS PORTA,4
\par \tab \tab ADDLW 2
\par \tab \tab ADDWF PCL,F
\par \tab \tab GOTO HRSTEN
\par \tab \tab GOTO HRSONE
\par \tab \tab GOTO MINTEN
\par \tab \tab GOTO MINONE
\par \tab \tab GOTO SECTEN
\par \tab \tab GOTO SECONE
\par 
\par DIGSHW:\tab MOVF DIGIT,W
\par \tab \tab ADDWF PCL,F
\par \tab \tab RETLW 1
\par \tab \tab RETLW 2
\par \tab \tab RETLW 4
\par \tab \tab RETLW 8
\par 
\par INTRPT:\tab CALL DIGSEL
\par \tab \tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab CALL CLKADD
\par \tab \tab GOTO INTRPT
\par \tab 
\par CLKADD:\tab DECFSZ CLKCNT,F
\par \tab \tab RETURN
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab 
\par SECCLK:\tab INCF CLKSEC,F
\par \tab \tab MOVLW 6
\par \tab \tab ADDWF CLKSEC,W
\par \tab \tab BTFSS STATUS,DC
\par \tab \tab RETURN
\par \tab \tab MOVWF CLKSEC
\par \tab \tab XORLW %01100000
\par \tab \tab BTFSS STATUS,Z
\par \tab \tab RETURN
\par \tab \tab CLRF CLKSEC
\par \tab 
\par Each time the seconds roll over to zero from 59, the minutes need
\par incrementing; each time they roll over to zero from 59, the hours need
\par incrementing. The hours, though, need to roll over to zero from 23. As far
\par as incrementing each of the three counters is concerned, the easiest thing
\par to do (but not the shortest) is to use three separate BCD routines - as we
\par do in TUT28. The minutes routine is the same as the seconds one, both
\par requiring a count from 0 to 59, with routines to check for 10 and 60. The
\par hours routine, though, requires slight alteration.
\par 
\par With the hours, we need to check when counts of 10 and 20 occur (+ 6
\par check), and also when 24 occurs (BCD = 00100100). This check cannot be done
\par in the same way as for the BCD 60 check. With the latter, the check is made
\par at the same time as the tens are incremented. For 24 hours, the simplest
\par test is to check on each hourly digit increment:
\par 
\par HRSCLK: INCF CLKHRS,F
\par         MOVLW 6
\par         ADDWF CLKHRS,W
\par         BTFSC STATUS,DC
\par         MOVWF CLKHRS
\par         XORLW %00100100
\par         BTFSC STATUS,Z
\par         CLRF CLKHRS
\par 
\par The activating of the decimal point, when required, is done by setting the
\par correct bit in the code once the table has been called (BCF PORTB,7), as
\par seen in the OUTPUT routine:
\par 
\par OUTPUT: ANDLW 15
\par         CALL TABLE
\par         CLRF PORTA
\par         MOVWF PORTB
\par         CALL DIGSHW
\par         MOVWF PORTA
\par         MOVF DIGIT,W
\par         XORLW 1
\par         BTFSC STATUS,Z
\par         BCF PORTB,7
\par         RETURN
\par 
\par Minutes and seconds values are dealt with in the same manner. Minutes
\par units, though, are accompanied by the decimal point bit. Seconds are
\par processed similarly, but without any additional bit setting for colons or
\par points. In Tutorial 24 we shall show how a similar result can be achieved
\par by using fewer commands. A loop plays an active role and a table is used
\par when checking the roll-over values for the time.
\par 
\par In Listing 28, when switch SA4 is not pressed (checked by BTFSS PORTA,4), a
\par value of 2 is added to effective value of DIGIT, to cause the table jumps
\par within DIGSEL to be to the minutes and seconds display routines. The
\par decimal point is still turned on after the first (lefthand) digit. Pressing
\par SA4 results in hours and minutes being shown.
\par 
\par You will now observe that the brilliance of the display is less than that
\par previously seen, due to the multiplexing. In a real clock situation, the
\par use of a high brightness display would probably be preferable.
\par 
\par \plain\f2\fs24\b Exercise 21\plain\f2\fs20 
\par 
\par 21.1. You will have noticed "ghost" images on the "off" segments for the
\par active digits in TUT27, but not in TUT28. Study TUT28's full listing and
\par amend TUT27 similarly to eliminate the "ghosts".
\par 
\par 21.2. Create a table that holds all 16 conversions for a hexadecimal count
\par (i.e. 0 to 9 and A to F) to be shown on a 4-digit common anode display.
\par Write a simple counting routine which makes use of it. What compromise
\par might you have to accept?
\par 
\par 21.3. Extend the routine from 21.2 so that it blanks the display of any
\par leading zeros (i.e. don't show 0007, but just show 7).
\par 
\par \plain\f2\fs28\b Tutorial 22\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Using intelligent LCDs
\par Initialising the LCD
\par Sending a message to the LCD
\par 
\par \plain\f2\fs24\b LISTING 29 - PROGRAM TUT29
\par \plain\f2\fs20 
\par TABLCD:\tab ADDWF PCL,F
\par \tab \tab RETLW %00110011
\par \tab \tab RETLW %00110011
\par \tab \tab RETLW %00110010
\par \tab \tab RETLW %00101100
\par \tab \tab RETLW %00000110
\par \tab \tab RETLW %00001100
\par \tab \tab RETLW %00000001
\par \tab \tab RETLW %00000010
\par 
\par MESSAG:\tab ADDWF PCL,F
\par \tab \tab RETLW 'R'
\par \tab \tab RETLW 'E'
\par \tab \tab RETLW 'A'
\par \tab \tab RETLW 'D'
\par \tab \tab RETLW ' '
\par \tab \tab RETLW 'E'
\par \tab \tab RETLW 'P'
\par \tab \tab RETLW 'E'
\par 
\par SETUP:\tab CALL PAUSIT
\par 
\par LCDSET:\tab CLRF LOOP
\par \tab \tab CLRF RSLINE
\par 
\par LCDST2:\tab MOVF LOOP,W
\par \tab \tab CALL TABLCD
\par \tab \tab CALL LCDOUT
\par \tab \tab INCF LOOP,F
\par \tab \tab BTFSS LOOP,3
\par \tab \tab GOTO LCDST2
\par \tab \tab CALL PAUSIT
\par \tab 
\par LCDMSG:\tab CLRF LOOP
\par \tab \tab BSF RSLINE,4
\par 
\par LCDMS2:\tab MOVF LOOP,W
\par \tab \tab CALL MESSAG
\par \tab \tab CALL LCDOUT
\par \tab \tab INCF LOOP,F
\par \tab \tab BTFSS LOOP,3
\par \tab \tab GOTO LCDMS2
\par NOMORE:\tab GOTO NOMORE
\par 
\par LCDOUT:\tab MOVWF STORE
\par \tab \tab MOVLW 20
\par \tab \tab MOVWF LOOPA
\par \tab 
\par DELAY:\tab DECFSZ LOOPA,F
\par \tab \tab GOTO DELAY
\par \tab \tab CALL SENDIT
\par \tab \tab CALL SENDIT
\par \tab \tab RETURN
\par 
\par SENDIT:\tab SWAPF STORE,F
\par \tab \tab MOVF STORE,W
\par \tab \tab ANDLW 15
\par \tab \tab IORWF RSLINE,W
\par \tab \tab MOVWF PORTB
\par \tab \tab BSF PORTA,5
\par \tab \tab BCF PORTA,5
\par \tab \tab RETURN
\par 
\par PAUSIT:\tab MOVLW 5
\par \tab \tab MOVWF CLKCNT
\par \tab \tab CLRF INTCON
\par \tab 
\par PAUSE:\tab BTFSS INTCON,2
\par \tab \tab GOTO PAUSE
\par \tab \tab BCF INTCON,2
\par \tab \tab DECFSZ CLKCNT,F
\par \tab \tab GOTO PAUSE
\par \tab \tab RETURN
\par 
\par 
\par Having established how 7-segment displays can be driven by the PIC, we now
\par examine how an intelligent LCD can be used to achieve not only the same
\par result, but one that has additional facilities as well. The coding required
\par is not especially complex, although minimum timing factors for some aspects
\par of sending data to an LCD have to be observed. The first requirement is to
\par show the basics of how data is output to an LCD from the PIC. We shall not
\par cover the LCD itself in any great detail - you are referred to
\par manufacturer's data sheets for more information.
\par 
\par Here, we first show how the LCD is initialised for 4-bit data transfer from
\par the PIC, using two control lines, RS and E. Line RS sets the LCD for
\par inputting either character data or control data. Line E tells the LCD to
\par act on the data output to it.
\par 
\par Disconnect the LED module and connect the LCD to the PCB as in Fig.27. The
\par equivalent circuit diagram is shown in Fig.28 (in a real circuit design
\par resistors R39 to R44 would be omitted). The LCD pinout diagram is shown in
\par Fig.29. Load TUT29.OBJ and run it. While reading these next paragraphs,
\par refer to Listing 29 as appropriate.
\par 
\par \plain\f2\fs24\b LCD CONTRAST\plain\f2\fs20 
\par 
\par The first time the LCD is used, the Contrast control VR2 should be adjusted
\par until the display is clearly visible.
\par 
\par It is appropriate to mention here that some LCDs, notably 2-line
\par 8-character types, require a negative voltage to be applied to their
\par contrast setting pin. A negative voltage can be generated using one of the
\par PIC's output lines and the circuit in Fig.30. The chosen PIC line is
\par connected to capacitor C6 (FREQ) and the wiper of VR2 (+VE) connected to
\par the contrast pin (pin 3) of the LCD. Adjustment of VR2 then sets the
\par contrast.
\par 
\par A negative voltage of about -3.5V is generated when the PIC line is toggled
\par up and down at a fairly fast rate. The commands to do this can be placed
\par anywhere within a frequently accessed part of the program. A suitable place
\par in TUT29 would be within the INTRPT loop. Assuming the toggling line is
\par PORTA RA0, the commands would be:
\par 
\par INTRPT: INCF PORTA,0
\par         BTFSS INTCON,2
\par         GOTO INTRPT
\par 
\par \plain\f2\fs24\b DELAYED START\plain\f2\fs20 
\par 
\par When the LCD is under high speed control from a device such as the PIC, it
\par is necessary to allow a minimum of 1/5th of a second between the circuit
\par being switched on and any data being sent to the LCD. So, following the
\par PIC's initialisation, the program jumps to the routine at SETUP which, via
\par sub-routine PAUSIT, creates this delay by making use of the prescaler. The
\par prescaler has been set for an INTCON,2 pulse every 1/25th of a second, so a
\par loop beginning at PAUSE is used to wait for five of these pulses to be
\par completed, i.e. 1/5th of a second. Then a series of commands is sent to set
\par the LCD into the required 4-bit mode. (There are other command routines
\par possible which achieve a similar result.) The commands are held in the
\par table TABLCD, which is accessed from the routine at label LCDSET. The first
\par command here clears the loop counter and the byte (RSLINE) which holds the
\par RS-controlling bit.
\par 
\par Bit 4 of RSLINE is used to inform the LCD what type of data is being sent
\par to it. The bit is cleared for control data, and set for character data.
\par Now, in the manner of table use which was demonstrated earlier, the control
\par commands from TABLCD are sent to the LCD via the LCDST2 routine. Next, the
\par loop counter is cleared, RSLINE bit 4 is set and used to inform the LCD
\par that the commands being sent to it are character data. Then the message
\par held in the table MESSAG is sent via the routine headed LCDMSG. The LCD
\par displays this message on its first line, starting at the left. Now, for the
\par sake this demo, the perpetual loop at NOMORE is entered and no more actions
\par occur. To replay the routine, use the Reset switch.
\par 
\par In both data sending routines, the LCD output routine is called by the
\par command CALL LCDOUT. The entire block between the start of LCDOUT and the
\par final RETURN at the end of SENDIT is responsible for sending each byte of
\par 8-bit data to the LCD as two 4-bit nibbles, to which control data is then
\par ORed to expand them to a full 8-bit byte. Nibble data is held in the LSN of
\par this byte, control data (in this instance just that for lines RSLINE and E)
\par is held in its MSN.
\par 
\par On entry into LCDOUT, the data brought in on W is copied into a temporary
\par file, STORE. Now a delay loop is entered. The LCD can only handle bytes of
\par data coming to it at a rate which allows previous data received to be
\par processed fully. Details of the delay required are stated in manufacturer's
\par data sheets. In theory, the delay depends on the type of data and command
\par being sent, but on a practical level, a fixed delay of so many PIC commands
\par can be used. In this example, LOOPA is set for 20 and then decremented
\par until zero, as performed by the instructions:
\par 
\par        MOVLW 20
\par        MOVWF LOOPA
\par DELAY: DECFSZ LOOPA,F
\par        GOTO DELAY
\par 
\par In the author's experience with many programs, this delay is satisfactory
\par for a PIC running at up to about 5MHz. Too short a delay will result in
\par erratic behaviour of the LCD, probably accompanied by erroneous display
\par results. If this occurs, the loop value should be increased. (In fact,
\par reader feedback suggests that a value of 40 or 50 might be better,
\par depending on the manufacturer of display used.)
\par 
\par Following the delay, there is a call to SENDIT. In SENDIT, the MS nibble of
\par data is retrieved from STORE with the commands:
\par 
\par SWAPF STORE,F
\par MOVF STORE,W
\par ANDLW 15
\par 
\par The first command swaps the two nibbles within STORE, the second copies
\par STORE into W, and then W is ANDed with 15 to isolate bits 0 to 3. The
\par result is ORed with the RSLINE bit and the byte is then output to the LCD
\par via PORTB. The E line is taken high and immediately low again, telling the
\par LCD to process the data on its data inputs. A return to the calling point
\par occurs and then SENDIT is again called. This time, the LSN is extracted
\par from STORE and sent to the LCD in the same way. After two RETURNs, the
\par program returns to the original calling point.
\par 
\par It is important to note that the port bits which are used in these routines
\par to control the RSLINE and E lines reflect the physical connections between
\par the PIC and the LCD as shown in Fig.28. It is permissible to use other PIC
\par port lines for this purpose, but the controlling bits of the software must
\par be changed accordingly.
\par 
\par \plain\f2\fs24\b Exercise 22
\par \plain\f2\fs20 
\par 22.1. There are two commands in the LCDOUT to SENDIT routine which, while
\par being perfectly legitimate, are actually unnecessary. What are they and why
\par are they not needed? (Think "default".)
\par 
\par 22.2. When the LCD is first initialised, it is possible (though not
\par definite) that all its character positions (cells) will show as black
\par squares. Sending the message will correct that situation for the first
\par eight cells. How could you ensure that the remaining eight cells on the top
\par line are set to "clear" blanks? There are two methods; try both.
\par 
\par 22.3. How would you now set the lower line to all blanks? (The answer is
\par also given later - don't cheat!)
\par 
\par \plain\f2\fs28\b Tutorial 23\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Coding hours, minutes and seconds for an LCD
\par Shortened clock monitoring code
\par Command SUBLW
\par Command SUBWF
\par 
\par \plain\f2\fs24\b LISTING 30 - PROGRAM TUT30
\par \plain\f2\fs20 
\par INTRPT:\tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab CALL CLKADD
\par \tab \tab GOTO INTRPT
\par 
\par CLKADD:\tab DECFSZ CLKCNT,F
\par \tab \tab RETURN
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab \tab MOVLW CLKSEC
\par \tab \tab MOVWF FSR
\par \tab \tab MOVLW 3
\par \tab \tab MOVWF LOOP
\par \tab \tab CLRF STORE1
\par 
\par ADDCLK:\tab INCF INDF,F
\par \tab \tab MOVLW 6
\par \tab \tab ADDWF INDF,W
\par \tab \tab BTFSC STATUS,DC
\par \tab \tab MOVWF INDF
\par \tab 
\par ADDCL2:\tab MOVF STORE1,W
\par \tab \tab CALL CHKVAL
\par \tab \tab MOVWF STORE2
\par \tab \tab MOVF INDF,W
\par \tab \tab SUBWF STORE2,F
\par \tab \tab BTFSC STATUS,C
\par \tab \tab GOTO CLKSHW
\par \tab \tab CLRF INDF
\par \tab \tab INCF STORE1,F
\par \tab \tab INCF FSR,F
\par \tab \tab DECFSZ LOOP,F
\par \tab \tab GOTO ADDCLK
\par \tab 
\par CLKSHW:\tab MOVLW %11000000
\par \tab \tab CALL LCDLIN
\par \tab \tab MOVF CLKHRS,W
\par \tab \tab CALL LCDFRM
\par \tab \tab MOVLW ':'
\par \tab \tab CALL LCDOUT
\par \tab \tab MOVF CLKMIN,W
\par \tab \tab CALL LCDFRM
\par \tab \tab MOVLW '.'
\par \tab \tab CALL LCDOUT
\par \tab \tab MOVF CLKSEC,W
\par \tab 
\par LCDFRM:\tab MOVWF STORE2
\par \tab \tab SWAPF STORE2,W
\par \tab \tab ANDLW 15
\par \tab \tab IORLW 48
\par \tab \tab CALL LCDOUT
\par \tab \tab MOVF STORE2,W
\par \tab \tab ANDLW 15
\par \tab \tab IORLW 48
\par \tab \tab CALL LCDOUT
\par \tab \tab RETURN
\par \tab 
\par LCDLIN:\tab BCF RSLINE,4
\par \tab \tab CALL LCDOUT
\par \tab \tab BSF RSLINE,4
\par \tab \tab RETURN
\par 
\par Now we know how the LCD can have data written to it, we will show how the
\par method can be extended in order to display 24-hour clock data. Load
\par TUT30.OBJ and run it, glancing at the display from time to time while you
\par read on here.
\par 
\par \plain\f2\fs24\b COMMANDS SUBLW AND SUBWF
\par \plain\f2\fs20 
\par Rather late on, perhaps, in the program we are about to display we
\par encounter the first use of subtraction. The PIC16x84 has two subtraction
\par commands, SUBLW (Subtract W from Literal) and SUBWF (Subtract W from File).
\par The latter command is used with either the F or the W suffix, e.g. SUBWF
\par (FILE),F and SUBWF (FILE),W. One might reasonably have expected that SUBLW
\par would actually mean Subtract Literal from W. This is not the case, the
\par subtraction is that of W from the Literal. Consequently, unless you keep
\par your wits about you, this is a command that you could quite easily use
\par incorrectly.
\par 
\par In the following code, the value in the file named DEMO is subtracted from
\par 30 and the result put back into DEMO (the first two lines are just to put
\par an initial value into DEMO):
\par 
\par MOVLW 20
\par MOVWF DEMO
\par MOVF DEMO,W
\par SUBLW 30
\par MOVWF DEMO
\par 
\par In this case, the answer is 10 (30 - 20), even though instinctively we
\par might have expected 30 to be subtracted from 20. In this next example, to
\par illustrate SUBWF, again it is the value already in W which is subtracted
\par from the value in file DEMO, the result being returned to DEMO. This is
\par more logical. (Once more the first two commands are just to put an initial
\par value into DEMO.)
\par 
\par MOVLW 20
\par MOVWF DEMO
\par MOVLW 5
\par SUBWF DEMO,F
\par 
\par The answer put back into DEMO is, of course, 15 (20 - 5).
\par 
\par In these two examples, the value subtracted is less than the value from
\par which it is being subtract. What happens if the opposite is true? For a
\par start, if the value subtracted is greater than the value from which it is
\par being subtracted, the byte simply "rolls-over". We have already seen that
\par decrementing a value of zero results in an answer of 255. Decrementing, of
\par course, is simply a subtraction of 1 from a number and we could, therefore,
\par consider the 0 - 1 situation as being expressed (256 + 0) - 1 = 255.
\par 
\par What we have done by using the addition of 256, is to "borrow" the 256 in
\par order to achieve the correct 8-bit result. The same roll-over situation
\par applies to subtraction of numbers greater than 1. Thus subtracting 20 from
\par 10 produces an answer of 246 (256 + 10 - 20 = 246). We are quite used to
\par "borrowing" in normal arithmetic, so the concept should be familiar to you,
\par although we express the result of subtracting 20 from 10 as equalling -10
\par 
\par The difference with PICs (and other digital devices) is that we cannot
\par produce a negative answer as such. What we can do, however, is to use a
\par flag to indicate that a borrow or negative answer situation has occurred.
\par With the PIC, the Carry bit is used for this purpose. In a subtraction
\par operation we simply test the Carry bit to establish whether or not there
\par has been a borrow. This, though, is where another "inverted" concept has to
\par be applied to SUB commands. Whereas with the ADD commands the Carry bit is
\par Set if a carry result occurs, with the SUB commands the Carry bit is
\par Cleared if a borrow occurs, and it is Set if a borrow does NOT occur. You
\par could, perhaps, regard the Carry bit as being the bit which is available to
\par be "borrowed" for the subtraction, hence it remaining set if a borrow is
\par not needed, and cleared if it is.
\par 
\par The following are examples of routines which test the Carry bit in a
\par subtraction operation:
\par 
\par MOVLW 30
\par MOVWF DEMO
\par MOVF DEMO,W
\par SUBLW 20
\par MOVWF DEMO
\par BTFSS STATUS,C
\par INCF STORE,F
\par RETURN
\par 
\par The above example will cause STORE to be incremented since a borrow will
\par occur when 30 is subtracted from 20. The next example, 30 - 20, does not
\par result in a borrow, so STORE remains at its previous value:
\par 
\par MOVLW 20
\par MOVWF DEMO
\par MOVF DEMO,W
\par SUBLW 30
\par MOVWF DEMO
\par BTFSS STATUS,C
\par INCF STORE,F
\par RETURN
\par 
\par You will see the use of SUBWF and the subsequent testing of the Carry bit
\par for the occurrence of a borrow in TUT30.
\par 
\par \plain\f2\fs24\b TIME OUT TO LCD
\par \plain\f2\fs20 
\par As with 7-segment LED clock counting routines, with the LCD program the
\par numerical values are held as BCD counts and each digit is, of course,
\par between 0 and 9 decimal. To the LCD, though, values 0 to 9 represent the
\par characters which it holds at its character register addresses 0 to 9, which
\par is not the same thing. The LCD's characters which "look like" our 0 to 9,
\par are held at its addresses 48 to 57, in other words, they are ASCII
\par characters.
\par 
\par With the 7-segment display, we had to use a table to convert from decimal
\par to a code that it would show meaningfully. With the LCD, the conversion is
\par much easier, we simply add the difference between the decimal value and its
\par ASCII value, i.e. we increase the value by 48. Conveniently, 48 decimal has
\par a binary value of 00110000. The BCD values for decimal 0 to 9 lie between
\par binary 00000000 and 00001001. All we need to do, therefore, is to set bits
\par 4 and 5 of the time digit value in order to increase it by 48, i.e. decimal
\par 9 becomes binary 00111001, which equals 57, the ASCII code for numeral 9.
\par 
\par The easiest way to set bits 4 and 5 is to either add 48 to the digit's
\par value, or to OR 48 with it. In other words, to use either ADDLW 48 or IORLW
\par 48 as the command. In this situation they both have the same effect. To use
\par BSF would require two commands instead of just one. In the following
\par conversion example, the additive technique is used (IOR is used in the
\par program):
\par 
\par SWAPF STORE2,W  ; get tens
\par ANDLW 15
\par ADDLW 48
\par CALL LCDOUT
\par MOVF STORE2,W   ; get units
\par ANDLW 15
\par ADDLW 48
\par CALL LCDOUT
\par MOVLW ':'       ; insert colon
\par CALL LCDOUT
\par 
\par The SWAPF STORE2,W swaps the nibbles of the value held in STORE2 and holds
\par the result in W, putting the tens into the LSN position. Command ANDLW 15
\par isolates that nibble, zeroing the MSN. Now ADDLW 48 converts the value to
\par the ASCII character, and LCDOUT is called, which sends the data to the LCD.
\par (Have you noticed the similarity to the nibble extraction used for
\par 7-segment displays?)
\par 
\par Next, MOVF STORE2,W brings the entire byte into W, ANDLW 15 isolates the
\par nibble which is in the correct LSN position. Again ANDLW 48 and CALL LCDOUT
\par are performed. Following that, the ASCII value for a colon (58) is sent to
\par the LCD, using the single quotes method previously seen in tables.
\par 
\par \plain\f2\fs24\b CLOCKING ON\plain\f2\fs20 
\par 
\par Hours, minutes and seconds values are dealt with similarly, although
\par minutes are followed by the decimal point (ASCII 46). Seconds are not
\par followed by any character, although they could have a space character
\par (ASCII 32) sent after the units. The sequence of events, from individually
\par incrementing time to outputting the data to the LCD is shown in Listing 30.
\par Now compare this listing with that for outputting the time data to the
\par 7-segment displays (TUT28). Look especially at the clock count section
\par (from CLKADD to end of ADDCL2). The second version, of which the main part
\par is shown here, is considerably more compact.
\par 
\par After initialisation and general set-up, the program enters the INTRPT
\par routine. At each 1/25th second time-out, CLKADD is called and the CLKCNT
\par counter decremented, as we saw earlier. Only if CLKCNT is zero is the next
\par routine entered. After resetting CLKCNT, the address of CLKSEC is set in
\par the indirect address register FSR, a loop (LOOP) is set for three
\par operations and STORE1 is cleared for use as an up-counter. In the three
\par steps round the loop, CLKSEC is dealt with first, then CLKMIN and then
\par CLKHRS.
\par 
\par First time round the loop, at ADDCLK the first byte to be incremented is,
\par of course, CLKSEC. This is then checked for a units value greater than nine
\par and action taken accordingly. Next, the value within STORE1 is copied into
\par W and a table (CHKVAL - see full listing) is called, returning with the
\par maximum permitted value for the byte being processed, storing it in STORE2.
\par The value of the time byte (CLKSEC at this moment) is then copied into W,
\par which is then subtracted from STORE2. If the Carry flag is set, then STORE2
\par is greater than CLKSEC (there is no borrow) and an exit is made from the
\par loop, no further action being needed, and a jump is made to the display
\par routine (CLKSHW).
\par 
\par If CLKSEC is greater than STORE2 a borrow occurs, thus CLKSEC is cleared,
\par counter STORE1 is incremented for the sake of the table jump address, and
\par the FSR address is incremented (to point now to CLKMIN). The loop counter
\par (LOOP) is decremented and, if it is not zero, the loop is repeated, this
\par time incrementing and checking CLKMIN in the same way as CLKSEC was dealt
\par with. If CLKMIN is reset, the loop is repeated for the third occasion, this
\par time for CLKHRS, after which the CLKSHW display routine is entered
\par directly.
\par 
\par Two sub-routines are used with CLKSHW, to save repetition of too many
\par commands. The routines are LCDLIN and LCDFRM. The former is responsible for
\par setting the starting display cell position on the LCD. Since in a larger
\par program this position could change frequently, it is worthwhile having a
\par generalised routine for this purpose. In this case, we want the time to be
\par shown at the start of the second LCD line, so the value %11000000 (the
\par address of line 2 cell 0) is moved into W and LCDLIN called. All LCDLIN
\par does is set the RSLINE flag for command mode, call LCDOUT, and reset the
\par RSLINE flag to character mode.
\par 
\par Next, the value of CLKHRS is moved into W and LCDFRM called. This routine
\par does the swapping, ANDing and ORing necessary for numerical conversion to
\par the ASCII value. After this, the colon is sent directly to LCDOUT. Similar
\par commands are then given with regard to CLKMIN and CLKSEC. Notice that after
\par the MOVF CLKSEC,W command, LCDFRM is entered directly by default. The
\par program then returns to the INTRPT routine to begin again.
\par 
\par \plain\f2\fs24\b Exercise 23\plain\f2\fs20 
\par 
\par 23.1. Extend the program so that the clock also keeps track of months and
\par years.
\par 
\par \plain\f2\fs28\b Tutorial 24\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Adding time-setting switches
\par 
\par \plain\f2\fs24\b LISTING 31 - PROGRAM TUT31\plain\f2\fs20 
\par 
\par INTRPT:\tab BTFSS INTCON,2
\par \tab \tab GOTO INTRPT
\par \tab \tab BCF INTCON,2
\par \tab \tab CALL CLKADD
\par \tab \tab GOTO INTRPT\tab 
\par 
\par CLKADD:\tab DECFSZ CLKCNT,F
\par \tab \tab RETURN
\par \tab \tab MOVLW 25
\par \tab \tab MOVWF CLKCNT
\par \tab \tab CALL GETKEY
\par \tab \tab INCF HLFSEC,F
\par \tab \tab BTFSC HLFSEC,0
\par \tab \tab CALL CLKIT
\par \tab \tab RETURN
\par 
\par (Section from CLKIT to end of LCDLIN omitted)
\par 
\par GETKEY:\tab BTFSS PORTA,3
\par \tab \tab GOTO CHKSW2
\par \tab \tab BSF EVENT,0
\par \tab \tab MOVLW CLKHRS
\par \tab \tab GOTO TIMSET
\par 
\par CHKSW2:\tab BTFSS PORTA,2
\par \tab \tab RETURN
\par \tab \tab CLRF EVENT
\par \tab \tab MOVLW CLKMIN
\par \tab 
\par TIMSET:\tab MOVWF FSR
\par \tab \tab BTFSC PORTA,0
\par \tab \tab GOTO SUBTIM
\par \tab \tab BTFSS PORTA,1
\par \tab \tab RETURN
\par 
\par ADDTIM:\tab INCF INDF,F
\par \tab \tab MOVLW 6
\par \tab \tab ADDWF INDF,W
\par \tab \tab BTFSC STATUS,DC
\par \tab \tab MOVWF INDF
\par \tab \tab INCF EVENT,W
\par \tab \tab CALL CHKVAL
\par \tab \tab MOVWF STORE2
\par \tab \tab MOVF INDF,W
\par \tab \tab SUBWF STORE2,F
\par \tab \tab BTFSS STATUS,C
\par \tab \tab CLRF INDF
\par \tab \tab GOTO CLKSHW
\par 
\par SUBTIM:\tab MOVLW 1
\par \tab \tab SUBWF INDF,F
\par \tab \tab BTFSS STATUS,C
\par \tab \tab GOTO SUBSET
\par \tab \tab BTFSC STATUS,DC
\par \tab \tab GOTO ENDSUB
\par \tab \tab MOVF INDF,W
\par \tab \tab ANDLW %11110000
\par \tab \tab IORLW 9
\par \tab \tab MOVWF INDF
\par \tab \tab GOTO ENDSUB
\par 
\par SUBSET:\tab INCF EVENT,W
\par \tab \tab CALL CHKVAL
\par \tab \tab MOVWF INDF
\par 
\par ENDSUB:\tab GOTO CLKSHW
\par 
\par 
\par The clock program of TUT30 that is now being run is perfectly usable as a
\par real-time clock, as is the 7-segment version. They both have a major
\par problem though, the programs have to be started (reset) at exactly midnight
\par for the time shown to be accurate. What we need is the ability to set the
\par current time via switches, as with most other time-keepers. Here we will
\par show how switched time-setting can be programmed into the LCD version.
\par 
\par We have already looked quite heavily at the use of switches in earlier
\par sections. It is not hard to implement switched time-setting routines, but
\par it takes quite a few commands (as Listing 31 shows), especially as we are
\par allowing you a luxury: the ability to count upwards or downwards on both
\par minutes and hours. Many clocks do not allow this, and it can be a right
\par pain if you overshoot the time you want! We also allow a fifth switch to
\par reset the seconds.
\par 
\par First, though, attention must be paid to the rate at which the digits are
\par changed by the switches. We could easily insert a switch checking routine
\par either on each 1/25th count, or on each second. However, the first is too
\par fast for convenience, and the other too slow. A better rate is on every
\par half-second. This can be arranged by halving the prescaler rate, setting it
\par for a ratio of 1:64 instead of 1:128. Thus, in the initialisation, instead
\par of MOVLW %00000110 and MOVWF OPTION, we use:
\par 
\par MOVLW %00000101
\par MOVWF OPTION
\par 
\par Counter CLKCNT is still set for 25 but we use an additional counter HLFSEC
\par for half seconds, so that although the switches are sampled every half
\par second, the seconds themselves are still incremented correctly. Referring
\par to Listing 31, you will see the command CALL GETKEY, which is then followed
\par by INCF HLFSEC,F. Only if bit 0 of HLFSEC is 1 will the CLKADD routine be
\par entered. Imagine now that the switches on PORTA are designated as follows:
\par 
\par SA4 = seconds reset
\par SA3 = hours
\par SA2 = minutes
\par SA1 = plus (+)
\par SA0 = minus (-)
\par 
\par At GETKEY, if switch SA3 is pressed (hours), EVENT bit 0 is set to 1. This
\par file value will be used when accessing the CHKVAL table for the maximum
\par roll-over value for hours or minutes. Now the address of CLKHRS is moved
\par into W and TIMSET is called. At this routine, the plus (+) and minus (-)
\par keys are read for their status, and the addition (ADDTIM) or subtraction
\par (SUBTIM) routine is jumped to and processed. In these routines, not only
\par have the units to be checked for values greater than nine, but the overall
\par BCD value has to be checked for greater than 23 (hours) and greater than 59
\par (minutes).
\par 
\par In the addition routine, the excess value is checked for, and the value is
\par reset to zero if it is exceeded. In the subtraction routine, zero is
\par checked for, in which case the maximum allowed value is moved into the byte
\par as the reset value. In both instances, the value within EVENT is moved into
\par W and table CHKVAL is called for the maximum value. All the commands
\par involved in these routines should by now be familiar to you without further
\par explanation. Load TUT31.OBJ, run it and experiment with setting the time.
\par 
\par \plain\f2\fs24\b Exercise 24\plain\f2\fs20 
\par 
\par 24.1. Using one of the switch paths not yet used, create a routine that
\par will also show how many hours, minutes and seconds there are until midnight
\par (00:00.00 hours or 24:00.00 if you find it easier) when you press a switch,
\par clearing the answer when the switch is released.
\par 
\par \plain\f2\fs28\b Tutorial 25\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Dual use of port pins for input and output
\par Configuring program for alarm system monitoring
\par 
\par \plain\f2\fs24\b LISTING 32A - PROGRAM TUT32
\par \plain\f2\fs20 
\par CHKZON: clrf PORTB      ;clear val in PORTB
\par         PAGE1
\par         movlw %11001111 ;change PORTB data directions
\par         movwf TRISB     ;Port RB5-RB6 as output, others as input
\par         PAGE0
\par         clrf PORTB      ;clear PORTB again
\par         nop             ;1 command delay for PORT to settle
\par         movf PORTB,W    ;get PORTB vals to check for zone entry etc
\par         movwf ZONCHK    ;store it
\par         PAGE1
\par         clrf TRISB      ;reset Port RB0-RB7 as output
\par         PAGE0
\par         btfss ZONCHK,7  ;is alarm-on switch on (=1)?
\par         goto CHKRST     ;no
\par         comf ZONCHK,W   ;yes, invert byte for ease of zone checking
\par         andlw %00001111 ;limit to first 4 bits
\par         xorwf ZONE,W    ;has zone been triggered before?
\par         btfsc STATUS,Z
\par         return          ;yes
\par         iorwf ZONE,F    ;no
\par         movf BELLON,W   ;get bell status
\par         btfss STATUS,Z  ;has bell already been set off?
\par         goto ZONSHW     ;yes
\par         bsf BELLON,1    ;turn on buzzer flag
\par         bsf BELLON,0    ;turn on bell flag
\par         movlw 3
\par         movwf BELL2     ;set bell countdown MSB
\par         bsf BELL1,6     ;set bell countdown LSB (approx 10 min)
\par 
\par \plain\f2\fs24\b LISTING 32B
\par \plain\f2\fs20 
\par CHKBEL: btfss BELLON,0  ;is the bell on?
\par         goto CLKSHW     ;no
\par         decfsz BELL1,F  ;yes, dec bell counter LSB, is it zero?
\par         goto CLKSHW     ;no
\par         decfsz BELL2,F  ;yes, dec bell counter MSB, is it zero?
\par         goto CLKSHW     ;no
\par         bcf BELLON,0    ;yes, clear bell flag (leave buzzer on)
\par 
\par \plain\f2\fs24\b LISTING 32C
\par \plain\f2\fs20 
\par CHKRST: clrf ZONE       ;turn off alarm routine - 1st clear zone flags
\par         clrf BELLON     ;clear bell-on flag
\par         clrf BELL1      ;reset bell counter 1
\par         clrf BELL2      ;reset bell counter 2
\par         BCF ALMSET,0    ;clear alarm set flag
\par         movlw %10000100 ;clear alarm set-off time etc
\par         call LCDLIN     ;set address for line 1 (address 8)
\par         movlw 4         ;set loop for 4
\par         movwf LOOP
\par RSTALM: movf LOOP,W     ;show ZONE---- message
\par         call MESSAG
\par         call LCDOUT
\par         incf LOOP,F
\par         btfss LOOP,3
\par         goto RSTALM
\par RSTAL2: movlw ' '       ;clear rest of LCD line to blanks
\par         call LCDOUT
\par         incf LOOP,F
\par         btfss LOOP,4
\par         goto RSTAL2
\par         return
\par 
\par We turn now to extending the program of TUT31 for use as an alarm system
\par monitor in which the time that the alarm is first triggered and which of
\par four zones have been entered are shown on the LCD screen. The equivalent
\par circuit diagram is shown in Fig.33.
\par 
\par In this operation, PORTA and PORTB are used as inputs in some routines, as
\par outputs in others. Referring to TUT32.ASM, after the usual initialisation
\par routines, the only change at the beginning of the program is for the
\par message table to be changed to ZONE - - - -, in which the number of any
\par triggered zone will replace the appropriate dash, from 1 to 4. The next
\par change is the addition of CALL CHKZON (see Listing 32A) following the CALL
\par GETKEY command in the CLKADD routine. The routine at CHKZON checks each of
\par the four alarm zones (RB0 to RB3) to see if any have been entered:
\par 
\par Notice in particular the initialisation of the bell-on countdown. This
\par involves two bytes, BELL1 and BELL2 which are set for a countdown period of
\par approximately 10 minutes, after which the exterior bell is switched off (in
\par another routine). It remains off and cannot be retriggered until after the
\par system has been reset. The program has been written this way to save
\par potential annoyance to neighbours!
\par 
\par Following the ADDCLK routine (in the full program) is CHKBEL (see Listing
\par 32B) which, if the alarm bell has already been triggered by a zone entry,
\par is checked to see whether the bell's timed countdown has reached zero. If
\par it has, the BELLON flag is cleared, signalling that the external bell
\par should be turned off. The internal buzzer remains switched on until the
\par system is manually reset.
\par 
\par If the alarm has just been triggered, the routine ALMSHW is entered
\par immediately following CHKZON (see full program). Here, PORTB is set for all
\par pins as outputs and the current time (the time at which the alarm has first
\par been triggered) is output to the LCD. Note the commands which route the
\par first character to cell 8 on line 1. This routine is only used at this
\par instant and the time remains showing until the alarm is reset.
\par 
\par Following ALMSHW, is routine ZONSHW. This shows the status of all four
\par zones and is entered when the status of any zone changes. Routine CHKRST
\par (see Listing 32C) is performed once every second and checks to see if the
\par alarm is being reset via switch SB37 (in practice, this would be a
\par keyswitch combined with a switch in the bell and buzzer positive power
\par line). If SB37 is off (up), the bell and buzzer are turned off, and the LCD
\par messages of the zone entered and the time of the alarm being triggered are
\par cleared.
\par 
\par Load and run TUT32.OBJ. Referring to the equivalent circuit diagram for the
\par alarm system mock-up in Fig.33, experiment with the switches, setting the
\par alarm on with SB37, entering zones (SB33 to SB30), etc. Do not use any
\par other PORTB switches.
\par 
\par You can still set the time via PORTA switches, as in TUT31, although in a
\par real system, the additional bell (AWD) switch seen in Fig.33 would need to
\par be switched off, otherwise using the switches on pins RA0 and RA1 would
\par activate the bell and buzzer.
\par 
\par \plain\f2\fs24\b Exercise 25\plain\f2\fs20 
\par 
\par 25.1. Modify the program of TUT32 so that five zones are monitored and
\par their status displayed on the screen. Make one of them a normally-open
\par detector (e.g. as would be found in a pressure pad). Why cannot you use pin
\par RB5 as the fifth monitoring line while the LCD is connected as shown, even
\par though RB0 to RB4 are used jointly?
\par 
\par \plain\f2\fs28\b Tutorial 26\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Writing and reading EEPROM file data
\par Register EECON1
\par Register EECON2
\par Register EEDATA
\par Register EEADR
\par 
\par \plain\f2\fs24\b LISTING 33A - PROGRAM TUT33
\par \plain\f2\fs20 
\par SETPRM: movwf EEADR     ;Copy W into EEADR to set eeprom address
\par         PAGE1
\par         bsf EECON1,WREN ;enable write flag
\par         PAGE0
\par         movf STORE1,W   ;get data value from STORE1 and hold in W
\par         movwf EEDATA    ;copy W into eeprom data byte register
\par 
\par MANUAL: PAGE1           ;these next 12 lines are according to
\par         movlw $55       ;Microchip manual dictated factors
\par         movwf EECON2    ;they cause the action required by
\par         movlw $AA       ;by the eeprom to store the data in EEDATA
\par         movwf EECON2    ;at the address held by EEADR.
\par         bsf EECON1,WR   ;set the ``perform write'' flag
\par 
\par CHKWRT: btfss EECON1,4  ;wait until bit 4 of EECON1 is set
\par         goto CHKWRT
\par         bcf EECON1,WREN ;disable write
\par         bcf EECON1,4    ;clear bit 4 of EECON1
\par         PAGE0
\par         bcf INTCON,6    ;clear bit 6 of INTCON
\par         return          ;and return
\par 
\par \plain\f2\fs24\b LISTING 33B
\par \plain\f2\fs20 
\par PRMGET: movwf EEADR     ;copy W into EEADR to set eeprom address
\par         PAGE1           ;
\par         BSF EECON1,RD   ;enable read flag
\par         PAGE0
\par         movf EEDATA,W   ;read eeprom data now in EEDATA into W
\par         return          ;and return
\par 
\par We have already found how convenient it is to be able to repeatedly change
\par the program data within the PIC16x84. The demos and your experiments would
\par simply not have been practical had we been using a microcontroller which
\par required erasing by ultra-violet light each time a new program had to be
\par loaded into it. Now we come to another great advantage of the PIC16x84, the
\par presence of an EEPROM data memory which can be written to and read from
\par whenever we want, and which will not lose the data when the power is
\par switched off.
\par 
\par We shall now show the commands needed for EEPROM data memory read/write
\par operation and then embody them into the alarm program so that alarm trigger
\par times can be stored and retrieved even if the power to the alarm has been
\par switched off after the alarm has been triggered. The full program is on
\par your disk as TUT33, its main contents are shown in Listings 33A and 33B.
\par Note that this program cannot be run as it stands.
\par 
\par In some respects, use of the EEPROM read/write facility is similar to that
\par used in indirect addressing, a special register (EEADR) is loaded with the
\par address within the EEPROM at which the data is to be stored or retrieved.
\par This register can be likened to FSR. The data which has to be written to
\par the EEADR register is loaded into register EEDATA (equivalent to INDF). On
\par retrieving data from the EEPROM, register EEADR is loaded with the address
\par from which the data is to come, and then the PIC copies the data from that
\par position into EEDATA. Prior to writing data to the EEPROM, a write-enable
\par flag has to be set in register EECON1. Another flag is set in EECON1 when
\par data is to be read from the EEPROM.
\par 
\par To transfer data from EEDATA to the EEPROM file pointed to by EEADR, an
\par obligatory routine as specified in the PIC Data Book has to be performed.
\par This routine initialises operations built into the PIC16x84 and which last
\par for a predetermined time. A flag (EECON1,4) is set by the PIC when these
\par operations have occurred and its setting has to be waited for before
\par further program commands can be performed. Failure to wait for the flag
\par setting can disrupt the correct storage of the data.
\par 
\par An example of how the writing routine is used is shown in Listing 33A.
\par Prior to entry into the routine at SETPRM, the data to be written is
\par temporarily placed in file STORE (or any name you like). Then the EEPROM
\par address at which the data is to be stored is moved into W and the call to
\par SETPRM is issued. On entry to SETPRM, the contents of W are copied into
\par EEADR, and then, via PAGE1, the command BSF EECON1,WREN is given, setting
\par the EEPROM into write-enable mode, after which follows a reset to PAGE0.
\par Data is then copied from STORE into W and then into EEDATA.
\par 
\par Now the routine specified in the PIC Data Book is started at label MANUAL.
\par The 12 lines of this routine, from PAGE1 down to BCF INTCON,6 should be
\par followed parrot-fashion in any other EEPROM-writing program. The final
\par RETURN command could be replaced by a GOTO, or by the program immediately
\par following on into another routine.
\par 
\par Reading data from the EEPROM is very simple, as Listing 33B shows. The
\par routine is entered at PRMGET with the EEPROM file address held in W. This
\par is copied into EEADR then, via PAGE1, the enable read flag is set (BSF
\par EECON1,RD) and PAGE0 reset. The data required is immediately available to
\par be copied into W by the command MOVF EEDATA,W.
\par 
\par \plain\f2\fs24\b Exercise 26
\par \plain\f2\fs20 
\par There is no exercise for this Tutorial.
\par 
\par \plain\f2\fs28\b Tutorial 27\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Integrating writing and reading EEPROM file data into TUT32 alarm system
\par 
\par \plain\f2\fs24\b LISTING 34 - PROGRAM TUT34
\par \plain\f2\fs20 
\par RSTPRM:\tab BTFSS RSTKEY,0\tab ; has alarm been triggered?
\par \tab \tab RETURN            ; no
\par \tab \tab BTFSS ZONCHK,4\tab ; is reset switch pressed
\par \tab       RETURN            ; no
\par \tab       BTFSC ZONCHK,7    ; is alarm-on switch off (= 0)?
\par \tab       RETURN            ; no
\par \tab \tab CLRF LOOP\tab       ; yes
\par \tab \tab CLRF STORE1
\par 
\par RSTPR2:\tab MOVF LOOP,W
\par \tab \tab CALL SETPRM
\par \tab \tab INCF LOOP,F
\par \tab \tab BTFSS LOOP,3
\par \tab \tab GOTO RSTPR2
\par \tab \tab CLRF RSTKEY
\par \tab \tab INCF RSTCNT,F
\par \tab \tab RETURN
\par 
\par Program TUT34.ASM shows the way in which the read/write routines have been
\par used in the alarm system. The writing routine is accessed from CHKZON and
\par is headed STORIT. Reading back the data is called up by pressing switch SB6
\par which calls up the RECALL routine. Both routines are too long to be shown
\par here. There is nothing unusual about them which needs to be explained.
\par 
\par Load TUT34.OBJ and run it. The equivalent circuit diagram of the system is
\par shown earlier in Fig.33. Experiment with the switches as you did with
\par TUT32, but also experiment with viewing the stored EEPROM entry time data
\par (SB6), and with resetting it (SB4).
\par 
\par When first studying TUT34.ASM, note the number of variables that are
\par specifically reset prior to the start of the program. This ensures that
\par these counters and flags always start at zero. They could, otherwise, take
\par up any value at switch on or when the reset switch (S2) is used. It is not
\par necessary to do this for variables whose values are "moved" into them in
\par some way while the program is running.
\par 
\par Of programming development interest is the way in which some aspects of the
\par program have their results temporarily output to the LCD. The bell-off
\par countdown, the alarm-on flag and an EEPROM reset/counter all have their
\par results shown on the righthand side of LCD line 2. The author wanted to
\par ensure that what he THOUGHT should be done by the routines in question,
\par WAS being done. These additional commands have been left in to show you how
\par monitoring of program sections can be usefully done using a display of some
\par sort.
\par 
\par It was especially important to make sure that the EEPROM was only being
\par reset ONCE when its reset switch was pressed. There is a finite limit to
\par the number of times the PIC's EEPROM data memory can be written to (around
\par one million times). If a programming error existed, it would be possible
\par for the EEPROM to be written to by the reset routine many times during the
\par running of the program. This would wastefully use up the EEPROM memory
\par cells' life-time. Whenever writing routines which send data to the EEPROM
\par data memory, ALWAYS check that the routine is being used only when it
\par should be! Never trust your judgement that what you have written in a
\par situation such as this is going to be performed in the way that you had
\par intended. Any programmer can, and does, make mistakes.
\par 
\par As an exercise, once you have studied TUT34.ASM, find out which aspects are
\par used purely as a programming check, and delete them. Ensure that the LCD
\par screen shows blanks where the checking values had previously been shown.
\par 
\par The EEPROM reset routine is accessed from the CHKZON routine (CALL RSTPRM).
\par EEPROM data storage and retrieval of the alarm set-off time are also
\par accessed during CHKZON (CALL STORIT and CALL RECALL).
\par 
\par \plain\f2\fs24\b CONDITIONAL RESET\plain\f2\fs20 
\par 
\par Studying the EEPROM reset routine, note how three conditions have to be met
\par before the resetting action occurs. The program extract is shown in Listing
\par 34.
\par 
\par Before EEPROM resetting is allowed to happen, the alarm has to have been
\par triggered by one or more of SB30 to SB33 (BTFSS RSTKEY,0), the EEPROM reset
\par switch (SB4) has to be pressed (BTFSS ZONCHK,4), and the Alarm-on switch
\par (SB37) has to be off (BTFSC ZONCHK,7). If all these conditions are true, a
\par loop is entered in which the eight used bytes of the EEPROM (addresses 0 to
\par 7) are programmed with zero).
\par 
\par At the end of the loop, RSTKEY is reset to zero so that the three
\par conditions cannot be met until the alarm has been set on again and a zone
\par entry has occurred. So that the author "knew" that his logic was correct, a
\par counter is incremented each time an EEPROM reset occurs (INCF RSTCNT,F).
\par This counter's value is displayed in the far right cell of LCD line 2
\par (during the CLKSHW routine).
\par 
\par Another trick was used by the author so that the correctness of the data
\par storage in the EEPROM could be speedily checked. The time at which a zone
\par is entered is normally stored just as hours and minutes. To have included
\par seconds as well would not only be irrelevant in a real life situation, but
\par would also mean that there would be insufficient LCD cells available to
\par show the recalled data in a meaningful manner. During program development,
\par to avoid having to wait for changes of minutes before triggering a zone
\par entry via the switches, the author put in two commands which would place
\par the seconds value in the hours byte. It was then possible to "enter" zones
\par at intervals only seconds apart, and then to see that the data had been
\par correctly stored in the allocated EEPROM bytes. Those commands are at the
\par entry to STORIT:
\par 
\par STORIT: MOVF CLKSEC,W
\par .       MOVWF CLKHRS
\par 
\par Both commands may be deleted, but the label STORIT must remain.
\par 
\par Observe in the STORIT routine how the use of three commands extracts the
\par latest zone to be entered so that only its time is stored in the EEPROM:
\par 
\par MOVF PRVZON,W  ;get prev zones triggered
\par XORWF ZONE,W   ;XOR with current zones
\par MOVWF STORE2   ;store answer
\par 
\par Failure to take this action would result in the current time being stored
\par in zone locations other than that (those) required.
\par 
\par Variable PRVZON holds the record of the zones previously triggered. It is
\par updated once the current zone data has been recorded:
\par 
\par STOR4:  MOVF ZONE,W
\par         MOVWF PRVZON
\par         RETURN
\par 
\par \plain\f2\fs24\b Exercise 27\plain\f2\fs20 
\par 
\par 27.1. Incorporate a fifth zone into this program, allowing its data to be
\par recorded on the EEPROM and displayed on the screen. Hint: if you lose the
\par seconds resetting option, line RA4 becomes free for the fifth zone. Then
\par the EEPROM playback display could alternate at a slow rate between
\par different zone trigger details.
\par 
\par 27.2. You will see that many commands are used in the STORIT and RECALL
\par routines. Each byte is dealt with separately, but a loop could be written
\par to perform the same actions in fewer commands. Rewrite each of these
\par routines as a loop!
\par 
\par \plain\f2\fs28\b Tutorial 28\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPT EXAMINED
\par \plain\f2\fs20 
\par Interrupts
\par 
\par \plain\f2\fs24\b LISTING 35 - PROGRAM TUT35
\par \plain\f2\fs20 
\par \tab \tab .ORG $0004
\par \tab \tab GOTO INTRPT
\par \tab \tab .ORG $0005\tab \tab 
\par 
\par \tab \tab CLRF PORTA
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISA
\par \tab \tab CLRF TRISB
\par \tab \tab MOVLW %00000111
\par \tab \tab MOVWF OPTION
\par \tab \tab PAGE0
\par 
\par \tab \tab MOVLW %10100000
\par \tab \tab MOVWF INTCON
\par START:\tab NOP
\par \tab \tab GOTO START
\par 
\par TEST:\tab \tab INCF PORTB,F
\par 
\par INTRPT:\tab INCF PORTA,F
\par \tab \tab BCF INTCON,2
\par \tab \tab RETFIE
\par 
\par From here on, none of the commands examined directly relate to extending or
\par modifying any of the foregoing programs. You do not need to disconnect the
\par LCD for these programs, although it will probably continue to show the last
\par display.
\par 
\par \plain\f2\fs24\b INTERRUPTS\plain\f2\fs20 
\par 
\par Early on in this article, mention was made of Interrupts, saying they would
\par be examined later. That "later" has arrived!
\par 
\par An Interrupt, as the term implies, literally is an "interrupt" to the
\par program, causing it to stop what it is currently doing, and perform another
\par action or set of actions, returning to where it left off when the interrupt
\par occurred. Interrupts can be set to occur from several sources, of which two
\par seem the most likely ones to be required: externally from another piece of
\par equipment, such as a switch or from a trigger pulse generated by another
\par electronic circuit; internally, at the end of a time-out period generated
\par by the PIC's own timer. There are other interrupt possibilities, but which
\par are probably of more benefit to experienced programmers and which will not
\par be detailed here. Readers wanting more information on interrupts are
\par referred to the PIC Data Book.
\par 
\par There are countless situations where interrupts can be put to good use.
\par Let's examine two of them.
\par 
\par First, the address to which the program must jump when interrupted has to
\par be specified. This is where the .ORG $0004 statement now comes into its
\par own. Following that statement, and prior to the .ORG $0005 statement, the
\par jump address is inserted. Let's call the jump address INTRPT. We have, in
\par fact, been using the term INTRPT as an address label throughout the program
\par examples so far. In the strict sense of the word, though, it has not been
\par used as an Interrupt address, merely as a convenient term. Any other term
\par could have been used in the examples shown. So, at the beginning of the
\par program listing we make the following statements:
\par 
\par .ORG $0004
\par GOTO INTRPT
\par .ORG $0005
\par 
\par Since the program, once triggered by an interrupt, automatically jumps to
\par the program address stated, we can simply set up a holding routine which
\par waits until the interrupt occurs, and then the routine specified at the
\par interrupt address is performed. We could actually allow the entire program
\par to be performed without using a holding routine, jumping to the specified
\par routine when the interrupt does occur. This is tricky, though, and can be
\par dangerous to the correct operation of the main program. Allowance has to
\par made for a particular operation to be completed before the interrupt
\par routine is performed. The use of a holding routine, therefore, seems
\par preferable. It can be as simple as:
\par 
\par START: NOP
\par        GOTO START
\par 
\par The program would normally be constantly looping through the two commands
\par NOP and GOTO START, waiting for an interrupt to occur. On its occurrence,
\par the loop would be exited, and a jump made to the routine at INTRPT.
\par Obviously, at the end of the routine caused by the interrupt, a return to
\par the program point from where the interrupt jump was made must be specified.
\par There is a command which is used for this purpose, RETFIE.
\par 
\par \plain\f2\fs24\b TIMER INTERRUPT\plain\f2\fs20 
\par 
\par A simple program which makes use of an internally timer-generated interrupt
\par to increment a count on PORTA is shown in Listing 35. Here, the timer is
\par set in the same way as we have been doing previously. Then the INTCON
\par register is told that an interrupt is to be generated when the timer rolls
\par over to zero:
\par 
\par MOVLW %10100000
\par MOVWF INTCON
\par 
\par Setting bit 7 of INTCON enables the program to respond to any interrupts
\par generated. Setting INTCON bit 5 enables the timer as the source of the
\par interrupt. The stage is now set and the START loop entered. Each time a
\par timer interrupt occurs, a jump is made to INTRPT, PORTA is incremented and
\par a return made to START by the command RETFIE. Because of the way in which
\par the PCB is connected, the fifth LED on PORTA will not be seen to function
\par unless switch SA4 is pressed (RA4 is open-collector, remember). To prove
\par that the program is not just "dropping out" of the START loop, a command to
\par increment PORTB has been included immediately following the GOTO START
\par command. As you will see on PORTB's LEDs this routine is never performed.
\par 
\par Load and run TUT35.OBJ which illustrates this interrupt.
\par 
\par \plain\f2\fs24\b EXTERNAL INTERRUPT\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b LISTING 36 - PROGRAM TUT36
\par \plain\f2\fs20 
\par \tab \tab .ORG $0004
\par \tab \tab GOTO INTRPT
\par \tab \tab .ORG $0005
\par 
\par \tab \tab CLRF PORTA
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISA
\par \tab \tab MOVLW %00000001
\par \tab \tab MOVWF TRISB
\par \tab \tab MOVLW %01000111
\par \tab \tab MOVWF OPTION
\par \tab \tab PAGE0
\par 
\par \tab \tab MOVLW %10010000
\par \tab \tab MOVWF INTCON
\par 
\par START:\tab NOP
\par \tab \tab GOTO START
\par 
\par INTRPT:\tab INCF PORTA,F
\par \tab \tab BCF INTCON,1
\par \tab \tab RETFIE
\par 
\par If, instead of using the timer to generate interrupts, we want an external
\par source to generate them, the usual pin used for this purpose is PORTB RB0,
\par designated in the pinout diagram as RB0/INT. (Logic level changes on PORTB
\par RB4 to RB7 are other possible interrupt sources.) To use RB0 as the
\par interrupt source, INTCON bit 4 must be set, as follows:
\par 
\par MOVLW %10010000
\par MOVWF INTCON
\par 
\par INTCON bit 7 must, as shown, also be set to enable the interrupt.
\par 
\par Suppose now that we want an external interrupt on RB0 to cause PORTA to be
\par incremented. Each time this interrupt occurs, the jump from the holding
\par loop is performed as before. However, it is now INTCON bit 1 which is set
\par on the interrupt and has to be cleared before returning to the holding
\par loop, i.e. BCF INTCON,1.
\par 
\par Load and run TUT36 which illustrates this external interrupt. The interrupt
\par can be generated using switch SB0. Since the switches used on the PCB are
\par only low-cost types, it is possible that switch-bounce will cause slightly
\par erratic behaviour of the LEDs. It should become clear, however, that the
\par count is basically incremented when the switch is pressed, not when it is
\par released. If a signal generator is connected to RB0 (via resistor R37) and
\par monitored on a scope, the triggering edge should be obvious when the
\par generator's rate is set very slow. The signal generator must produce clean
\par 0V to +5V pulses.
\par 
\par It is possible to change the interrupt response to occur on either edge of
\par the external pulse. As illustrated in TUT36, it is in response to the
\par rising edge. To use the falling edge, OPTION bit 6 must be cleared during
\par the PAGE1 setup routine, e.g.:
\par 
\par MOVLW %00000111
\par MOVWF OPTION
\par 
\par Change TUT36 to respond to the falling edge and observe the result when you
\par now press switch SB0. Note that the setting of OPTION bits 0, 1 and 2 is
\par irrelevant in this interrupt mode.
\par 
\par As you have seen, INTCON bit 7 is used for enabling (1) and disabling (0)
\par the interrupts, in addition to any other bits required for an interrupt to
\par be enabled. It is possible that at the moment of wishing to disable the
\par interrupts, however, that an interrupt could be in the process of
\par occurring. This would result in the disabling command not taking effect. To
\par ensure that all interrupts are fully disabled (except WDT - see later), the
\par follow routine can be used:
\par 
\par DISABL: BCF INTCON,GIE
\par         BTFSC INTCON,GIE
\par         GOTO DISABL
\par 
\par The term GIE is that equated for use as INTCON bit 7. It should be equated
\par as such in the initialising commands. Its use is in keeping with the PIC
\par Data Book, which calls this bit by that name, standing for Global Interrupt
\par Enable.
\par 
\par \plain\f2\fs24\b Exercise 28\plain\f2\fs20 
\par 
\par 28.1. Modify one of the early counting programs so that it is automatically
\par triggered by an interrupt from line RB0 without the need to read the INTCON
\par register flag.
\par 
\par 28.2. You know that INTCON bit 1 and INTCON bit 2 are both flags for
\par interrupts. Modify your program from 28.1 so it automatically responds to
\par interrupts from RB0 and from the TMR0 timer.
\par 
\par Hint: once an interrupt has occurred, the INTCON flags can be read to see
\par which source has caused the interrupt. You can also inhibit one interrupt
\par from occurring while you process the first by using other INTCON bits. Use
\par both sets of LEDs to show respective counts from each source.
\par 
\par \plain\f2\fs28\b Tutorial 29\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPT EXAMINED\plain\f2\fs20 
\par 
\par Command SLEEP
\par 
\par \plain\f2\fs24\b LISTING 37 - PROGRAM TUT37
\par \plain\f2\fs20 
\par \tab \tab .ORG $0004
\par \tab \tab GOTO INTRPT
\par \tab \tab .ORG $0005
\par \tab 
\par \tab \tab CLRF PORTA
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISA
\par \tab \tab MOVLW %00000001
\par \tab \tab MOVWF TRISB
\par \tab \tab MOVLW %01000111
\par \tab \tab MOVWF OPTION
\par \tab \tab PAGE0
\par 
\par \tab \tab MOVLW %10010000
\par \tab \tab MOVWF INTCON
\par 
\par INTRPT:\tab DECFSZ DELAY1,F
\par \tab \tab GOTO INTRPT
\par \tab \tab DECFSZ DELAY2,F
\par \tab \tab GOTO INTRPT
\par \tab \tab MOVLW 2
\par \tab \tab ADDWF PORTB,F
\par \tab \tab BTFSS STATUS,C
\par \tab \tab GOTO BYPASS
\par \tab \tab INCF PORTA,F
\par \tab \tab SLEEP
\par 
\par BYPASS:\tab BCF INTCON,1
\par \tab \tab GOTO INTRPT
\par 
\par SLEEP is another command that is rarely likely to find use by most readers.
\par This mode sets the PIC into a very low current power-down mode. This can be
\par useful if the PIC is monitoring or controlling something at a very slow
\par rate. In this situation, there are power saving advantages if the PIC can
\par be put to sleep during periods when it is not required to perform. The PIC
\par can be awoken from SLEEP by a WDT time-out or through an external
\par interrupt. The program which illustrates the latter is TUT37. Load and
\par run it.
\par 
\par The program adds two to the count on PORTB's LEDs from zero up to the
\par roll-over at 256, at which point PORTA is incremented, its count being
\par shown on its LEDs. At this point, the program is told to SLEEP. It can only
\par be awoken by pressing switch SB0. Whereupon, the PORTB count resumes, until
\par again it rolls over to zero and increments PORTA, then falling asleep once
\par more. (This might remind you of your occasional behaviour on a Monday
\par morning after "the week-end before"!) Note the use of two delays (DELAY1
\par and DELAY2) slowing the program down by 256 x 256 looped actions for the
\par sake of the demo.
\par 
\par \plain\f2\fs24\b Exercise 29\plain\f2\fs20 
\par 
\par 29.1. Put the PIC to sleep between each detection of a TMR0 interrupt
\par occurring every 1/25th of a second while allowing it to appropriately
\par increment a seconds counter and show its value on any of the display types
\par covered.
\par 
\par \plain\f2\fs28\b Tutorial 30\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Watchdog timer
\par Command CLRWDT
\par 
\par \plain\f2\fs24\b LISTING 38 - PROGRAM TUT38
\par \plain\f2\fs20 
\par \tab \tab .ORG $0004
\par \tab \tab GOTO TESTON
\par \tab \tab .ORG $0005
\par 
\par \tab \tab CLRF PORTA
\par \tab \tab CLRF PORTB
\par \tab \tab PAGE1
\par \tab \tab CLRF TRISA
\par \tab \tab MOVLW %00000001
\par \tab \tab MOVWF TRISB
\par \tab \tab MOVLW %00001111
\par \tab \tab MOVWF OPTION
\par \tab \tab CLRWDT
\par \tab \tab PAGE0
\par \tab 
\par \tab \tab MOVLW %00000000
\par \tab \tab MOVWF INTCON
\par \tab 
\par TESTON:\tab BTFSS PORTB,0
\par \tab \tab GOTO TESTON
\par \tab \tab MOVLW 2
\par \tab \tab ADDWF PORTB,F
\par \tab \tab BTFSS PORTB,3
\par \tab \tab GOTO TSTOFF
\par \tab \tab INCF PORTA,F
\par \tab \tab CLRF PORTB
\par 
\par \tab \tab PAGE1
\par \tab \tab CLRWDT
\par \tab \tab PAGE0
\par 
\par TSTOFF:\tab BTFSC PORTB,0
\par \tab \tab GOTO TSTOFF
\par \tab \tab GOTO TESTON
\par 
\par The Watchdog Timer (WDT) facility is also probably one for which most
\par readers are unlikely to find much use. The purpose of a WDT is to give the
\par PIC a type of protection against becoming stuck in a perpetual loop. This
\par can happen in several ways, but particularly in the event of unforeseen
\par program errors, or waiting for an external event to happen but which does
\par not (for many and varied reasons, including equipment malfunction). It is
\par also possible for electrical spikes on power lines to cause the
\par malfunction, although it can be argued that the use of a good power supply
\par should be mandatory in situations where this could be an unacceptable
\par problem.
\par 
\par In effect, the WDT provides a "last-ditch" time-out timer which, if it is
\par allowed to time-out, causes a complete system reset. The idea is that the
\par WDT is set with a timing value, and then at regular intervals in the main
\par loop of the program, this value is repeated reloaded into it, i.e. it is
\par reset, using the command CLRWDT. Should a problem occur which prevents the
\par WDT value from being reloaded, the WDT will time-out and cause a full
\par program reset.
\par 
\par The difficulty of using a WDT in many programs is that when the full reset
\par occurs, any variables which are specifically set to known values at the
\par start of the program will once more be reset to them. This means, for
\par example, that event counters within the program will also be reset. When
\par the existing count value is of importance, rather than use the WDT, the
\par program should be written so that an interrupt (from a switch, for
\par instance) can cause the program to resume running without being reset.
\par However, if it doesn't matter that the program restarts from the beginning,
\par as in some burglar alarm systems perhaps, then the WDT can be beneficially
\par used. It should be pointed out that the alarm system discussed earlier is
\par not suitable for WDT use since a real-time clock is an integral part of the
\par program.
\par 
\par To use the WDT, the PIC has to be set for this function prior to the
\par required operational program being loaded, using the PIC configuration
\par program. You will recall that when configuring the PIC for RC and crystal
\par modes, WDT was not selected. Now, though, reconfigure the PIC and set the
\par Watchdog on. Then load TUT38 and run it.
\par 
\par \plain\f2\fs24\b WATCH IT!\plain\f2\fs20 
\par 
\par Observing the LEDs on PORTB, repeatedly press switch SB0 on and off. At
\par each press, the count value displayed on the LEDs will be seen to increase.
\par The idea is to reach a count of %00001000, at which point the WDT is reset
\par with its starting value, the LEDs on PORTA increment, and PORTB is reset to
\par zero. Should you not press the switch fast enough, the WDT will time-out
\par and restart the program from the beginning, causing PORTA, PORTB and their
\par LED counts to be reset to zero.
\par 
\par The WDT timing period can be changed in the same way that we set the timing
\par prescaler for the real-time clock, i.e. using bits 0 to 2 of OPTION. Bit 3
\par of OPTION must always be set so that the prescaler is allocated to the WDT.
\par Try changing the values of OPTION bits 0 to 2; also see what happens when
\par STATUS bit 3 is set to zero.
\par 
\par The WDT cannot be disabled from within an operational program. It can only
\par be turned off from the PIC configuration program. Consequently, when you
\par have finished experimenting with the WDT, once again reconfigure the PIC
\par with WDT disabled. Unless you do this, none of the other demonstration
\par programs will run correctly.
\par 
\par An independent RC oscillator is used by the WDT and its timing is
\par unaffected by the frequency of the external oscillator that controls the
\par rest of the PIC.
\par 
\par Be aware that during development of the Tutorials, it was found necessary
\par to occasionally run the WDT configuration twice before the PIC would accept
\par this mode. The reason is unknown. If TUT38 does not behave as expected,
\par re-run the configuration for WDT.
\par 
\par \plain\f2\fs24\b Exercise 30\plain\f2\fs20 
\par 
\par 30.1. Invent your own exercise for WDT!
\par 
\par \plain\f2\fs28\b Tutorial 31\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPT EXAMINED\plain\f2\fs20 
\par 
\par Misc Special Register bits
\par 
\par We have examined the use of quite a few bits in the Special Registers, but
\par not all of them (see Table 4). First, there are two bits in the STATUS
\par register whose purpose and use seems obscure:
\par 
\par STATUS bit 3 (PD): POWER-DOWN bit. Set to 1 during power up or by a CLRWDT
\par command. Cleared to 0 by a SLEEP command.
\par 
\par STATUS bit 4 (TO): TIME-OUT bit. Set to 1 during power up and by the CLRWDT
\par and SLEEP commands. Cleared to 0 by a WDT time-out.
\par 
\par There are others which are similar in their setting to those that we have
\par discussed, that their examination is not justified here. The bits are
\par principally in the INTCON and OPTION registers, details of which are shown
\par in the PIC Data Book. A summary is as follows:
\par 
\par INTCON bit 0 (RBIF): RB port change interrupt flag. Set when any of RB4 to
\par RB7 inputs change. Has to be reset in software.
\par 
\par INTCON bit 3 (RBIE): RBIF interrupt enable bit; 0 = disable, 1 = enable.
\par 
\par OPTION bit 5 (RTS): TMR0 signal edge response to signal on RA4/TOCKI pin;
\par     0 = increment on low-to-high transition
\par     1 = increment on high-to-low transition.
\par 
\par It is suggested that you write simple routines, along the lines of those
\par that have been used in other demonstrations, to establish for yourself what
\par can be achieved using these bits. It is also well worthwhile reading
\par through the PIC16x84 part of the PIC Data Book in its entirety. There are
\par minor aspects relating to some of the commands that we have discussed that
\par deserve recognition if you wish to delve more deeply into programming these
\par devices.
\par 
\par What has been discussed here is, we believe, more than enough to fulfil our
\par intention to "get you started with the PIC16x84", but it is not the end of
\par the story.
\par 
\par \plain\f2\fs28\b Tutorial 32\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b CONCEPTS EXAMINED\plain\f2\fs20 
\par 
\par Programming
\par PICs vs. Hardware
\par Summing-up
\par 
\par \plain\f2\fs24\b PROGRAMMING\plain\f2\fs20 
\par 
\par To the uninitiated, it may seem that a software programmer simply sits down
\par and writes all the commands in a single operation. If only it were that
\par simple! Before a single line of code is written, there is a great deal of
\par thought involved about the overall objective and how each step on the way
\par to achieving it might be performed. Part of this consideration relates not
\par only to the logic of the software routines, but also to the control
\par requirements of external interfaces.
\par 
\par There are two schools of thought about the planning. The first considers
\par that the use of flow charts is an essential requirement. The other doesn't!
\par The advantage of using a flow chart is that it shows the questions and
\par answers of each stage of the program in a diagrammatic form. Theory says
\par that this chart then enables the code to be written to meet each of the
\par requirements illustrated. The use of a flow chart certainly helps in
\par concentrating immediate thought processes, and in recapturing concepts in
\par the future, but it cannot display the command by command reasoning of each
\par line of code. Only the code itself shows that, unless you translate each
\par line of code into lengthy textual comments, in which case there is the
\par danger of getting bogged down with words.
\par 
\par Additionally, there is always the possibility that some logical
\par consideration has been omitted from the flow chart and which only comes to
\par light once you try to run the program, requiring the chart to be redrawn as
\par well as the software having to be rewritten. The author finds that the
\par detailed thinking about the program structure already builds up as a mental
\par flow chart which does not require to be set down on paper. It is
\par acknowledged that in a commercial situation it would be mandatory for the
\par program structure to be well documented with flow charts - the program
\par might eventually need to be changed by someone other than the original
\par programmer. In that case, the flow chart would give a more immediate
\par insight into the original programmer's thought processes. However, let us
\par not deter you from drawing up flow charts if you prefer to do so. You may
\par well find that they help you to grasp what you are doing more readily than
\par just relying on your mental "visualisation" processes.
\par 
\par To discuss flow charts more fully is beyond the scope of this Tutorial, but
\par you will find examples of them in Microchip's Embedded Control Handbook. It
\par has to be said, though, that even in that publication, which is full of
\par program listings, flow charts are not widely used.
\par 
\par \plain\f2\fs24\b STAGE BY STAGE\plain\f2\fs20 
\par 
\par Whether or not you use flow charts, you should never attempt to write the
\par entire program from beginning to end in one operation. That way can lead to
\par extensive problems when you try to debug the program having found that it
\par doesn't do what you expected. Take each routine stage by stage. Get one
\par small section of code working before you move onto the next. Then get that
\par next small section working before you try to join it to the previous part.
\par "Be methodical" is the key command when programming.
\par 
\par In many ways, the manner in which this Tutorial has been presented has been
\par along those lines. We have tried to show you individual structures which
\par first stand on their own, and then are extended or joined to others to
\par achieve a larger operational system. Taking TUT2 as the effective starting
\par point, that program used only 13 command lines. Gradually we developed
\par other programs as stand-alone routines. Then things began to take a broader
\par structure as concepts were integrated into a more sophisticated whole. With
\par TUT34, nearly 500 commands were sent to the PIC - nearly half its capacity.
\par 
\par As you get further into PIC programming, you may decide that you would like
\par to write code in conjunction with a simulation program. These help you to
\par debug code on your PC before downloading it to the PIC. They will not
\par replace the thought processes needed when writing code, but they will let
\par you find many (but not all) of the errors more quickly.
\par 
\par However, the author finds it very easy to check program operation when the
\par code is in the PIC and the PIC is connected to its various interfaces. Had
\par the PIC16x84 not been an EEPROM device, then this would not be an
\par acceptable technique, but it IS rapidly reprogrammable and so is usable as
\par a live test-bed.
\par 
\par One further point, when writing a program the author finds it useful to
\par supplement its software file name with a suffix number, increasing the
\par number at each save of a major addition or change to the previous code
\par written. This allows an earlier version to be recalled should the need
\par arise. Thus you would number as, for example, PICIT01.ASM, PICIT02.ASM,
\par PICIT03.ASM, etc.
\par 
\par \plain\f2\fs24\b PICS VS. HARDWARE\plain\f2\fs20 
\par 
\par As enormously beneficial as the use of a microcontroller can be, there is
\par the likelihood that it may be regarded by the inexperienced as the ultimate
\par answer to all electronic circuit design. This is most definitely not the
\par case. All that a microcontroller will do is assist in using software
\par commands to replace a fair number of operations for which many electronic
\par components would otherwise be needed. It cannot substitute for all
\par electronic requirements.
\par 
\par There are also situations in which a microcontroller CAN be used but it is
\par not necessarily desirable that it should. What you will discover as you get
\par further into programming, is that the act of programming a PIC to replace a
\par given number of logic chips takes far longer than if you were to design a
\par circuit that performed the same function but only used such chips. Unless
\par you actually WANT to get a PIC to do something because it CAN and you see
\par it as a challenge, always ask yourself if the additional development time
\par is worth it in order to save a chip or two.
\par 
\par \plain\f2\fs24\b SUMMING-UP\plain\f2\fs20 
\par 
\par When writing software, you will find much frustration through the inability
\par to immediately see the bug in a program routine. Eventually, though, you
\par will spot it and the relief and exhilaration of at last getting that part
\par to work is enormous. In that frame of mind, you will move onto writing the
\par next sub-routine with the utmost confidence and anticipation of not making
\par a mistake on THIS one. Would that it were so! You can, and you will, make
\par mistakes. But the ultimate satisfaction of a complete working design makes
\par it all worthwhile.
\par 
\par If you can't take occasional bouts of desperation, isolation from friends
\par and family, followed by periods of ecstasy and feelings of well-being
\par towards all humanity, leave programming alone. The author, though, has
\par become a "programming-addict" and thrives on the challenges, come what
\par may!
\par 
\par Finally, remember that Murphy's Law has its most powerful influence when
\par programming is involved. If the microcontroller or other computer CAN
\par misunderstand what you MEAN by your commands, it WILL. It is up to you to
\par see the way in which each and every one of your commands will ACTUALLY be
\par interpreted. You are the intelligent one, the computer simply obeys your
\par instructions!
\par 
\par \plain\f2\fs28\b APPENDIX A:\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b BUGGED TEASER
\par 
\par LISTING 39 - COUNTING BUGS!
\par \plain\f2\fs20 
\par TABLE:ANDLW 7
\par \tab ADDWF PCL,F
\par \tab RETLW 'F'
\par \tab RETLW 'R'
\par \tab RETLW 'E'
\par \tab RETLW 'Q'
\par \tab RETLW ' '
\par \tab RETLW 'C'
\par \tab RETLW 'O'
\par \tab RETLW 'U'
\par \tab RETLW 'N'
\par \tab RETLW 'T'
\par \tab RETLW 'E'
\par \tab RETLW 'R'
\par \tab RETLW ' '
\par \tab RETLW ' '
\par \tab RETLW ' '
\par 
\par START:CALL SETUP
\par \tab CLRF LOOPB
\par 
\par MESSAG:MOVF LOOPB,W
\par \tab CALL TABLES
\par \tab CALL LCDOUT
\par \tab INCF LOOPB,F
\par \tab BTFSS LOOPB,4
\par \tab GOTO MESSAGE
\par 
\par INTCLR:MOVLW 50
\par \tab MOVWF CLKCNT
\par \tab CLRF COUNT0,W
\par \tab CLRF COUNT1,W
\par \tab CLRF COUNT2,W
\par \tab CLRF COUNT3,W
\par \tab CLRF COUNT4,W
\par \tab CLRF COUNT5,W
\par \tab BCF INTCON,2
\par 
\par INTRPT:MOVF PORTA,W
\par \tab ANDLW %00010000
\par \tab MOVWF STORE1
\par \tab XORWF COUNT0,W
\par \tab BTFSC STATUS,Z
\par \tab GOTO INT2
\par \tab MOVLW COUNT1,F
\par \tab MOVWF FSR
\par 
\par INTINC:INCF INDF,F
\par \tab MOVF INDF,W
\par \tab ADDLW 6
\par \tab BTFSS STATUS,DC
\par \tab GOTO INT2
\par \tab CLRF INDF
\par \tab INCF FSR,F
\par \tab GOTO INTINC
\par 
\par INT2:\tab MOVF STORE1,W
\par \tab MOVWF COUNT0
\par \tab BTFSS INTCON,2
\par \tab GOTO INTRPT
\par \tab BCF INTCON,2
\par \tab DECFSZ CLKCNT,F
\par \tab GOTO INTRPT
\par 
\par INT3:\tab CALL LCD2
\par \tab MOVLW COUNT5
\par 
\par SHOWIT:MOVWF FSR
\par \tab MOVLW 6
\par \tab MOVWF LOOPB
\par 
\par SHOW2:MOVF INDF,W
\par \tab IORLW 64
\par \tab CALL LCDOUT
\par \tab DECF FSR,F
\par \tab DECFSZ LOOPB,F
\par \tab GOTO SHOW2
\par       MOVLW 'H'
\par       MOVLW 'z'
\par \tab CALL LCDOUT
\par \tab GOTO INTCLR
\par 
\par LCD1:\tab MOVLW %10000000
\par \tab GOTO LCDLIN
\par 
\par LCD2:\tab MOVLW %11000000
\par 
\par LCDLIN:BCF RSLINE,4
\par \tab GOTO LCDOUT
\par 
\par Now to give your understanding of PIC programming (and your logical
\par thinking) a bit of a test!
\par 
\par Type in everything as stated in Listing 39 (this program has not been put
\par on disk, in order to test your ability at keying in commands!)(It could,
\par alternatively, be a test of your ability to copy data from one file to
\par another.)
\par 
\par Add an appropriate initialisation routine at the beginning, and add a
\par suitable set of LCD operating routines as illustrated earlier. Save the
\par code as two slightly different file names, then work on the second file.
\par 
\par Listing 39 has a number of bugs deliberately included - your task is to
\par debug the program and get it working as a frequency counter! Some of the
\par deliberate errors will be reported by the Assembler following assembly.
\par These are "literal" errors which anyone might make while creating a PIC
\par program - simple slips in thinking. The others, though, are "logical"
\par errors - much more significant errors in a programmer's analysis of a
\par situation and its interpretation, but still errors anyone could make.
\par 
\par First of all, get the LCD to display the opening message correctly (and
\par without the program "crashing"). Then, with the aid of a Signal Generator
\par (0V/5V output logic level) set to about 10kHz (you must decide which PIC
\par input pin to use), solve the remaining logical problems. You'll probably
\par curse the author a few times before you solve it all, but keep at it!
\par Having solved it (and felt the satisfaction of success!), think about how
\par switches and other routines could extend the counter's range.
\par 
\par \plain\f2\fs28\b APPENDIX B\plain\f2\fs20 
\par 
\par \plain\f2\fs24\b BASIC TASM AND MPASM DIFFERENCES\plain\f2\fs20 
\par 
\par With regard to the use of the "official" mnemonic codes for the PIC16x84 as
\par published in the PIC Data Book, there is no difference between TASM and
\par MPASM. There are differences, however, in the way in which numerical values
\par are expressed, and in some other significant matters.
\par 
\par Toolkit TK3 details the major differences through one of its Text buttons.
\par The comments relate to MPASM V01.40 - other versions may have other
\par differences. For more details about MPASM, refer to Microchip's MPASM
\par User's Guide. The following are also worth noting:
\par 
\par \plain\f2\fs24\b RADIX\plain\f2\fs20 
\par 
\par You discovered earlier that TASM requires the numerical formats as shown in
\par the following examples:
\par 
\par Decimal:      153
\par Hexadecimal:  $2B
\par Binary:       %10010110
\par ASCII:        'C'
\par 
\par Decimal requires no prefix, hex and binary need $ and % prefixes
\par respectively, ASCII requires enclosing in "right-handed" single quotes (').
\par 
\par MPASM, though, uses styles that (usually) require both prefix letters and
\par enclosing "right-handed" quotes:
\par 
\par Decimal:     D'153'
\par Hexadecimal: H'2B'
\par Binary:      B'10010110'
\par ASCII:       'C' or A'C'
\par Octal:       0'777'
\par 
\par Note the choice of two equivalent styles for ASCII, and that TASM (and
\par Toolkit) has no equivalent for the Octal expression.
\par 
\par A significant difference for MPASM is that the user may choose to express
\par decimal, hex or binary values without the use of a prefix and enclosing
\par quotes, defining this requirement in an initialisation line which is
\par prefixed with the statement "list". The term "radix" is used in this
\par respect, and the radix is then equated to be in decimal, hex or binary,
\par according to the user's preference. This same line also states the
\par processor for which the code is to be assembled. For example, the following
\par line tells MPASM that the code is to be assembled for the PIC16F84
\par (p=16F84) and that the radix is decimal (r=dec):
\par 
\par list p=16f84, r=dec
\par 
\par Having specified the radix, any unprefixed value encountered by MPASM
\par during assembly will be taken to be in that notation. Thus if the radix is
\par decimal (r=dec), 10 will be taken as decimal ten. If the chosen radix is
\par hex (r=hex), then 10 will be taken as $10 (decimal 16). For a binary radix
\par (r=bin), 10 will be interpreted as %00000010 (decimal 2).
\par 
\par Thus, before translating source code from MPASM to TASM, always check the
\par radix statement and convert values accordingly. If no radix statement is
\par made, the default radix is hexadecimal.
\par 
\par \plain\f2\fs24\b OTHER LIST OPTIONS\plain\f2\fs20 
\par 
\par Several other equational statements may occur following the "list"
\par statement. These control the format process when MPASM is assembling the
\par source code. They have no relevance to TASM (or Toolkit) and can be ignored
\par in any translation. The following are the "list directive" options you
\par might encounter:
\par 
\par free, fixed, b=, c=, f=, mm=, n=, st=, t=, w=, x= (where additional
\par statements or values follow the equals signs).
\par 
\par The full list is covered in the MPASM User's Guide, Appendix D, Table 11
\par and in Chapter 3, Table 3.1.
\par 
\par Be aware that code for one PIC processor type may not be suitable for use
\par with another PIC type. Some such as the PIC16x84 and PIC16F87x use 14-bit
\par command codes. Some others use 12-bit codes. Toolkit only handles 14-bit
\par codes. PICs also have differing quantities of user-accessible memory
\par locations which also needs to be taken into account if using a different
\par PIC to that specified in an EPE project. Consult PIC data sheets for the
\par details.
\par \plain\f2\fs28\b 
\par APPENDIX C
\par \plain\f2\fs20 
\par \plain\f2\fs24\b USEFUL PIC INFORMATION\plain\f2\fs20 
\par 
\par Arizona Microchip Technology Ltd, Microchip House, 505 Eskdale Road,
\par   Winnersh Triangle, Woking, Berks RG41 5TU.
\par   Tel: 0118 9211 5800
\par   Fax: 0118 921 4820 (details as at Feb '98).
\par 
\par Arizona Microchip: http://www.microchip.com
\par 
\par Arizona Microchip links to related web sites:
\par   http://www.microchip2.com/wwwsites.htm
\par 
\par Arizona Microchip links to enthusiasts' pages:
\par   http://www.microchip2.com/enth.htm
\par 
\par Arizona Microchip's UK sales rep: http://www.arizona.co.uk/arizona
\par 
\par Arizona UK Tech Desk (E-mail): techdesk@arizona.co.uk
\par 
\par EPE web site: http://www.epemag.wimborne.co.uk
\par 
\par EPE FTP site: ftp://ftp.epemag.wimborne.co.uk
\par 
\par EPE PIC-project source code files:
\par   ftp://ftp.epemag.wimborne.co.uk/pubs/PICS
\par 
\par Macintosh PIC tools: http://www3.sympatico.ca/numerel
\par 
\par PIC-related Internet resources list: http://www.eetoolbox.com/gatopix.htm
\par 
\par PICpoint website: http://www.picpoint.com
\par 
\par \plain\f2\fs28\b FURTHER READING\plain\f2\fs28 
\par \plain\f2\fs20 
\par \plain\f2\fs20\i PIC16/17 Microcontroller Data Book, Microchip Embedded Control Handbook,
\par MPASM User's Guide\plain\f2\fs20 . These books are obtainable from Arizona Microchip,
\par their distributors or via their web site. They are also available from
\par Microchip in CD-ROM format.
\par 
\par \plain\f2\fs20\i Everyday Practical Electronics \plain\f2\fs20 magazine. Frequent articles on PICs in
\par practical constructional designs!
\par 
\par \plain\f2\fs24\b\i John Becker\plain\f2\fs20 
\par 
\par }
 