HOW TO PROGRAM THE APPLE II USING 6502 ASSEMBLY LANGUAGE With an Introduction to Sweet-16 DATAMOST by Randy Hyde Apple II is a trademark of Apple Computer, Inc. DATAMOST 8943 Fullbright Ave., Chatsworth, CA 91311 (213) 709-1201 cover ***************************************************************** USING 6502 ASSEMBLY LANGUAGE ***************************************************************** USING 6502 ASSEMBLY LANGUAGE How Anyone Can Program the Apple II By Randy Hyde A Product of DATAMOST, INC. 8943 Fullbright Avenue Chatsworth, CA 91311 (213) 709-1202 1st Printing October 1981 2nd Printing December 1982 ***************************************************************** -ACKNOWLEDGMENTS- This book represents many hours of dedicated work by myself and everyone involved in its generation. While their names do not appear on the cover, special credit is due to David Gordon, Larry Bouyer, and my wife Mandy. The management and marketing efforts by Dave made this book possible (although it took a long time...). Larry and Mandy transformed a computer program- mer's "illiterate" rough draft into this document. Many thanks also to Glynn Dunlap, whose wonderful cartoons added greatly to this book. I owe these four people a great deal. The material included in Appendix A is reproduced with the permission of Apple Computer, Inc. It is originally printed in "The Apple II Reference Manual" copyrighted by Apple Computer. Thanks is hereby given to Apple Computer for allowing repro- duction herein. COPYRIGHT (C) 1981 BY DATAMOST This manual is published and copyrighted by DATAMOST. All rights are reserved by DATAMOST. Copying, duplicating, selling or otherwise distributing this product is hereby expressly forbid- den except by prior written consent of DATAMOST. The word APPLE and the Apple logo are registered trademarks of APPLE COMPUTER, INC. APPLE COMPUTER, INC. was not in any way involved in the writing or other preparation of this manual, nor were the facts presented here reviewed for accuracy by that company. Use of the term APPLE should not be construed to represent any en- dorsement, official or otherwise, by APPLE COMPUTER, INC. i ***************************************************************** TABLE OF CONTENTS NOTE An alphabetical index is located in the back of this manual. Chapter 1 INTRODUCTION 1-1 Purpose of Manual 1-1 Scope of Manual 1-1 General 1-1 Chapter 2 SYMBOLISM 2-1 General 2-1 Bit Strings 2-3 Binary Arithmetic 2-8 Unsigned Integers 2-9 Nibbles (NYBBLES?), Bytes, and Words 2-10 Signed Integers 2-11 Hexadecimal Numbers 2-13 Radix and Other Nasty Diseases 2-14 ASCII Character Set 2-14 Using Bit Strings to Represent Instructions 2-16 Chapter 3 REGISTERS, INSTRUCTION FORMATS, AND ADDRESSING 3-1 General 3-1 Accumulator (A or ACC) 3-3 X-Register (X) 3-3 Y-Register (Y) 3-3 Stack Pointer (SP) 3-4 Program Status Word (P or PWS) 3-4 Program Counter (PC) 3-4 Instruction Format (6502) 3-4 Two and 3-Byte Instructions 3-6 6502 Addressing Modes 3-8 iii ***************************************************************** Chapter 4 SOME SIMPLE INSTRUCTIONS 4-1 General 4-1 Assembly Language Source Format 4-1 Introduction to Real Instructions 4-4 Register Increments and Decrements 4-8 Labels and Variables 4-9 Expressions in the Operand Field 4-11 Chapter 5 ASSEMBLY LANGUAGE 5-1 General 5-1 Example Program 5-2 JMP Instruction 5-3 Processor Status (P) Register 5-5 Break Flag (B) 5-6 Decimal Flag (D) 5-6 Interrupt Disable Flag (Z) 5-6 Condition Code Flags (N, V, Z, C) 5-7 Branch Instructions (6502) 5-9 Loops 5-10 Comparisons 5-11 IF/THEN Statement Simulation 5-14 FOR/NEXT Loop Revisited 5-14 Testing Boolean Values 5-18 Chapter 6 ARITHMETIC OPERATIONS 6-1 General 6-1 Unsigned Integer (Binary) Arithmetic 6-1 Subtraction 6-4 Signed Arithmetic 6-5 Signed Comparisons 6-7 Binary Coded Decimal Arithmetic 6-8 Unsigned BCD Arithmetic 6-8 Signed BCD Arithmetic 6-10 Arithmetic Review 6-10 iv ***************************************************************** Chapter 7 SUBROUTINES AND STACK PROCESSING 7-1 General 7-1 Variable Problems 7-4 Passing Parameters 7-13 Chapter 8 ARRAYS, ZERO PAGE, INDEXED, AND INDIRECT ADDRESSING 8-1 General 8-1 Zero Page Addressing 8-1 Arrays in Assembly Language 8-3 Initializing Arrays at Assembly Time 8-8 Using Index Registers to Access Array Elements 8-10 Indirect Addressing Mode 8-13 Indirect Indexed Addressing 8-16 Indexed Indirect Addressing Mode 8-18 Chapter 9 LOGICAL, MASKING, AND BIT OPERATIONS 9-1 General 9-1 Complement Function 9-2 AND Function 9-2 OR Function 9-3 EXCLUSIVE-OR Function 9-4 Bit String Operations 9-4 Instructions for Logical Operations 9-5 Masking Operations 9-7 Shift and Rotate Instructions 9-13 Shifting and Rotating Memory Locations 9-16 Using ASL to Perform Multiplication 9-17 Using Shifts to Unpack Data 9-19 Using Shifts and Rotates to Pack Data 9-20 Chapter 10 MULTIPLE-PRECISION OPERATIONS 10-1 General 10-1 Multiple-Precision Logical Operations 10-1 v ***************************************************************** Multiple-Precision Shifts and Rotates 10-3 Multiple-Precision Logical Shift-Right Sequences 10-4 Multiple-Precision Rotate-Left Sequences 10-4 Multiple-Precision Rotate-Right Sequences 10-5 Multiple-Precision Unsigned Arithmetic 10-6 Multiple-Precision Unsigned Subtraction 10-8 Multiple-Precision Signed Arithmetic 10-9 Multiple-Precision Decimal Arithmetic 10-9 Multiple-Precision Increments 10-9 Multiple-Precision Decrements 10-10 Multiple-Precision Unsigned Comparisons 10-11 Signed Comparisons 10-14 Chapter 11 BASIC I/O 11-1 General 11-1 Character Output 11-1 Standard Output and Peripheral Devices 11-9 Character Input 11-11 Inputting a Line of Characters 11-13 Chapter 12 NUMERIC I/O 12-1 General 12-1 Hexadecimal Output 12-1 Outputting Byte Data as a Decimal Value 12-2 Outputting 16-Bit Unsigned Integers 12-4 Outputting Signed 16-Bit Integers 12-6 An Easy Method of Outputting Integers 12-6 Numeric Input 12-8 Unsigned Decimal Input 12-11 Signed Decimal Input 12-17 Chapter 13 MULTIPLICATION AND DIVISION 13-1 General 13-1 Multiplication 13-1 Division Algorithms 13-7 vi ***************************************************************** Chapter 14 STRING HANDLING OPERATIONS 14-1 String Handling 14-1 Declaring Literal Strings 14-5 String Assignments 14-5 String Functions 14-7 String Concatenation 14-9 Substring Operations 14-11 String Comparisons 14-12 Handling Arrays of Characters 14-17 Chapter 15 SPECIALIZED I/O 15-1 Apple I/O Structure 15-1 Chapter 16 AN INTRODUCTION TO SWEET-16 16-1 Sweet-16 16-2 Sweet-16 Hardware Requirements 16-10 Chapter 17 DEBUGGING 6502 MACHINE LANGUAGE PROGRAMS 17-1 General 17-1 GO Command (G) 17-2 Initializing Registers and Memory 17-3 Modifying Instruction Code (Patching) 17-6 Program Debugging Session 17-10 Appendix A APPLE II COMPUTER TABLES, CHARTS, AND GRAPHS A-1 vii ***************************************************************** CHAPTER 1 INTRODUCTION PURPOSE OF MANUAL. This manual provides 6502 assembly language instructions addressed directly to APPLE II computer applications. The infor- mation contained herein is intended for use by beginning, inter- mediate and advanced programmers. SCOPE OF MANUAL. This manual contains explanations of basic symbols and terminology used by programmers and engineers. Included is an introduction to computer concepts, simple assembly language instruction examples, and detailed 6502 assembly language instructions as related to APPLE II computer requirements. GENERAL. Why another book on 6502 assembly language? Well, there are several reasons. First, there were only two books available on the subject when I began writing this book. Second, none of the available books address themselves directly to the APPLE II computer. While assembly language theory can be learned from books, examples that run on other computers using 6502 assem- bly language are of little use to the APPLE II computer owner. This book is the product of my experiences as a 6502 assembly language instructor. The material chosen for this book is easily learned by the beginner. No promises can be made con- cerning your individual levels of expertise achieved after reading this book, but the material presented here should raise you to the level of an intermediate 6502 assembly language programmer. The "expert" status is achieved only through years of experience. This book is intended for the beginner. Intermediate and advanced programmers may find several items of interest in this book, but it was written with the beginner in mind. If you have had 1-1 ***************************************************************** prior 6502 experience, the first few chapters may contain infor- mation which you have seen previously. AVOID THE TEMPTA- TION TO SKIP ANY MATERIAL! If one important detail is not understood, the remainder of the book may prove impossible to understand. So take the time to review all of the available material and make sure that you understand the reviewed section before going on. Obviously, if you are a beginner it is very important that you understand each section before continuing. Since there are so many excellent books on computer the- ory, microcomputers, etc., I will try to keep the discussion of these subjects to a minimum. There are several books you should own if you are interested in learning 6502 assembly language. Books I highly recommend include: HOW TO PROGRAM MICROCOMPUTERS by William Barden Jr. PROGRAMMING THE 6502 by Rodney Zaks PROGRAMMING A MICROCOMPUTER by Caxton C. Foster 6502 ASSEMBLY LANGUAGE PROGRAMMING by Lance Leventhal 6502 SOFTWARE GOURMET GUIDE & COOKBOOK by Robert Findley While all of the previously mentioned text books are excel- lent, they were not written with the APPLE II computer in mind. This text presents practical applications instead of just the theory. Since each of the above books present 6502 assembly language in a different manner you may refer to them should you encounter any difficulties understanding the material presented here. If you are serious about learning assembly language you should have access to the previously mentioned text books as well as this manual. Before getting into assembly language, it would be very wise to aquaint you with some of the 'jargon' that will be used through- out this manual. 1-2 ***************************************************************** RAM: User memory. Programs and data are stored in the RAM. (RAM is an acronym for Random Access Memory) ROM: Used to hold the Apple monitor and BASIC. You cannot store data or programs in the ROM. (ROM is an acronym for Read-Only Memory.) MONITOR: A set of subroutines in ROM which allow you to read the keyboard, write characters to the video screen, etc. BASIC: When the word "BASIC" is used, it means Integer BASIC. Applesoft BASIC is referred to as "Applesoft." K: When "K" is encountered, you simply substitute "x 1024" (i.e, multiplied by 1024). Generally used to denote a memory size (such as 48K). MEMORY: Combination of all RAM and ROM locations. SIGNED: Any legal positive or negative integer ("legal" NUMBER as defined by the current operation). UNSIGNED: Any legal positive (only) number. Negative NUMBER numbers are not allowed. BYTE: One unit of memory. A byte can represent up to 256 different quantities (such as the numbers 0-255). WORD: Two bytes stuck back to back. With a word you can represent up to 65,536 different quantities (such as the numbers 0-65,535 or the signed numbers (-32768) to (32767)). SYNTAX: The rules governing sentence structure in a language, or statement structure in a language such as that of a compiler program. ADDRESS: Two bytes used to point to one of the 64K available memory locations in the APPLE II computer. An Address is also a Word but a Word is not necessarily an Address. PAGE: The 65,536 bytes in the address range of the APPLE II computer are broken into 256 blocks blocks of 256 bytes each. These blocks are numbered 0 to 255 and are called pages. ZERO: The first 256 bytes in the memory space (page number PAGE 0) of the APPLE II computer are often referred to as the "zero page" or "page zero." Naturally there is a "page one," a "page two," etc., but the use of the first 256 bytes in the machine occurs so often that the term, "zero page," has come into common use. 1-3 ***************************************************************** SLOT: One of the peripheral connectors (0-7) on the APPLE II computer. I/O: An acronym for input/output. LISA: An acronym for Lazer Systems Interactive Symbolic Assembler, pronounced LI ZA, not LE SA. PERIPHERAL: An I/O device (such as a disk or printer) connected externally to the computer. It is assumed, in this manual, that the reader is familar with Apple BASIC. BASIC will only be used in a few examples, but familiarity with BASIC means that you have mastered at least the elementary programming techniques. Assembly language is not the place for an absolute beginner to start. You should be some- what familar with programming concepts before attacking assem- bly language. Assembly language is a very detailed programming language and it is easy to get lost in the details if you are trying to learn elementary programming at the same time. Learning any program language, especially assembly lan- guage, requires "hands-on" experience. All of the examples pre- sented in this book use LISA (a disk-based 6502 assembler for the APPLE II computer). LISA is excellent for beginners because it is interactive, meaning it catches syntax errors immediately after the line is entered into the system. This is very much like Integer BASIC in the APPLE II computer. Since LISA catches syntax errors, learning assembly language will be easy. It is doubtful that you will ever "outgrow" it. This is not true for many other assemblers available for the APPLE II computer. If you decide to purchase an assembler now, keep in mind that, for the most part, you are stuck with it for life, since none of the assemblers available are compatible with one another. So software which you create on one assembler cannot be loaded into another assembler, even though they are both for the APPLE II computer! Even if LISA 1-4 ***************************************************************** were not interactive, I would still recommend it, since it is very powerful and will suit your needs for quite a while to come. WHY USE ASSEMBLY LANGUAGE? The fact that you have read the text this far shows that you have an interest in the subject. Nevertheless, some of you are certain to have some misconceptions about the language. Assembly language should be used when speed is the foremost requirement in a program, or possibly when you need to control a peripheral device, or maybe you have a specialized application that cannot be executed easily (or cleanly) in one of the high- level languages on the APPLE II computer. You should not use assembly language for business or sci- entific purposes. Pascal, FORTRAN, or Applesoft are better suited for these applications. Floating point arithmetic, although not impossible or even especially hard, is not something a begin- ner, or even an intermediate programmer would want to tackle. Another advantage provided by assembly language pro- grams is the possibility of interfacing them to existing BASIC, Applesoft, and Pascal programs. You can program the time crit- ical sections of code in assembly language; the rest of the code can be written in BASIC. Once you become experienced in assembly language pro- gramming you will discover that you can write and debug assem- bly language programs as fast as BASIC programs! Good luck. Hopefully, you will find machine language pro- gramming as easy as BASIC! LISA is available from your local computer store, or directly from: DATAMOST, INC. 8943 Fullbright Avenue Chatsworth, CA. 91311 (213) 709-1202 1-5 ***************************************************************** CHAPTER 2 SYMBOLISM GENERAL. When you see the number 4, what do you think? The number 4 is simply a symbol connected with the concept of four items. When humans communicate, they use several symbols to relay their ideas. As such, humans are very adaptive. If I told you that from now on we'll use the symbol "- -" to represent four, you could make the change. It might not be easy, but the change is possible. Computers, on the other hand, are very stupid. They are not adaptive and understand only a very low-level language which humans have considerable trouble understanding. This language is not "assembly" or "machine" language. Assembly, or machine language, is actually a human convention that makes an even lower-level language acceptable! The actual low-level language understood by a computer consists of different voltage levels on different wires within the machine. Although, with lots of educa- tion, humans can understand what each of these voltage levels mean (and in fact your friendly neighborhood computer repair man should), it certainly isn't very convenient. As such, we usually 2-1 ***************************************************************** rename the voltage levels something else (bits, true, false, 0, 1, etc.). We do the same thing in spoken languages all the time. For instance, "deux" (French) usually gets translated to "two" (English). Renaming voltage levels "bits" and groups of bits "words" performs this same function. We're merely taking one symbol, which is hard to understand, and translating this symbol to one easier to understand. The translation occurs in several distinct steps. These steps include: VOLTAGE => BINARY => CHARACTERS LEVELS => DIGITS => NUMBERS (+5v,0v) => (0,1) => ETC. Note that this translation is not performed by the computer. It is performed by humans. Remember, computers are dumb. Once we realize that computers only represent "things" with voltage levels, a natural question is: 'How do we represent "things" with voltage levels?' Well, as it turns out, representing binary digits (or bits) is really quite simple. We have two voltages (+5v and 0v) and two binary digits (0 and 1) to work with. Since we have a one-to-one correspondence, we'll just arbitrarily assign "1" to +5v and "0" to 0 volts. The assignment is perfectly arbi- trary. We could have defined the binary digit "0" to be +5v and the binary digit "1" to be 0 volts. By convention (which means everyone has more or less agreed upon it), however, we'll stick to the former definition. With one bit, we can represent two different values or "states." Examples include the so-called Boolean values (true or 2-2 ***************************************************************** false), signs (+ or -), yes or no, on or off, and any other user- defined binary quantities (husband/wife, boy/girl, ... you get the idea). Now that we have a bit to play around with, would you like to play around a bit? Let's define some operations on this bit. First, we need to define an ordinality for our binary values. This is necessary because often we need to compare one value to another to determine which is the greater. "0" and "1" are easy, one is always greater than zero. For the other binary values we need to use our intuition to decide on the ordinality. "True" should be greater than "false," so let's assign true the value "1" (or +5v) and false the value "0" (or 0v). Yes/no, on/off, etc., should be assigned in a similar manner. When it comes to data types, such as male/female, the choice is arbitrary. If you're a male you'll probably pick the "male" data type as being larger; if you're a female you'll probably pick "female" as being the greater value. Keep in mind that our usage of +5v and 0v becomes very context-dependent. Sometimes +5v will be used to denote the number "1," other times it will be used to denote the "true" value and in other instances it will be used as "on," etc. Try not to get confused about the type of data you are trying to represent as this can cause all kinds of problems. From this point on I will universally use "1" to denote +5v and "0" to denote 0v. For example, when I say that "true" is defined as the value "1," I really mean that true is defined as +5v. BIT STRINGS. Up to this point we have limited ourselves to one binary digit, or "bit." Although there are several applications where one bit provides enough information for our needs, there are other times when we need to represent more than two different values. A good example would be the base ten digits (0 thru 9). In this example we need to represent ten different values but our bit can only supply us with two. Well, why not use more than one bit to represent the different values? Specifically, let's use 10 bits and label them 0 thru 9. Now, to represent the digit "5," for example, we can set the sixth bit to "1" (leaving all others zero). To repre- sent the value "0" we would set the first bit to "1," leaving the rest "0." To represent the digit 9 we would set the tenth bit to "1," leaving all others at "0." 2-3 ***************************************************************** Each decimal digit would require 10 bits and would be laid out as follows: DECIMAL BIT NUMBER DIGIT 0 1 2 3 4 5 6 7 8 9 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 2 0 0 1 0 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 4 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 0 1 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 7 0 0 0 0 0 0 0 1 0 0 8 0 0 0 0 0 0 0 0 1 0 9 0 0 0 0 0 0 0 0 0 1 Note that the bits are numbered 0 thru 9. When numbering bits within a bit string, we will always start at bit number 0. Bit number 0 is the first bit, bit number 1 is the second bit, ..., bit number 9 is the tenth bit, etc. It is possible to have only a single bit "set" (set means equal to one) in our bit string. A value of 100100100 is not defined. This scheme would probably work just fine, except it is not very efficient. We have a unique string of bits for each value, but as we have defined it here there are several combinations that are unique but undefined. Since each bit we use will cost us money (since it takes one of those 16K RAM chips to equal one bit) we would like to define a bit string which 2-4 ***************************************************************** uses memory efficiently, thereby lowering the cost of our com- puter. To make our discussion easier to understand, let's just con- sider two bits. As per the previous discussion we can represent two different values with the two bits, zero and one. Wait a minute! Previously We discovered that we could represent two different values with only one bit! This means, that right off the bat, we are wasting at least half of our memory! So why don't we define the numbers zero and one as having the following two-bit values: value bit string 0 00 1 01 Note that we are using the value and simply tacking on a leading zero. Now consider the following bit strings: value bit string ? 10 ? 11 Notice that the value is undefined. We can't use zero or one because these two bit strings are quite obviously two different values from zero and one as previously defined. Since we now have two additional values, why not use them to represent the values two and three? If we do this, we wind up with the following: value bit string 0 00 1 01 2 10 3 11 So now we can represent four different values with only two bits! We save two bits over the previous method by defining our data this way! Now suppose we use a bit string of length three to represent our values. As before, if the left-most bit is zero, we can simply ignore it (the left-most bit is often called the "high-order" bit). This leads to: value bit string 0 000 1 001 2 010 3 011 ? 100 ? 101 ? 110 ? 111 2-5 ***************************************************************** Notice that we now have FOUR undefined values. Conti- nuing as expected, we will define these next four values to be the values 4 thru 7. Now we are saving quite a bit of memory. Remem- ber, previously it took eight bits to represent the values 0 thru 7, now it only takes three! We have cut our memory usage down to almost one third of that previously required! Since we want to be able to represent the decimal digits 0 thru 9, it looks like we will need to add another bit to our bit string since three bits can only represent the values 0 thru 7. Upon appending this extra bit we obtain the following: value bit string 0 0000 1 0001 2 0010 3 0011 4 0100 5 0101 6 0110 7 0111 ? 1000 ? 1001 ? 1010 ? 1011 ? 1100 ? 1101 ? 1110 ? 1111 By adding the extra bit we have added EIGHT new values to our number system. We only needed two more values however! Since we now have 16 different values on our hands, we can represent the values 0 thru 15. But, since we only needed to represent the values 0 thru 9, we will leave the bit combinations 1010 thru 1111 undefined. Yes, we are wasting some memory, but remember, we only wanted to represent the values 0 thru 9 so the waste can be considered undesirable, but required in this case. Notice the final memory savings - only four bits are required as opposed to ten! In general, each time we add a bit to our bit string we DOUBLE the number of possible combinations. For instance, with eight bits we can represent 256 different values, with ten bits we can represent 1024 different values, and with 16 bits we can represent 65,536 different values. We have just invented the binary numbering system which is used by computers! Each bit in our bit string represents a power of two. 2-6 ***************************************************************** 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 2 2 2 2 2 2 2 2 The first bit represents 2^0 (any number raised to the power "0" is one), the second bit represents two raised to the first power (i.e, 2^1), the third bit represents two raised to the second power (2^2), etc. For example, binary 1100101 represents 1x2^6 + 1x2^5 +0x2^4 + 0x2^3 + 1x2^2 + 0x2^1 + 1x2^0 or 101 in decimal. With eight bits we can represent up to (128) + (64) + (32) + (16) + (8) + (4) + (2) + (1) plus one (since we can also represent zero which is distinct from all the other values) or 256 different values. In general, to represent 2^n - 1 distinct values (such as the numbers 0 to 2^n - 1) we will need n bits. For instance, to represent the ten decimal digits 0-9, three bits are not enough as (2^3) - 1 equals 7, we still need two more values. In order to get these two extra values we must add another bit even if it means some of the available combinations must be wasted. Converse to all of this, if we are limited to n bits we can only represent 2n different values (such as the numbers 0 to (2n) - 1). Remember, we can represent quantities other than numbers with our bit strings. For instance the colors RED, BLUE, YELLOW, and GREEN as follows: COLOR BINARY CODE RED 00 BLUE 01 YELLOW 10 GREEN 11 Or possibly the alphabetic characters: Character Binary Code A 00000 B 00001 C 00010 D 00011 E 00100 F 00101 . . . . . . X 10111 Y 11000 Z 11001 (UNUSED) 11010 (UNUSED) 11011 (UNUSED) 11100 (UNUSED) 11101 (UNUSED) 11110 (UNUSED) 11111 2-7 ***************************************************************** Since there are 26 characters, we'll need 5 bits (2^5=32). Four bits simply aren't enough (2^4=16). BINARY ARITHMETIC. Now that we know how to represent data, let's see how to manipulate this data. BASIC ADDITION RULES: First let's review what happens when we add two numbers in the decimal (base ten) system. If we were to add 95 and 67, we would perform the following steps: -First we add 5 and 7 95 +67 add 5 to 7 --- 2 result is 2, carry is 1. Next, we add 9 and 6, plus one since there was a carry. 95 +67 add 9 to 6 plus one (from the carry). --- 62 result is 6, carry is 1. After the carry is added in, we get the final result of 162. Binary addition works the same way, but is even easier. It's based on seven rules: 1) 0 + 0 = 0; carry = 0 2) 1 + 0 = 1; carry = 0 3) 0 + 1 = 1; carry = 0 4) 1 + 1 = 0; carry = 1 5) 0 + 0 + carry = 1; carry = 0 6) 1 + 0 + carry = 0; carry = 1 7) 1 + 1 + carry = 1; carry = 1 So, now we can add any n-bit binary quantity as follows: STEP 1) Add 0 to 1 in the first column, which generates 1, carry = 0. 0110 0111 ---- 1 C = 0 2-8 ***************************************************************** STEP 2) Add 1 to 1 in the second column, giving zero and carry = 1. 0110 0111 ---- 01 C = 1 STEP 3) Add 1 and 1 plus 1 (from the carry). This gives us 1 and the carry remains set (equal to one): 0110 0111 ---- 101 C = 1 STEP 4) Add 0 to 0 plus 1 (from the carry). The result is one, and the addition is complete. 0110 0111 ---- 1101 C = 0 This procedure can be carried on for any number of bits. Examples of binary addition: 01101100 1101101 11101011 1111011 -------- ------- 101010111 11101000 UNSIGNED INTEGERS. Up to this point we've made the assumption that we have as many bits as we need at our disposal. In the 'real' world, this is simply not the case. Usually we are limited to a fixed number of bits (usually 8 or 16). Due to this restriction, the size of our num- bers is limited. With 16 bits we can represent numbers in the range 0 to 65,535 (2^16 - 1 = 65,535). With eight bits we can rep- resent values in the range 0 to 255. Since the 6502 is an 8-bit machine (we are limited to using 8 bits at a time), it would seem that we can only handle numbers in the range 0-255. Luckily this is not entirely true, multiple precision routines will be studied later on. An unsigned integer will be defined as any value between 0 and 65,535, so an unsigned integer will need 16 bits. 2-9 ***************************************************************** NIBBLES (NYBBLES?), BYTES, and WORDS. In our discussions, we will often use bit strings of length 4, 8, and 16. These lengths are not arbitrary, but rather they are dependant upon the hardware being used. The 6502 likes its data in chunks of 4, 8, and 16 bits. Since we use these lengths all the time, we have special names for them. A "NIBBLE" is a bit string of length four. As you may recall from the previous discussion, it takes at least four bits to represent a single decimal digit. Sometimes decimal numbers are represented by strings of nibbles (i.e, groups of four bits) in a form known as binary coded decimal. Binary coded decimal arithmetic is possible on the 6502 and will be discussed later. Often, binary coded decimal is abbreviated to BCD. A "BYTE" is a bit string of length eight. The byte is the most common data type used by the 6502 because the data width of the 6502 is eight bits (that is, the 6502 is an eight bit processor): A "WORD" is a bit string of length 16. Words are used primarily to hold addresses and integer values. With a word it is possible to represent up to 65,536 different values (64K). This is the reason the 6502 can directly address up to 64K of memory. Note that there are two nibbles in a byte and two bytes in a word. This generates some additional terminology. Each bit string has a low-order bit and a high-order bit. The low-order bit is always bit number 0, and the high-order bit is equal to (n - 1) where n is the number of bits in the bit string. For a nibble, n is four so the high-order bit is bit number three (remember, we start with zero!). For a byte (n = 8) the high-order bit is bit number 7 and for a word (n = 16) the high-order bit is bit number 15. EXAMPLES: Bit # 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 --------------------------------------------- NIB 1 0 1 0 BYTE 0 0 1 1 0 0 1 1 WORD 0 0 1 0 1 1 1 0 1 1 1 0 0 0 1 1 2-10 ***************************************************************** Additional terminology results from the symmetry of nibbles, bytes, and words. Since there are two nibbles in every byte, we can speak of a "high-order nibble" and a "low-order nibble." The low-order nibble is comprised of bits 0 thru 3 and the high-order nibble is comprised of bits 4 thru 7 in any given byte. Likewise, the low-order byte in a word consists of bits 0 thru 7 and the high- order byte consists of bits 8 thru 15. These definitions come in handy when we have to work with data in groups of eight bits, and it's nice to be able to relate words and nibbles to bytes. SIGNED INTEGERS. On many occasions a range of zero to (2n - 1) is simply not enough. To represent values larger than (2n - 1) all we need to do is add additional bits to our bit string and the range of our numbers is increased proportionately. But sometimes we need to be able to represent numbers less than zero. Unfortunately, this cannot be accomplished with the number system we have described so far. In order to represent negative numbers we must abandon the binary numbering system we have created and devise a new numbering system that includes negative numbers. While many numbering systems exist that allow negative numbers, we are forced to use the so-called two's complement numbering system. This choice has to be made because of the 6502 arithmetic hardware. The two's complement system uses the following conventions: 1) The standard binary format is used 2) The high-order bit of a given binary number is assumed to be the sign bit. If this bit is set, the number is negative. If this bit is clear, the number is positive. 3) If the number is positive, its form is identical to the standard binary format. 4) If the number is negative, it is stored in the two's complement format. The two's complement format is achieved by taking a posi- tive number, inverting all the bits (that is, if a bit is zero change it to one; if a bit is one change it to zero), and then adding one to the inverted result. For example, given that the positive 16-bit representation for two is: 0000000000000010 2-11 ***************************************************************** then the two's complement of two (i.e, minus two) is computed by inverting all the bits: 1111111111111101 and adding one to the inverted result: 1111111111111110 Therefore, 1111111111111110 is the two's complement repre- sentation for minus two. The two's complement operation, also called negation, can be thought of as a multiplication by minus one. In fact, if you take the two's complement of a negative num- ber, you wind up with its positive counterpart. Consider minus two: 1111111111111110 To take the two's complement of minus two, we first invert all the bits: 0000000000000001 Next, one is added to the result so that we obtain: 0000000000000010 which is the binary representation for two! Why even bother with such a weird format? After all, it's probably much simpler to just use the high-order standard binary format. Well, a simple addition problem may help clear things up. Consider the addition of two plus minus two. 0000000000000010 1111111111111110 ---------------- 0000000000000000 carry = 1 Note that if we ignore the carry out of bit #15, we wind up with a zero result, exactly what we expect. It is easy to prove to ones self by the use of examples that if the carry is ignored, the result is always what one would expect. If the carry out of the sixteenth bit is meaningless, how does one detect an overflow? If the sign bit is treated as a separate entity from the rest of the number, bit #14 is technically the high- order bit. A carry out of this bit will be what we test for to determine two's complement overflow. 2-12 ***************************************************************** HEXADECIMAL NUMBERS. Binary numbers are fine for examples. But when used for conveying information to people, they tend to be too bulky. Can you imagine having to write out one hundred 16-bit numbers in binary? Or having to read them? Several years ago programmers began using the octal (base eight) numbering system to compact the large binary numbers. With the octal system it is possible to cram 16 bits of information into six digits. The octal numbering system is still popular on several minicomputers today. When microcomputers came along, manufacturers switched to the hex- adecimal numbering system which made it possible to get 16 bits of information into only four digits! The only drawback to the hex- adecimal numbering system is that most people are not familiar with it. The hexadecimal system (base 16) contains 16 distinct digits. The first ten digits are the familar numeric characters 0 thru 9 and the last six digits are the alphabetic characters A thru F. Hexadecimal numbers have the values: BINARY DECIMAL IEXADECIMAL 0000 0 0 0001 1 1 0010 2 2 0011 3 3 0100 4 4 0101 5 5 0110 6 6 0111 7 7 1000 8 8 1001 9 9 1010 10 A 1011 11 B 1100 12 C 1101 13 D 1110 14 E 1111 15 F Why all the fuss over hexadecimal numbers (or hex numbers as they are usually referred to)? They are easy to convert to binary and vice versa. Decimal numbers, unfortunately, are not as easy to use. For example, 11111100 is not easily converted to 252 decimal, but it is a trival matter to convert it to the hex- adecimal number FC. Clear as mud, right? It's actually quite sim- ple once you learn one little trick. In order to convert a binary number to a hexadecimal number you must first adjust the binary number so that it contains the number of bits which are a multiple 2-13 ***************************************************************** of four (four, eight, twelve, sixteen, etc.). You accomplish this by adding leading zeros to the left of the binary number. Next, you start from the right and divide the bit string into groups of four bits each. Look up each of these "quadruples" in the chart above and replace them with the corresponding hexadecimal value. In the previous example, 11111100 is split up into two groups of four bits yielding 11111100. Looking up 1111 in the chart yields the hexadecimal digit "F". The binary number 1100 corresponds to the hexadecimal digit "C". Going in the other direction, converting hexadecimal to binary, is just as easy. Simply look up the binary equivalent of each hex- adecimal character in a hex string and substitute the binary value. Don't forget to include leading zeros in the middle of a hex string. For example, EFC4 converts to 1110 1111 1110 0100. Although hexadecimal numbers may seem cumbersome to the new programmer, they are in fact a great convenience. RADIX AND OTHER NASTY DISEASES. Now we have decimal, binary, and hexadecimal numbers. If you were to find "100" printed somewhere, how would you be able to tell which base, or "radix," the number is represented in? Does "100" mean 100 base two (ie., decimal four), 100 base 10 (i.e., one hundred), or "100" hex (i.e., 256 decimal)? To avoid confusion the radix is usually specified by some leading character. If a number is prefaced by a percent sign the number will be considered to be a binary number. If the number is preceded by a dollar sign the number will be assumed to be hexadecimal. A exclaimation point is used to denote a decimal number. Decimal numbers may also appear without a radix prefix, so if a string of digits appears without a leading radix character the decimal number system is assumed. The use of the radix prefix prevents ambiguity. ASCII CHARACTER SET. As has been continually pointed out, binary values may be used to represent values other than numeric quantities. A com- puter is required to handle text consisting of alphabetic charac- 2-14 ***************************************************************** ters, numeric characters, and several punctuation symbols as often as it must perform numeric manipulation. Since character manipulation is very important, we must define a character set, that is, a set of unique binary values for each of the valid char- acters we wish to represent. As you may remember, it requires a minimum of five bits (or 32 distinct values) to represent the characters of the alphabet. When you add to that the numeric characters 0 thru 9, it becomes apparent that six bits are going to be required. When you add the lowercase letters and several punctuation characters, the number of required characters jumps to 96. Finally, by adding several "device-control" characters such as return, cursor control, tab, the total jumps to 128 characters. To represent 128 different values requires seven bits. To allow other special characters (such as inverted or blinking characters) another bit will be used to bring the bit total to eight bits, yielding a maximum of 256 distinct characters. Now the only problem that remains is to assign these 256 different characters a unique 8-bit code. Rather than create our own character code, we will use the American Standard Code for Information Interchange (ASCII) character set. The ASCII char- acter set is used by almost all computer manufacturers. Even IBM, which has used its own character set since the early sixties, has finally started using ASCII characters in some of its equip- ment. The first 32 values in the ASCII character set are the so- called control codes. These include carriage return, line feed, backspace, tab, and several other non-printing characters reserved for device control use. The next 32 characters are reserved for the often used punctuation characters (such as period, comma, space) and the numeric characters. The following 32 characters are reserved for the uppercase letters and some infrequently used punctuation characters. The final 32 values in the ASCII character set are reserved for the lowercase letters and some little-used punctuation characters. ASCII does not define the final 128 characters in the char- acter set. These are user-definable characters. On the Apple II, the remaining characters comprise the inverted and blinking char- acter set. For a full description of the Apple/ASCII character set, see Appendix A. 2-15 ***************************************************************** USING BIT STRINGS TO REPRESENT INSTRUCTIONS. Until now we have assumed that bit strings are used only to represent data of some type. This is not always the case. A bit string can also be used to represent a command. Imagine, if you will, a small subset of the commands humans obey every day. One command might be the alarm clock ringing in the morning, causing you to get out of bed. A second command might be, "Get dressed." A third command could be, "Drive to work." A fourth command could be, "Perform all actions required at work." Another command could be, "Drive home from work." And a last command could be, "Go to bed." To represent these six commands we need three bits. The commands could be assigned as follows: bit string command 000 Get out of bed. 001 Get ready for work. 010 Drive to work. 011 Perform required duties. 100 Drive home from work. 101 Go to bed. With these simple commands the apparent actions of a human being can be performed. Each command will be assumed to be given sequentially. This does not mean numerically (i.e, in the order given above), but rather it means that the human exe- cutes one instruction at a time. Although it may not make much sense, it is perfectly valid to give the commands out of numerical order. For example, suppose the person drove to work and then realized that he left something at home which was required to perform his job-related duties. This situation would require the instruction sequence: 000 Get out of bed. 001 Get ready for work. 010 Drive to work. 100 Drive home and pick up forgotten items. 010 Drive back to work. 011 Perform required duties. 100 Drive home from work. 101 Go to bed. Obviously, several other schemes are possible with some yielding weird results. Commanding objects other than human 2-16 ***************************************************************** beings is also possible. Examples include automated machinery, programmable toys, and, of course, the computer. The fact that commands can be represented as bit strings is the whole basis for the computer programming to be studied in the following chap- ters. 2-17 ***************************************************************** CHAPTER 3 REGISTERS, INSTRUCTION FORMATS, AND ADDRESSING GENERAL. Up until now, our discussion of data types has been, for the most part, unrestricted. Unfortunately, in the "real" world of com- puters several restrictions apply which limit the size and feasibility of the operation we wish to perform. In order to be able to write good programs the user must first learn the limitations, and advantages, of the APPLE II computer. The APPLE II computer consists of three major parts: 1) Central Processing Unit (6502 Microprocessor) 2) Input/Output (Keyboard, Video Display, Disk, Etc.) 3) Memory Memory in the APPLE II computer is arranged as 65,536 8- bit bytes. Each byte is individually addressable; that is, if we want to, we can perform our data operation on any of the 65,536 loca- tions available to us. Several of these locations (5120 in fact) are specifically reserved for Input/Output (I/O) purposes 1024 of these locations comprise the screen memory, and storing data in any of them (located from $400 thru $7FF in memory) is likely to affect the video display. Another 4K (4096) of these memory locations is reserved for use by the peripheral cards which plug into your Apple. The remaining 59K bytes (ie, 60,416 bytes) are used to hold variables, your program, BASIC, Pascal, etc. Typically, the user has 48K at his disposal for program storage (minus any language requirements such as DOS, etc.). The Central Processing Unit (CPU) is where all the action takes place. The CPU is the "brains" behind the computer. Data 3-1 ***************************************************************** is transferred to and from memory and I/O devices, arithmetic is performed, comparisons are made, etc., within the CPU. So, the CPU will function as a "middleman" in most of our operations. Let's define the 6502 microprocessor. Internally the 6502 microprocessor consists of an Arithmetic/Logical Unit (ALU) where additions, subtractions, etc., take place, a control unit 3-2 ***************************************************************** which moves data to and from memory, decodes the instructions, and accesses six special memory locations called, registers. Five of these registers are 8 bits wide (just like our memory) and one of them is 16 bits wide (the same as the 6502 address bus). These six registers each serve a special purpose, therefore they have been given special names as follows: 1) Accumulator (A or ACC) 2) X-register (X) 3) Y-register (Y) 4) Stack Pointer (SP) 5) Program Status Word (P or PSW) 6) Program Counter (PC) A separate description of each register is given in the fol- lowing paragraphs: ACCUMULATOR (A or ACC). The accumulator is where most of the data transactions occur. Numbers are added and subtracted here. Data transfer from memory location to memory location usually goes through the accumulator. All logical operations occur in the accumulator. For most of our purposes, the accumulator will be the general purpose register that we utilize. X-REGISTER (X). The X-register in the 6502 is a special purpose register. We cannot add or subtract numbers with it, however the X-register is used for accessing elements of simple arrays, strings, pointers, etc. Using the X-register to access elements of an array is called "indexing." Often, the X-register is called the X-index register. We will discuss indexing later in the text. Y-REGISTER (Y). The Y-register, identical to the X-register, is reserved for indexing purposes. Two different index registers allow us to per- form such functions as substring, concatenation, and other array functions. 3-3 ***************************************************************** STACK POINTER (SP). The Stack Pointer is another special purpose register in the 6502. It is used when calling subroutines and returning from sub- routines, as well as when saving temporary data. Since it is 8 bits wide, the stack pointer can only be used to address 256 different locations in the 6502 address space. These 256 locations occur from location $100 to location $1FF. NOTE Since locations $100 thru $1FF are reserved for the Stack Pointer register, NEVER use these locations for data or program storage. PROGRAM STATUS WORD (P or PSW). The program status word (also called the processor status register) is not a register in the true sense of the word. It is simply a convenient collection of seven status bits which will be used by such things as conditional branches (to be described later). PROGRAM COUNTER (PC). The program counter is a register used by the computer to point to the instruction currently being executed. This register is unique in that it is the only 16-bit register on the 6502. It is 16 bits wide since 16 bits are required to access the 65,536 different locations (the address space) on the 6502. INSTRUCTION FORMAT (6502). Thus far we have discussed the ways computers store data and where the data is manipulated (i.e., the registers). We have not discussed how we tell the computer what to do with this data. A computer instruction is used to tell the 6502 which operation to perform. What is an instruction? An instruction is simply another 8-bit code stored in memory. Since each instruction is 8 bits wide there is a maximum of 256 possible instructions. In the 6502, however, there are only about 120 actual instructions. The instruc- tion codes corresponding to these 110 to 120 instructions are called valid instruction codes, or valid opcodes. The remaining 3-4 ***************************************************************** 136 to 146 invalid instructor codes are referred to as the invalid instruction codes, or invalid/illegal opcodes. The opcodes (computer instructions) are stored in memory in a manner identical to date. How then does the computer dif- ferentiate between data and instructions? Clearly, the meaning of a byte in memory is very context-dependant. A byte in memory is assumed to be a computer instruction if the program counter is ever allowed to "point" at (i.e., contain the address of) that particular byte in memory. Also, programs are assumed to be stored sequentially in memory (with some exceptions). That is, the second instruction immediately follows the first instruction, the third instruction follows the second, etc. EXAMPLE: MEMORY 1st INSTRUCTION <- PROGRAM COUNTER 2nd INSTRUCTION 3rd INSTRUCTION 4th INSTRUCTION 5th INSTRUCTION The program counter is loaded with the address of the first instruction. The processor loads and then executes this instruc- 3-5 ***************************************************************** tion. The program counter is then incremented by one so that it points to the second instruction. This instruction is fetched and the cycle is repeated. Always remember that the computer cannot tell the differ- ence between data and instructions. Whatever the program pointer points to will be interpreted as an instruction. TWO AND 3-BYTE INSTRUCTIONS. Many instructions require more than one byte. For instance, suppose we want to load the accumulator with the 8-bit constant $FF. The 6502 has an instruction which will load the accumulator with an 8-bit constant. The only problem is how do you specify the constant? Why not immediately follow the instruction with the constant! Well, this is exactly what's done. The hex code $A9, when executed, tells the 6502 to load the accumulator with the 8-bit constant located in the next byte, so the two bytes ($A9, $FF) instruct the 6502 to load the accumulator with the constant $FF. Loading the accumulator with a constant (or load the accu- mulator immediate, as it's often called) is an example of a 2-byte instruction. Rather than using just one byte to perform the oper- ation, we need two. Naturally, the program counter is incremented by two instead of one so that the constant does not get executed as the next 6502 instruction. 3-6 ***************************************************************** In addition to the 2-byte instructions, there are also 3-byte instructions. One good example is the "store the accumulator in an absolute memory location" instruction. This instruction (which consists of $8D followed by a 16-bit address) will store the con- tents of the accumulator at any of the 65,536 different memory locations available in the 6502 memory space. For example, ($8D, $00, $10) will store the accumulator at location $1000, and ($8D, $C3, $48) will store the accumulator at location $48C3. Remember, whenever a multibyte instruction is encoun- tered, the program counter is automatically incremented past the additional data. EXAMPLE: A9 INSTRUCTION #1 LOAD ACC WIUH $FF FF 8D INSTRUCTION #2 STORE ACC AT LOCATION $1234 34 12 -- ETC. -- -- WARNING Remember, there is nothing sacred about the location of your program instructions. The computer cannot differentiate between data and valid instructions. In the previous example, if the pro- gram began at location $1234 we would have loaded the accu- 3-7 ***************************************************************** mulator with $FF and then proceeded to destroy the first instruc- tion ($A9 stored at location $1234) by storing a $FF over the top of the $A9, leaving you with the following code: LOC DATA/CODE 1234 FF 1235 FF 1236 8D 1237 34 1238 12 1239 -- ETC. ETC. With this in mind, be very careful where you store data since you can easily wipe out your program if you are not careful. 6502 ADDRESSING MODES. The 6502 microprocessor utilizes 56 distinct instructions. Previously it was said that there are about 120 different instruction codes. Why the difference? Well some operations can be carried out in one of several ways. For instance, one type of operation on the 6502 is that of loading the accumulator with an 8-bit value. The operation is called, "the load the accumulator operation" and is often abbreviated LDA. There are several LDA instructions. You can load the accumulator with a constant, load the accumulator with the value contained in one of the 65,536 memory locations, load the accumulator with an element of an array or string, etc. All of these operations have one thing in common- the end result is that the 6502 accumulator is loaded with a new value. Although the operation is the same (loading the accumulator) the method used to load it is different. Since it is a different operation (so to speak) on a very low level, the 6502 uses a different opcode for each variance of the LDA instruction. These variances on the LDA instruction are often called, "addressing modes." Whereas an instruction tells the computer what to do, the addressing mode tells the computer where to get the data (or operand). IMMEDIATE ADDRESSING MODE. The immediate addressing mode tells the computer that the data to be used is an 8-bit constant, which immediately follows the instruction code. Remember, the $A9 in one of the previous 3-8 ***************************************************************** examples, $A9 says, "Load the accumulator with the value con- tained in the following byte." This is an example of the immediate addressing mode. The instruction could be worded as, "Load the accumulator with the byte immediately following the instruction byte." With this wording the term "immediate addressing mode" makes a little more sense. Instructions using the immediate addressing mode are always two bytes long: one byte for the instruction and one byte for the immediate data. ABSOLUTE ADDRESSING MODE. Sometimes, rather than loading the accumulator with a con- stant, we need to be able to load the accumulator with a variable that is stored in memory. As with the immediate addressing mode we need one byte to specify the instruction (LDA or load the accumulator). Next, to be able to uniquely specify one of the 65,536 different locations in the 6502 address space, we need a 2-byte address. This type of addressing mode is called, "absolute addressing mode" (since we are loading the accumulator from an absolute memory location). Obviously this instruction must be three bytes long: one byte for the instruction and two bytes for the address. The actual instruction code for the LDA absolute instruc- tion is $AD. This instruction code is always followed by a 2-byte address; the low-order byte comes first followed by the high-order byte. If we wanted to instruct the 6502 to load the accumulator from memory location $1234, the code sequence to do this would be: ($AD, $34, $12 or AD3412). Yes, it does look funny seeing the 34 before the 12, but get used to it. You will see this (byte- reversed order) used all the time on the 6502. ZERO PAGE ADDRESSING MODE. The 6502 incorporates a special form of the absolute addressing mode known as the "zero page addressing mode." In this addressing mode the 6502 loads the accumulator from the specified memory location, just like the absolute addressing mode. The only difference is that the instruction is only two bytes long: one byte for the instruction and one byte for the address. 3-9 ***************************************************************** Since eight bits only allow 256 different values you are limited to 256 different addresses. In the 6502 address space this corre- sponds to the first 256 locations in the machine (location $00 to location $FF, also known as 'page zero'). Since the zero page addressing mode strongly restricts its usage (you're only allowed to access 1/256th the amount of data possible with the absolute addressing mode), why should you even bother using it? The first part of the answer should be obvious. The absolute addressing mode results in 3-byte instructions whereas the zero page addressing mode uses only two bytes. You save memory by using the zero page addressing mode. The second, and less obvious, reason is that instructions using the zero page addressing mode execute faster than instructions using the absolute addressing mode. Page zero is often used for variable storage, and the other memory locations are often used for program, array, and string storage. INDEXED ADDRESSING MODE. As mentioned previously, the X- and Y-registers are used as index registers. An index register is used to access elements of a small array or a string. Remember, in integer BASIC, when you use an array you specify the element of the array by placing an "index" within parentheses after the variable name (e.g., M(I): I is the index). The X- and Y-registers are used in place of the variable I (or whatever you happen to be using). For instance, the instruction code $BD tells the 6502 to load the accumulator from the absolute memory location specified in the next two bytes AFTER the contents of the X-register are added to this value. If the computer executes the instruction sequence BD 34 12, and the X-register contains 5, then the accumulator will not be loaded from location 1234, but rather from location 1239 (1234 + 5). In general, if you have an array (containing less than 256 elements) you can access any element of this array by loading the X-register with the desired value and then loading the accumulator from the first element of the array indexed by X. NOTE The Y-register can be used in an identical manner. Naturally, the instruction code is changed, but the effect is the same. 3-10 ***************************************************************** INDIRECT ADDRESSING. The indirect addressing mode is rather tricky. Rather than using the 2-byte address which follows the instruction, we go to the address specified and use the data contained in that location as the low-order byte of the actual address. To get the high-order byte of the actual address we must add one to the 16-bit value following the instruction and go to that address which will contain the high-order byte of the actual address. Now that a low- and high-order byte are obtained, the address is fully specified, and we can continue on our merry way. Yes, this description is worth- less and you do need several examples to demonstrate how indirect addressing is used. Rather than give these examples now, their presentation will be deferred until the addressing mode is actually used in a program. INDIRECT INDEXED BY Y As with the indirect addressing mode, the indirect indexed by Y mode is mentioned solely for completeness. A full discussion will be presented later in the text. INDEXED BY X, INDIRECT. Again, this discussion must be deferred. IMPLIED ADDRESSING MODE. The implied addressing mode means exactly that--the instruction itself implies what type of data is to be operated on. Instructions that use the implied addressing mode are always one byte long. ACCUMULATOR ADDRESSING MODE. The accumulator addressing mode specifies an operation upon the accumulator. The instructions in this class are all one byte long. 3-11 ***************************************************************** NOTE It may seem that many of the operations in the 6502 should be considered in the class of accumulator addressing mode instruc- tions. The difference between the true accumulator addressing mode instructions and the other instructions is that the accumu- lator addressing mode instructions reference only the accumu- lator. They do not require any operands in memory. RELATIVE ADDRESSING MODE. The relative addressing mode is used by a group of instruc- tions known as the branch instructions. The description of the relative addressing mode is beyond the scope of this chapter and will be considered in a later chapter. Once again it is mentioned solely for sake of completeness. ADDRESSING MODE WRAP-UP. If this discussion of addressing modes doesn't make much sense, don't worry about it. This section was intended only as a crude introduction to make you aware of the fact that addressing modes do indeed exist. The use of a particular addressing mode will become obvious in the next few chapters. 3-12 ***************************************************************** CHAPTER 4 SOME SIMPLE INSTRUCTIONS NEW INSTRUCTIONS: EQU EPZ DFS LDA LDX LDY STA STX STY INC DEC TAX TAY TXA TYA INX INY DEX DEY GENERAL. Until now, everytime we wanted the computer to perform some action, we pulled a magic little number out of the hat and used it as an instruction code. Unfortunately, there are about 120 different instruction codes. Trying to memorize all of these would be mind boggling. It would certainly be quite a bit nicer if we could use phrases like, "load the accumulator with the constant $FF," or "store the contents of the accumulator at location $1234." This idea was so good that several people have indeed done this. LISA is an example of a computer program that takes phrases (such as LDA for load the accumulator) and converts them to one of the 120 or so valid instruction codes. Programs which do this for you are called, "assemblers." Rather than using long phrases, such as "load the accumulator", short mnemonics were chosen instead. Mnemonics are three-character representations of the desired phrases. For instance, LDA replaces "load the accumu- lator," and STA replaces "store the accumulator." Although you must take the time to learn these mnemonics, the payoff is rather good. When entering a program, you will only have to type three letters instead of an entire phrase! ASSEMBLY LANGUAGE SOURCE FORMAT. The actual machine language code that the 6502 under- stands is often called, "object code." The mnemonics that 4-1 ***************************************************************** humans understand are often called, the text file, or source code. Unlike BASIC, which has few restrictions concerning the arrangement of statements on a line, assembly language has a very rigid format. An assembly language source statement is divided into four sections known as "fields." There is a label field, a mnemonic field, an operand field, and a comment field. Fields in an assembler are usually separated by at least one blank; often two or three of these fields are optional. SOURCE FORMAT: LABEL MNEMONIC OPERAND ;COMMENTS The label field contains a label that is associated with the particular source line. This is very similar to the line number in BASIC. All branches and jumps (a GOTO in BASIC) will refer to this label. Unlike BASIC, this label is not a number, but rather a string, usually one to eight characters long beginning with an uppercase alphabetic character. Labels should only contain uppercase characters and digits. EXAMPLES OF VALID LABELS: LABEL LOO1 A MONKEY EXAMPLES OF INVALID LABELS: 1HOLD (BEGINS WITH "1") HELLOTHERE (LONGER THAN 6 CHARS) LBL,X (CONTAINS ",") Labels are not required on every line like line numbers in BASIC. Labels are only required when you need to access a 4-2 ***************************************************************** particular statement. Labels must begin in column one of the source line, and there must be a blank between the label and the following mnemonic. As previously mentioned, labels are optional. If you do not wish to enter a label on the current line you must be sure that column one of the source line contains a blank (see the LISA documentation for furthur details on labels). MNEMONIC FIELD. The mnemonic field follows the label field. A three-character LISA mnemonic is expected in this field. These include instruc- tions such as LDA, LDX, LDY, STA, ... OPERAND FIELD. The operand field follows the mnemonics field. The operand field contains the address and the addressing mode, if required. If an address appears all by itself the absolute (or zero page, if possible) addressing mode will be used. This address can be an "address expression." An address expression is similar to an arithmetic expression one would find in Integer BASIC except that only addition and subtraction are allowed. (Some versions of LISA allow other operators as well.) For instance, $1000 + $1 will return the value $1001. If you had an instruction of the form "LDA $1000+$1" (LDA stands for load the accumulator), the accu- mulator would be loaded from the contents of memory location $1001. The discussion of address expressions will be considered in greater detail later in the text. To specify a constant (the immediate addressing mode), you must preceed a 16-bit address expression with either a "#" or a "/". If you use the "#", the low-order byte of the address expression will be used. If you use the "/", the high-order byte of the address expression will be used as the 8-bit immediate data. The indexed addressing modes are specified by following an address expression with ",X" or ",Y" depending on whether you wish to use indexed by X or indexed by Y addressing. If possible, the zero page form will be used. To specify the implied addressing mode, or the accumulator addressing mode, you must leave the operand field blank. Any- 4-3 ***************************************************************** thing but a comment (see the next section) will produce an error. The syntax for the indirect, indirect indexed by Y, and indexed by X indirect will be considered later. COMMENT FIELD. Following the operand field you can optionally place a remark on the same line as the instruction. Just make sure that the operand field and the comment field are separated by at least one blank, and also make sure that your comment begins with the special character ";" (semicolon). INTRODUCTION TO REAL INSTRUCTIONS. So far, we've only discussed assembly language in a very general way, making use of relatively few concrete examples. Now, let's focus our attention on some of the real commands at our disposal. LOAD GROUP. There are three distinct instructions in the load group cate- gory. They are: LDA (load accumulator), LDX (load the X-regis- ter), and LDY (load the Y-register). These instructions go to the location specified in the operand field, make a copy of the data stored there, and then enter this data into the specified register. To load the accumulator with the data (contained in one of the 6502's 65,536 different memory locations) simply follow the LDA instruction with the address of the desired memory cell. LDA $1FA0 -- LOADS ACC FROM LOCATION $1FA0. Note that the content of the specified memory location is not altered. A copy is made and placed in the accumulator; the mem- ory location's data is not altered. In general, LDA $nnnn (where nnnn is a one to four digit hex number) will load the accumulator from location $nnnn. Examples: LDA $11F0 - LOADS THE ACC FROM LOCATION $11F0 LDA $127F - LOADS THE ACC FROM LOCATION $127F LDA $0 - LOADS THE ACC FROM LOCATION $0000 4-4 ***************************************************************** Loading a constant into the accumulator is just as easy. Simply preceed the constant with the "#" or the "/" depending on whether you wish to load the low-order eight 8 bits or the high- order eight bits of the 16-bit expression given in the operand field. Examples: LDA #$1000 - Loads the ACC with the value $00 ($00 is the low-order byte of $1000, the high-order byte, $10, is ignored). LDA #$FF - Loads the ACC with the value $FF. $FF is really $00FF. The high-order byte in this case is $00, once again, it is ignored. LDA #$0 - Loads the ACC with the value $0. $0 is really $0000, whose low- order (as well as high-order byte) is zero. LDA /$1000 - Loads the ACC with $10. $10 is the high-order byte of the value $1000. The low-order byte ($00) is ignored. LDA /$FF - Loads the ACC with $00. $FF is really $00FF whose high-order byte is $00. The low-order byte ($FF) is ignored. LDA /$0 - Loads the ACC with $00. Both the low- and high-order bytes of $0 (same as $0000) are $00. In all of the previous examples, the operation performed was that of loading the accumulator. You can load the X-register or the Y-register in a similar manner simply by substituting the LDX (Load the X-register) or the LDY (Load the Y-register) instruction in place of the LDA instruction. There are several other methods used in loading registers (i.e, different addressing modes) other than the ABSOLUTE and IMMEDIATE addressing modes described here. These methods will be considered in later chapters. STORE INSTRUCTIONS. Now that we can move data into the accumulator, let's dis- cuss how to store data from the accumulator, X- or Y-register into external memory. The 6502 store instructions provide us with this 4-5 ***************************************************************** capability. There are three store instructions: STA (store the accumulator), STX (store the X-register), and STY (store the Y- register). To store the register information at some memory loca- tion simply follow the store instruction with the address of the desired storage location. Examples: STA $1000 - Stores a copy of the contents of the accumulator at location $1000. The contents of the ACC are not disturbed . STA $2563 - Stores the contents of the accumulator at location $2563. STA $FF - Stores the contents of the accumulator at location $FF. By using the STX and STY instructions, we can store data from the X- and Y-registers in a similar manner. STX $1500 - Stores a copy the contents of the X-register at location $1500 in memory. STY $220 - Stores the contents of the Y-register at location $220 in memory. Remember, the store instructions do not alter the contents of the register being stored; only the memory location where the data is being stored. Now that we know a few basic commands, let's write a simple assembly language program. This program will simply transfer the data contained in locations $1000 and $1001 to locations $2000 and $2001 respectively. After this program is executed, location $2000 and $1000 will contain the same value and loca- tions $1001 and $2001 will contain the same value. The (seem- ingly) easiest way to do this is to execute an instruction sequence "LET $2000 EQUAL $1000, and LET $2001 EQUAL $1001." Unfortunately, there is no memory transfer function which will perform this task for us. What we can do, however, is to perform this action in an indirect manner. Instead of a straight memory transfer, we can load the accumulator with the data contained in location $1000 and then store the accumulator at location $2000. 4-6 ***************************************************************** This process can then be repeated for locations $1001 and $2001. The final assembly language program is: LDA $1000 STA $2000 LDA $1001 STA $2001 All data transfers must be routed through one of the 6502 registers, and generally we will use the accumulator since it is the general purpose register on the 6502. DATA TRANSFER INSTRUCTIONS. Now that we know how to exchange data between a register and memory, what about transfering data between registers? The 6502 has the capability to transfer data from the accumulator to the X- or Y-registers and likewise from the X- or Y-register to the accumulator. There are also two instructions which allow the 6502 to transfer data from the X-register to the Stack Pointer and to transfer data from the Stack Pointer to the X-register. The mne- monics for these instructions are: TXA - Transfers data from the X-register to ACC. TYA - Transfers data from the Y-register to ACC. TAX - Transfers data from the ACC to the X-register. TAY - Transfers data from the ACC to the Y-register. TXS - Transfers data from the X-register to SP. TSX - Transfers data from SF to the X-register. You will notice that there are no explicit instructions for trans- fering data from the X-register directly to the Y-register and vice versa. Should this need arise two instruction sequences can be used: Transfer X to Y Transfer Y to X TXA TYA TAY TAX - OR - - OR - STX $nnnn STY $nnnn LDY $nnnn LDX $nnnn 4-7 ***************************************************************** The register transfer instructions require no operands. In fact, if an attempt is made to place an operand after the transfer mnemonic, an error message will be displayed. This is an exam- ple of the 'implied' addressing mode. The instruction itself implic- itly defines the location of the data being operated upon (the registers). REGISTER INCREMENTS AND DECREMENTS. Being able to load and store data is not particularly inter- esting by itself, so now we are going to discuss some instructions which operate on the data in a register. The first four instructions we will study in this category are the X- and Y-register increment and decrement instructions (increment means to add one; dec- rement means to subtract one). INX - Takes the value contained in the X-register, adds one, and leaves the result in the X-register. INY - Takes the value in the Y-register, adds one, and leaves the result in the Y-register. DEX - Takes the value contained in the X-register, subtracts one, and leaves the result in the X-register. DEY - Takes the value in the Y-register, subtracts one, and leaves the result in the Y-register. The above instructions are handy for simple register arith- metic. Since (as you will soon find out) most of the time we are adding one to or subtracting one from these registers, the incre- ment and decrement instructions are very useful. There is one slight problem with the increment and decre- ment register instructions. What happens when you try to incre- ment a register which contains $FF (the maximum value possible for an 8-bit register) or decrement $00 (the smallest value pos- sible for an 8-bit register)? When a register containing $FF is incremented, the computer will "wrap-around" and end up with the value $00. Likewise, whenever you decrement the value $00 you will end up with $FF. 4-8 ***************************************************************** Like the transfer instructions, INX, INY, DEX, and DEY are implied addressing mode instructions and require no operands. INCREMENT AND DECREMENT INSTRUCTIONS. The increment (INC) and decrement (DEC) instructions are special. They operate directly on memory without the need to go through the accumulator, X-, or Y-registers as an intermediate step. These two instructions increment and decrement values at specified memory locations. EXAMPLES: INC $2255 - Takes the value at location $2255, adds one, and then leaves the result at location $2255 DEC $15 - Takes the value contained at location $15, subtracts one, and then leaves the result in location $15. The INC and DEC instructions are not implied addressing mode instructions. They require an absolute or zero page address, like the load and store instructions. Keep in mind, you are limited to eight bits; as such, "wrap-around" will occur if you attempt to increment $FF or decrement $00. LABELS AND VARIABLES. Until now, everytime we wanted to use a variable, the actual memory address of that variable had to be specified. This is inconvenient (This situation is similar to using all POKE instruc- tions instead of variable names in BASIC). For instance, suppose we have a value giving the X-coordinate of a point we wish to plot on the screen. XCOORD would be much more meaningful than $800. It would be nice to be able to write LDA XCOORD instead of 'LDA $800.' Labels allow us to do exactly this! Somewhere in our program we define a label to be equal to some value (an address). Thereafter, whenever that label is referenced, the address is used instead. In the previous example you would equate the value $800 with the label XCOORD, then you could write LDA XCOORD, and the assembler would automatically sub- 4-9 ***************************************************************** stitute $800 for you! A label may be used in place of an address. Remember, the assembler simply substitutes the assigned value upon encountering a label. LDA XCOORD does not mean load the accumulator with the value XCOORD, but rather, load the accumulator with the value contained in the memory location $800. Labels allow you to assign more meaningful names to memory locations. There is one catch however. Somewhere within the program you must equate the value with the label. How is this accom- plished? Very simply. To equate a label with an address you use the EQU pseudo opcode. First, what is a pseudo-opcode? A pseudo opcode is simply an instruction to LISA embedded within your assembly language source file. When encountered, a pseudo opcode tells LISA to do something special with the fol- lowing data. A pseudo opcode generally does not emit any instruction code for use by the 6502 microprocessor. The EQU pseudo opcode has the form: LABEL EQU Both the label and the value (an address expression to be described later) are required. The EQU pseudo opcode tells LISA to take the label and store it with its corresponding address value in the assembler symbol table. Later, when you use the label in your program, LISA looks up the label in the symbol table and substitutes the address for the label. The assembler remembers ugly things like addresses for you, and all you have to do is remember which variable name (or label) you used. EXAMPLES OF LABELS: XCOORD EQU $800 - XCOORD is assigned the value $800 LABEL EQU $1000 - LABEL is assigned the value $1000 LDA XCOORD - Same as LDA $800 STA LABEL - Same as STA $1000 CONST EQU $FF22 - CONST is assigned the value $FF22 LDA #CONST - The value $22 is loaded into the acc ($22 is the low order byte of CONST). 4-10 ***************************************************************** LDA /CONST - The value $FF is loaded into the acc ($FF is the high order byte of CONST). INC XCOORD - Increments the value at location $800. DEC LABEL - Decrements the value at location $1000 When a label is defined using the "EQU" pseudo opcode, absolute addressing will always be used (even if the value is less than $FF). In order to use zero page addressing another pseudo opcode (equate to page zero) must be used. This pseudo opcode is EPZ and it has the same syntax as EQU. EXAMPLE: LABEL EPZ must be less than or equal to $FF. Sometimes, when defining a variable, even worrying about where the data should be stored in memory is too much of a bother. It would be nice if one could say, "Hey, I need a one-byte variable, but let LISA worry about its actual location in memory." The DFS (or define storage) pseudo opcode will do exactly that for you. The DFS pseudo opcode uses the syntax: LABEL DFS Unlike EQU the value does not specify where the data is to be stored, but rather how many bytes you wish to reserve for your variable. Usually this value will be one or two. DFS simply uses the current code location as the address for the variable. Because of this you must be careful to place the DFS pseudo opcode in your program where it will not be executed as an instruction. We'll discuss how you do this later on. EXPRESSIONS IN THE OPERAND FIELD. Suppose in our previous example that XCOORD was a 16- bit value located in bytes $800 and $801. How can we access 4-11 ***************************************************************** these locations using our label scheme? LISA allows simple arith- metics to be used in the operand field. The operators "+" and "-" are allowed. EXAMPLE: XCOORD EQU $800 LDA #$0 -CLEAR THE ACCUMULATOR STA XCOORD -CLEAR LOCAUION $800 STA XCOORD+$1 -CLEAR LOCATION $801 Some versions of LISA also allow multiplication, division, and some logical operations in address expressions. For more details consult the LISA reference manual. 4-12 ***************************************************************** CHAPTER 5 ASSEMBLY LANGUAGE NEW INSTRUCTIONS: BRK JMP BCC BCS BEQ BNE BMI CLD BPL BVC BVS BLT BGE BFL BTR SED CMP CPX CPY CLC CLV SEC CLI SEI END GENERAL. The load and store instructions discussed in the previous chapter are examples of sequentially executing instructions. After a load or store is executed, the computer proceeds to the next instruction and continues processing there. As in BASIC, we often need to interrupt this sequential program flow and continue execution elsewhere. Unlike BASIC, we do not have a GOTO, 5-1 ***************************************************************** FOR/NEXT, or IF/THEN instruction at our disposal. In their place the 6502 microprocessor has a group of jump and branch instruc- tions. Finally, we need to be able to tell the computer to stop and return control to the user. There are several methods of achieving this goal. The easiest and most straight-forward method is prob- ably the BRK, or break instruction. When executed, the BRK instruction will beep the bell and relinquish control to the Apple monitor. A nice feature of the BRK instruction is that it prints the contents of the 6502 registers before returning to the monitor. This is a very simple form of output which we will make use of until more sophisticated I/O routines are possible. EXAMPLE PROGRAM. Let's try writing a program using loads and the BRK instruc- tion. First, access LISA (see its accompanying documentation for details) and proceed as follows: 1) When the prompt (!) is displayed, type INS and depress return (CR) key. 2) Response-LISA will display a 1 on the next line. 3) Enter a space, type LDA #$0 and depress CR. 4) Response-LISA will display a 2 on the next line. 5) Enter a space, type LDX #$1 and depress CR. 6) Response-LISA will display a 3 on the next line. 7) Enter a space, type LDY #$2 and depress CR. 8) Response-LISA will display a 4 on the next line. 9) Enter a space, type BRK and depress CR. 10) Response-LISA will display a 5 on the next line. 11) Enter a space, type END and depress CR. The execution of step 11 informs LISA that the end of the program has been reached. 12) Response-LISA will display a 6 on the next line. Since you have completed source code entry: 13) Type a control-E as the first character of line six and depress the return key. 14) Response--The ! prompt will be displayed on the next line. 5-2 ***************************************************************** If all has gone well the display will appear as indicated below: !INS 1 LDA #$0 2 LDX #$1 3 LDY #$2 4 BRK 5 END 6 LISA is now waiting for your next command. Before any program can be run it must be assembled. To assemble your program, simply type ASM when you get the "!" prompt back. LISA will flash an assembly listing on the screen while the pro- gram is being assembled. Ignore this for now. When you get the "!" prompt back, type BRK (this is a LISA command as well as a 6502 instruction) which will place you in the Apple monitor. To run your program type 800G when you get the monitor '*' prompt character. Immediately after pressing return, the speaker should beep and the screen should look like: 0808- A=00 X=01 Y=02 P=30 S=F0 (The value after "S=" may be different.) The 0808 is the address in memory of the BRK instruction PLUS TWO. This means that the BRK instruction is really located at memory location $806. The reason for having two added to the true value will be dis- cussed in the section on debugging your programs. The next five entries on the line are the values contained in the accumulator, X-register, Y-register, PSW, and stack pointer when the BRK occurred. As mentioned previously, we will use the fact that the BRK instruction prints these registers to perform simple I/O. In essence, the BRK instruction is very similar to the END and STOP instructions in BASIC. JMP INSTRUCTION. The 6502 JMP (jump) instruction is an unconditional branch. It is used in a manner identical to the GOTO instruction in BASIC. The difference is that you specify an absolute memory address instead of a BASIC line number. The following infinite loop con- tinually copies location "J" into location "I" and then sets location 5-3 ***************************************************************** J to zero (obviously, after the first time through, location I will also contain zero). EXAMPLE: PROGRAM LOC. STATEMENT $800 I EQU $0 $800 J EQU $1 $800 LDA J $803 STA I $806 LDA #$0 $808 STA J $80B JMP $800 $80B END (Note that the EQU and END statements do not take up a program location.) The JMP instruction is always three bytes long: the JMP instruction code, followed by the low-order and then high-order byte of the jump to address. Obviously, using absolute addresses, as in the previous example, presents a problem. First, at the time the text file is created, the actual destination address of the JMP instruction is not usually known. To overcome this difficulty we use labels as the destination address of the JMP instruction, much like we used labels in the load and store instructions. Unfortunately, using labels seems to be a matter of simply delaying the inevitable. After all, if a label is used it must be declared using the EQU, 5-4 ***************************************************************** EPZ, or DFS pseudo opcodes, right? Not always. If a label appears on the same line as an instruction, and it begins in col- umn one of the source line, then the address of that instruction will be used as the value equated to the label. The code sequence presented previously can be replaced by: I EQU $0 J EQU $1 LABEL LDA J STA I LDA #$0 STA J JMP LABEL END and the assembler will worry about where LABEL is supposed to be. You will note that this is even easier to use than the GOTO in BASIC because you don't have to worry about using sequential line numbers, especially when you are branching forward. The assembler detects a label on the current line by checking column one. If column one contains an uppercase alphabetic character, the following characters (up to a space or ":") are assumed to be part of the label. You must separate the label and the mnemonic field by at least one space. Also (as mentioned in a previous chapter), if a label does not appear on the current line, there must be a blank in column one. If you do not place a blank in column one, the assembler will treat the mnemonic as a label and attempt to use the operand (if any) as your mnemonic. The result? An illegal mnemonic error most likely, so always remember to place a space in column one if a label does not appear on the current line of text. One final remark: since LISA detects a label by check- ing column one of the current source line, labels such as LDA, LDX, INC, or any other 6502 mnemonic are perfectly valid. For clarity's sake however, you should avoid mnemonic names as statement labels. PROCESSOR STATUS REGISTER (P or PSW). The 6502 instruction set does not include an "IF/THEN" or "FOR/NEXT" instruction. Conditional testing is accomplished by testing bits in the processor status register. The processor status register is unlike the other registers in 5-5 ***************************************************************** the 6502 processor. Rather than being an 8- or 16-bit register whose data is treated as eight or 16 bits of information, the pro- cessor status register is simply a collection of eight bits where each bit is treated separately (actually only seven bits are used; one of the bits is ignored). Four of these bits are set by the result of the previous instruc- tion. For instance, there is a zero flag in the P-register which is set if the last result was a zero and reset otherwise. Two of the remaining flags are explicitly set or cleared by 6502 instructions, and one of the flags is set if the last interrupt serviced was due to the execution of the BRK instruction. BREAK FLAG (B). The break flag (bit number four in the processor status reg- ister) is set only if the last interrupt detected was due to the execution of the BRK instruction. You will notice that whenever you execute a break instruction, the P-register is usually dis- played as P=30 (sometimes other values will creep in). If you convert this hex number to binary, you will find that bit number four is always set, because the last instruction executed was a BRK instruction. DECIMAL FLAG (D). The decimal flag (bit number three in the PSW) is set only by the SED (set decimal) instruction. It can be cleared by the CLD (clear decimal flag) instruction. The decimal flag is used to deter- mine what type of arithmetic will be used by the 6502 micropro- cessor. More information will be given on the decimal flag in the next chapter. INTERRUPT DISABLE FLAG (I). Interrupts are beyond the scope of this book. For complete- ness however, it should be mentioned that one of the flags in the processor status register is used to prevent interrupts from occur- ring. This flag (bit number two in the PSW) can be set by the SEI instruction, and it can be cleared with the CLI instruction. The 6502 IRQ line is disabled when the interrupt disable flag is set. 5-6 ***************************************************************** CONDITION CODE FLAGS (N,V,Z,C). The condition code flags are the flags affected by the normal operation of the 6502 microprocessor. The Z (or zero) flag is set when the last operation executed produced a zero result. For instance, loading the accumulator with zero sets the zero flag; decrementing a register when that register previously contained one gives a zero result and, as such, sets the zero flag; incre- menting $FF results in a wrap-around to zero giving a zero result. There are no explicit instructions for setting or clearing the zero flag. If you want to set it, simply load a register with zero. If you want to clear it, simply load a register with a value other than zero. Sneaky trick: If you can't afford to bother the contents of any of the 6502 registers, simply increment a memory location known to contain $FF. Location $FFC3 in the Apple monitor is such a location (both for the old monitor and the new Auto-start ROM). If you issue the instruction "INC $FFC3," the zero flag will be set. Likewise, to reset the zero flag without affecting any of the 6502 registers, simply increment a location which does not con- tain $FF. Location $F800 is a good choice. The Z flag resides in bit number one of the PSW. The 6502 N flag is set if the last result was a negative value. Wait a second! All along we've been saying that there are no negative values in the 6502 registers. Well, if you remember the section on two's complement, we used the high-order bit as a sign flag. If it was set, the number was negative. If it was reset, the number was positive. We will discuss signed arithmetic later. Here it is useful to note that the negative (N) flag will contain whatever was in bit number seven of the previous result. This is sometimes useful in itself, just to be able to check the status of one of the bits in a memory location. The N flag resides in bit number seven of the PSW. As with the zero flag, there are no explicit set or clear instruc- tions associated with the N flag. To set the N flag simply increment any location which contains a value in the range $7F to $FE. The result of such an increment will always be negative i.e., bit number seven of the result will always be one. Location $F804 in the Apple monitor is a good choice. To reset the negative flag simply increment a memory location which contains a value in the range $00 to $7E, or $FF. The result of such an increment is always 5-7 ***************************************************************** positive i.e., bit seven of the result will always be zero (memory location $F800 in the Apple monitor is a good choice). The carry (C) flag in the 6502 microprocessor is affected by additions, subtractions, comparisons, and logical operations. It can also be explicitly set or cleared with the SEC (set carry) and CLC (clear carry) instructions. The carry flag resides in bit number zero of the processor status register. We will return to the discus- sion of the carry flag when the discussion of the aforementioned operations is taken up. The last flag in the processor status register is the overflow (V) flag. This flag is used for signed arithmetic and is affected only by the addition, subtraction, and bit test operators. It can be explicitly cleared with the CLV instruction but there is no "set overflow flag" instruction. This flag resides in bit number six of the processor status word. We will discuss its use when signed arith- metic is considered. The unused bit in the processor status word is bit number five. Usually it contains a one (i.e, it's set), but you are not guar- anteed this. None of the 6502 instructions access this flag. You might try running the following programs noticing their affects on the P-register: PGM1: LDA #$0 BRK END PGM2: LDA #$1 BRK END PGM3: CLC BRK END PGM4: SEC BRK END PGM5: LDA #$80 BRK END PGM6: LDA #$7F BRK END 5-8 ***************************************************************** BRANCH INSTRUCTIONS (6502). Now you know that certain operations affect the flags in the processor status register. Big deal! How does this help us simu- late the "IF/THEN" Statement in BASIC? By themselves the status flags are not very useful in this capacity. Fortunately, the 6502 allows us to test each of the condition code flags with some branch instructions. A branch instruction is very similar to a JMP instruction. Under certain circumstances it causes program flow to continue at a different location. Unlike the JMP instruction, which is an unconditional branch, the branch instructions do not always jump to the specified location. Before a branch is made, one of the flags in the processor status word is tested and, if the test is met, then (and only then) will the branch be taken. Should the test fail, the program continues executing at the next instruction, just like the IF/THEN in BASIC. Using the branch instructions we can test any of the condi- tion code flags to see if they are set or cleared. The allowable branches are: BCC - Branch if the carry flag is clear. BCS - Branch if the carry flag is set. BEQ - Branch if the zero flag is set. BNE - Branch if the zero flag is clear. BMI - Branch if minus (N=1). BPL - Branch if plus (N=0). BVS - Branch if overflow is set (V=1). BVC - Branch if overflow is clear. Just as with the JMP instruction you must specify an address (or label) in the operand field. EXAMPLE: LDA #$0 BEQ LBL1 LBL2 LDA #$FF LBL1 BEQ LBL2 In this example the accumulator is loaded with the value zero. This sets the zero flag which causes the following branch to be taken. At LBL1 there is another branch if equal to zero instruction. Since we have not modified any registers or memory locations, the zero flag has not had a chance to be affected so the branch will be taken. This leads us to LBL2 where we load the accumulator with the value $FF. The next instruction (at loca- 5-9 ***************************************************************** tion LBL1) tests the zero flag. Since the last result obtained was $FF (from the LDA instruction), this branch will not be taken and the program will fall through to the next instruction after LBL1. If you would like to test a memory location to see if it is zero, and increment it if it is zero, you could use the following code: LDA $1F ;GET THE VALUE CONTAINED IN LOCATION $1F BNE LBL ;IF IT IS NOT ZERO BRANCH TO "LBL". INC $1F ;ADD ONE TO THE VALUE AT LOCATION $1F LBL --- ;NEXT INSTRUCTION ETC. LOOPS. One of the more powerful features of a computer is it's ability to repeat a section of code over and over for a specified number of times. This technique is called looping. In BASIC you might use the "FOR/NEXT" loop to accomplish this task. In assembly language there is no "FOR/NEXT" loop so this function has to be synthesized. Possibly the easiest way to synthesize a loop is to load a memory location with an initial value and then decrement the memory location until it becomes zero. By using the BNE instruc- tion you can cause the body of the loop to be executed until the memory location becomes zero. 5-10 ***************************************************************** As an example, suppose you wanted to add 10 to the vari- able J. Since we have not yet discussed addition on the 6502 we will have to use the increment instruction. Since INC only adds one to a memory location, we will have to repeat this instruction 10 times. We could simply type ten INC J instructions in a row, but this would be somewhat inefficient. Instead, let's store 10 in some memory location (e.g. I) and then set up a loop whereby we increment J ten times. The actual program to do this could be: I EQU 0 J EQU 1 LDA #!10 ;INITIALIZE I TO 10 STA I LDA #$0 ;INITIALIZE J TO 0 STA J LP INC J ;NOW, INCREMENT J 10 TIMES DEC I BNE LP LDA J ;LOAD J SO WE CAN DISPLAY IT BRK ;BREAK AND DISPLAY J (IN THE ACC) END A "step size" of minus one is not always convenient, not to mention that we can only end our loop when I becomes zero. To learn how to alleviate this problem, read on... COMPARISONS. Unfortunately, in the real world we need to be able to test other things besides just our condition code flags. For instance, sometimes it would be nice if we could determine whether or not I=5,or possibly if (X=6) AND (J<=(Ix5+2)) OR (L=M). Other times we might want to have a loop with an indexing variable which is initialized to one and is incremented until it becomes some other non-zero value such as 10. In order to perform these types of operations, we will have to use the 6502 compare instruc- tions. The CMP (compare to accumulator) instruction compares the memory operand specified against the accumulator. How is the comparison made? The data in the operand field is subtracted from the accumulator. The PSW flags are set according to the result obtained and then the difference obtained from the sub- traction is discarded. After the compare instruction, both the accumulator and the memory operand contain their original 5-11 ***************************************************************** values. So what good is the CMP instruction if the results are lost? Even though the result of the subtraction is not kept around, the condition code flags are set, depending upon the status of the subtraction. If the contents of the accumulator equals the contents of the specified memory location, the result of the subtraction will be zero. You can then use the BEQ branch instruction immedi- ately after a compare to test for equality (doesn't BEQ, branch if equal, make a little more sense now?). Likewise, if the contents of the accumulator do not equal the data contained in the spec- ified memory location, the zero flag will be reset, and you can use the BNE (branch if not equal) to test for this condition. The N and C flags are affected in a reverse fashion. If the C flag is set or the N flag is clear, the value in the accumulator is greater or equal to the contents of the specified memory locations. If the N flag is set or the C flag is clear, the value in the accu- mulator is less than the contents of the specified memory location. These tests are so useful that two instructions have been added to LISA's repertoire: BGE (for branch if greater than or equal to) and BLT (for branch if less than). These two instructions generate the same machine code as BCS or BCC respectively. Why have two mnemonics which mean the same thing? For the same rea- 5-12 ***************************************************************** son you program in assembly language instead of machine lan- guage: these extra mnemonics are easier to remember when testing for the greater than or equal to and the less than conditions. The overflow flag (V) is not affected by the compare instruc- tion, so the use of the BVC or BVS instructions after a compare is futile. You can also compare the X- and Y-registers against some memory operand by using the CPX and CPY instructions respec- tively. The same condition code flags are set, and you can use the branch instructions to test for the same conditions as with the CMP instruction. One thing you have probably noticed is the lack of BGT (branch if greater than) and BLE (branch if less than or equal) instructions. These instructions are simply not available on the 6502 microprocessor. Even though they are not available as dis- crete instructions, they may be synthesized by using the BEQ, BNE, BLT, and BGE instructions. Suppose you wanted to com- pare I with J and jump to LBL if I is less than or equal to J. This could be accomplished with the following code: LDA I CMP J BLT LBL BEQ LBL If I is less than J, the first branch encountered will be taken; if I is equal to J, the first branch will not be taken, but the second branch will be taken. If I greater than J, then neither branch will be taken, and the program will simply fall through. Testing for the greater than function is only slightly more difficult. To compare I with J and branch to LBL if I is greater than J, you could use the code: LDA I CMP J BEQ EQL BGE LBL EQL --- ETC. In this example I is compared with J. If they are equal, I cannot be greater than J so a branch around the following BGE instruction is made. If I does not equal J, then it can only be less than or greater than J. If I is greater than J, the branch to location 5-13 ***************************************************************** LBL will be taken; if I is less than J, the program will simply fall through to the instruction at location EQL. More efficient methods of simulating the BGT and BLE instructions will be considered later. IF/THEN STATEMENT SIMULATION. The IF/THEN statement in BASIC (or Pascal for that matter) has the form: IF THEN where gets executed if and only if the logical expression is TRUE. For instance, the BASIC statement IF X>= 7 THEN Y=0 would set Y to zero if and only if X is currently greater than or equal to seven. To simulate the IF statement in assembly language you would use the opposite type branch to jump around the statement to be executed. As an example, if you wanted to convert the previous BASIC statement to assembly language, you would use the code sequence: LDA X CMP #$7 BLT LBL LDA #$0 STA Y LBL --- ETC. In this example, if X is greater than or equal to seven, the program simply drops through the branch instruction and sets Y to zero. If X is less than seven, the branch if less than instruction causes the code which sets Y to zero to be skipped. Naturally, a block of instructions can be executed by placing these instructions between the branch instruction and the target label of that particular branch. FOR/NEXT LOOP REVISITED. As mentioned previously, it would be nice if we could end our loops at some value other than zero. Now that we have the CMP instruction under our belts we can do just that! If you wish to start your loop index variable with the value $1 and increment 5-14 ***************************************************************** it until 10 is reached you could use the following code: LDA #$1 STA I LOOP LDA I CMP #!10 BEQ LBL1 BGE LOPX LBL1: ;NOTE: The normal code within your loop body goes here. INC I JMP LOOP LOPX BRK ETC. If you would like a step size of two, simply increment I twice before jumping to LOOP. One last improvement which can be made is in the testing process. Since we want to test I to see if it is greater than 10, we must synthesize the BGT branch using the BEQ and BGE branches. One other method of doing this is to test to see if I is greater than or equal to 11. Since we have a BGE branch, this will save us some code. The resulting program would be: LDA #$1 STA I LOOP LDA I CMP #$B ;$B = 11 DECIMAL BGE LOOPX ;NORMAL LOOP BODY GOES HERE INC I JMP LOOP LOOPX BRK ETC. This small simplification makes life much easier for us. - TWO FINAL WARNINGS - Up to this point our discussion has concerned itself with unsigned values. Signed comparisons, which will be considered 5-15 ***************************************************************** later, follow a completely different set of rules. BE AWARE OF THIS. Also, in the discussion of the branch instructions, it was implied that you could branch anywhere in memory. This is not the case. Branches use a special addressing mode called, "rel- ative addressing." Unlike the JMP instruction which is followed by a 16-bit absolute address, the branch instructions are followed by a one-byte displacement. This displacement is added to the address of the instruction which follows the branch instruction to give an address which is in the range -126 to +129 bytes from the beginning of the branch instruction. What does this mean to your program? Usually nothing, since most branches fall within this range. Once in a great while a branch will be out of this range and the assembler will give you a "branch out of range" error message. Since we cannot increase the range of the branch instruction, another method must be used to correct this problem. Simply replace the branch instruction with the opposite type branch (e.g., if a BEQ is out of range, use a BNE branch) and use the strange looking address of "*+$5" for your operand. Immediately after the branch instruction, enter a JMP instruction using the address of the original branch. First, what does "*+$5" mean? Whenever a 6502 assem- bler encounters the asterisk in the operand field it will substitute the address of the beginning of the current instruction for the '*'. The "*+$5" means add five to the address of the branch instruc- tion and go there if the condition is satisfied. Since the branch instruction is two bytes long and the following JMP instruction is three bytes long the branch to "*+$5" will branch to the instruc- tion following the JMP instruction. EXAMPLE: BEQ LBL is out of range, fix it. Simply substitute: BNE *+$5 JMP LBL If the last operation set the zero flag, the program will drop through to the JMP instruction and then jump to location LBL. If the zero flag was not set after the last operation, a branch will occur to the next instruction after the JMP instruction. This effec- tively simulates a "LONG BRANCH IF EQUAL" to location LBL. 5-16 ***************************************************************** A Table of Branches and the Long Branch Form IF THIS BRANCH IS OUT OF RANGE USE THIS --------------- -------- BEQ LBL BNE *+$5 JMP LBL BNE LBL BEQ *+$5 JMP LBL BCC LBL BCS *+$5 JMP LBL BCS LBL BCC *+$5 JMP LBL BVC LBL BVS *+$5 JMP LBL BVS LBL BVC *+$5 JMP LBL BMI LBL BPL *+$5 JMP LBL BPL LBL BMI *+$5 JMP LBL BGE LBL BLT *+$5 JMP LBL BLT LBL BGE *+$5 JMP LBL BTR LBL BFL *+$5 (SEE THE NEXT SECTION) JMP LBL BFL LBL BTR *+$5 JMP LBL The asterisk can be used in other address expressions as well as the branch instructions, however its use is not really rec- ommended. 5-17 ***************************************************************** TESTING BOOLEAN VALUES. Remember the values true and false? Often within a pro- gram you will use certain variables to hold flags for use in other parts of the program. Since the use of such Boolean variables occurs often, it would be nice to define the Boolean values TRUE and FALSE. As per the discussion in Chapter 2, we will let FALSE be represented by the value $00 and TRUE by the value $01. Now we can use the BEQ and BNE instructions to test for true or false. The only problem with this scheme is that we use the branch if not equal instruction, to test for true and the branch if equal to test for false. This may seem incongruent. Rather than leaving you feeling strange about using these tests, LISA incorporates two additional branch instructions, BTR and BFL (branch if true and branch if false, respectively), which generate the same code as BEQ and BNE. The former instructions are simply easier to remember. While on the discussion of true and false, it should be men- tioned that you should include the statements: FALSE EQU $0 TRUE EQU $1 5-18 ***************************************************************** at the beginning of your program. True and false will not be used as memory locations, but rather as symbolic constants. Now your programs will read: LDA #FALSE STA I LDA #TRUE STA FLAG instead of: LDA #$0 STA I LDA #$1 STA FLAG Obviously, the first version is much more readable. Inci- dently, the use of symbolic constants is not limited to true and false. Anytime you use some hex value which has special signif- icance (for instance the ASCII code for carriage return), it should be declared as a symbolic constant. Symbolic constants make your programs much easier to read and modify. 5-19 ***************************************************************** CHAPTER 6 ARITHMETIC OPERATIONS NEW INSTRUCTIONS: ADC SBC GENERAL. The art of assembly language programming is actually the art of learning to do things a piece at a time. Arithmetics cannot be performed with a single statement as in BASIC. Rather, 10, 20, or even 50 lines of machine language code may be required to perform a specific operation. There are three basic types of arithmetic operations per- formed by the 6502 microprocessor: (1) unsigned binary, (2) signed binary, and (3) unsigned decimal arithmetic. DO NOT CONFUSE THESE! Each type of arithmetic follows its own set of rules; inter- mixing these operations and/or rules may cause invalid results. UNSIGNED INTEGER (BINARY) ARITHMETIC. When working with unsigned values, the 6502 processor can handle numbers in the range of 0 thru 255. Although the range is not very good, eight bits are suitable for many appli- cations. As with the decrement instructions, wrap around occurs if you try to add two numbers whose sum exceeds the range of 0 thru 255, likewise, wrap around occurs if you try to subtract a large number from a smaller one. Do not worry about the range limitation at this time. Multi- precision operations which allow numbers to greatly exceed the 0 thru 255 limitation will be discussed later. Unlike your handy pocket calculator, the 6502 cannot per- form functions such as SIN, COS, 1/X, LOG, or TAN. In fact, the 6-1 ***************************************************************** 6502 cannot even multiply or divide two numbers. The only arith- metic operations the 6502 microprocessor can perform are addition and subtraction. All of the other fancy operations can be simulated by using addition and subtraction. The 6502 instruction mnemonic for addition is ADC (add with carry). This instruction takes a memory operand and adds it to the accumulator. Once this is accomplished, the value con- tained in the carry flag (zero or one) is also added to the accu- mulator. The reason behind this will become clear when we dis- cuss multi-precision arithmetic. In any case, your first unsigned arithmetic rule is: ALWAYS CLEAR THE CARRY FLAG BEFORE PERFORMING AN ADC. Obviously, if you do not explicitly clear the carry flag before performing an addition, you stand a 50/50 chance of ending up with the intended sum PLUS ONE. EXAMPLES: CLC ;ALWAYS! LDA #$5 ADC #$3 BRK ;PRINTS RESULT OF ADDITION END CLC LDA #7 ADC #$3 BRK END CLC