Note from the tired typist: Wow, over 15000 lines...but I finally hit my target typing goal of 200wpm! :) I fixed TONS of typos from the original text, and standardized some formatting that was pretty varied in the original. Page numbers are intact, and are delimited by colons, so if you find a page number in the index at the bottom of this file, you can simply do a search for :number: if you're reading this in a text editor. Some page numbers are a little off because I shuffled around the placement of some tables/figures, and enacted pretty gratuitous orphan/widow control. Any comments that I have added to this book are delimited by square brackets and end with "-wf". This file was created in DOS v7's "EDIT" text editor, spell-checked by Word 97, and is designed to be viewed in a monospaced, square-character font, like VGA 80x50 text mode or Courier. Maximum column width is 77 characters, which are only ASCII characters 10, 13, and 32-126 ($0A, $0D, and $20-$7E). The text in here is AWESOME. I wish I had this book when I was learning ML. Some quotes from the back cover: "This book demystifies the magic of computers...it's the machine language masterpiece we've all been waiting for!" "...an excellent introduction to machine language...I love the consistent use of both hex and decimal, the overall readability, and the numerous summaries!" There's a plethora of information about the entire Commodore 8-bit line of computers up to the time of print, not just the Commodore 64, so you get plenty of info for PET, VIC, et al. As a simple legal notice, Jim Butterfield has posted on the internet newsgroup comp.sys.cbm that he has reclaimed rights to this book from the original publisher, and given permission to have this book converted to electronic format for free public distribution. You rock, Jim! :) Thanks to Burt "Terminator" Bochenek for getting me this book. -- White Flame (aka David Holz) http://fly.to/theflame :i: MACHINE LANGUAGE FOR THE COMMODORE 64 and Other Commodore Computers James Butterfield Brady Communications Company, Inc., Bowie, MD 20715 A Prentice-Hall Publishing Company :ii: Copyright (c) 1984 by Brady Communications Company, Inc. [etc etc etc blah blah blah -wf] ISBN 0-89303-652-8 [yada yada yada -wf] Publishing Director: David T. Culverwell Acquisitions Editor: Terrell W. Anderson Production Editor/Text Design: Lisa G. Kolman Art Director: Don Sellers Assistant Art Director: Bernard Vervin Manufacturing Director: John Kosma Cover design and illustration: Dave Joly Copy Editor: Keith Tidman Indexer: William O. Lively Typesetter: Harper Graphics, Waldorf, MD Printer: R. R. Donnelly & Sons, Harrisonburg, VA Typefaces: Helvetica (text, display), Times Roman (display) [E-text conversion completed on 5/28/2000 by: White Flame (aka David Holz) http://fly.to/theflame -wf] :iii: ============ = CONTENTS = ============ Note to Readers vi Preface vii Introduction ix 1 First Concepts 1 The Inner Workings of Microcomputers Computer Notation: Binary and Hexadecimal The 650x's Inner Architecture Beginning Use of a Machine Language Monitor A Computer's "Memory Layout" First Machine Language Commands Writing and Entering a Simple Program 2 Controlling Output 23 Calling Machine Language Subroutines The PRINT Subroutine Immediate Addressing Calling Machine Language from BASIC Tiny Assembler Programs Indexed Addressing Simple Loops Disassembly 3 Flags, Logic, and Input 39 Flags that hold Status Information Testable Flags: Z, C, N, and V Signed Numbers The Status Register First Concepts of Interrupt Logical Operators: OR, AND, EOR The GETIN Subroutine for Input The STOP Subroutine :iv: 4 Numbers, Arithmetic, and Subroutines 57 Numbers: Signed and Unsigned Big Numbers: Multiple Bytes Arithmetic: Add and Subtract Rotate and Shift Instructions Multiplication Home-Grown Subroutines 5 Address Modes 71 Non-addresses: Implied, Immediate, Register Absolute and Zero Page Indexing The Relative Address for Branches Indirect Addressing Indirect, Indexed 6 Linking BASIC and Machine Language 91 Where To Put a Machine Language Program Basic Memory Layout Loading and the SOV Pointer Basic Variables: Fixed, Floating, and String Exchanging Data with BASIC 7 Stack, USR, Interrupt, and Wedge 111 The Stack for Temporary Storage USR: An Alternative to SYS Interrupts: IRQ, NMI, and BRK The IA Chips: PIA and VIA Infiltrating BASIC: The Wedge 8 Timing, Input/Output, and Conclusion 131 How To Estimate the Speed of Your Program Input and Output from Tape, Disk, Printer Review of Instructions Debugging Symbolic Assemblers Where To Go from Here :v: Appendix A The 6502/6510/6509/7501 Instruction Set 147 Appendix B Some Characteristics of Commodore Machines 155 Appendix C Memory Maps 161 Appendix D Character Sets 215 Appendix E Exercises for Alternative Commodore Machines 225 Appendix F Floating Point Formats 231 Appendix G Uncrashing 233 Appendix H A Do-It-Yourself Supermon 237 Appendix I IA Chip Information 245 Appendix J Disk User's Guide 309 Glossary 317 Index 322 :vi: =================== = Note to Readers = =================== This book introduces beginners to the principles of machine language: what it is, how it works, and how to program with it. It is based on an intensive two-day course on machine language that has been presented many times over the past five years. Readers of this book should have a computer on hand: students will learn by doing, not just by reading. Upon completing the tutorial material in this book, the reader will have a good idea of the fundamentals of machine language. There will be more to be learned; but by this time, students should understand how to adapt other material from books and magazines to their own particular computers. LIMITS OF LIABILITY AND DISCLAIMER OF WARRANTY The author and publisher [and e-text converter -wf] of this book have used their best efforts in preparing this book and the programs contained in it. These efforts include the development, research, and testing of the programs to determine their effectiveness. The author and the publisher [and e-text converter -wf] make no warranty of any kind, expressed or implied, with regard to these programs, the text, or the documentation contained in this book. The author and the publisher [and e-text converter -wf] shall not be liable in any event for claims of incidental of consequential damages in connection with, or arising out of, the furnishing, performance, or use of the text or the programs. At time of publication, the Commodore 264 is still undergoing design changes. The name is being changed to the "PLUS/4"; a related machine, the Commodore 16, has also been announced. Detailed design information is not available; but the information given in this book for the Commodore 264 should be generally accurate. :vii: =========== = Preface = =========== This book is primarily tutorial in nature. It contains, however, extensive reference material, which the reader will want to continue to use. No previous machine language experience is required. It is useful if the reader has had some background in programming in other languages, so that concepts such as loops and decisions are understood. Beginners will find that the material in this book moves at a fast pace. Stay with it; if necessary, skip ahead to the examples and then come back to reread a difficult area. Readers with some machine language experience may find some of the material too easy; for example, they are probably quite familiar with hexadecimal notation and don't need to read that part. If this is the case, skip ahead. But do enter all the programming projects; if you have missed a point, you may spot it while doing an exercise. Programming students learn by doing. The beginner needs to learn simple things about his or her machine in order to feel in control. The elements that are needed may be itemized as: o Machine language. This is the objective, but you can't get there without the next two items. o Machine architecture. All the machine language theory in the world will have little meaning unless the student knows such things as where a program may be placed in memory, how to print to the screen, or how to input from the keyboard. o Machine language tools. The use of a simple machine language monitor to read and change memory is vital to the objective of making the computer do something in machine language. Use of a simple assembler and elements of debugging are easy once you know them; but until you know them, it's hard to make the machine do anything. Principles of sound coding are important. They are seldom discussed explicitly, but run as an undercurrent through the material. The objective is this: it's easy to do things the right way, and more difficult to do them the wrong way. By introducing examples of good coding practices early, the student will not be motivated to look for a harder (and inferior) way of coding. It should be pointed out that this book deals primarily with machine language, not assembly language. Assembler programs are marvelous things, :viii: but they are too advanced for the beginner. I prefer to see the student forming an idea of how the bytes of the program lie within memory. After this concept is firmly fixed in mind, he or she can then look to the greater power and flexibility offered by an assembler. ==================== = Acknowledgements = ==================== Thanks go to Elizabeth Deal for acting as resource person in the preparation of this book. When I was hard to find, the publisher could call upon Elizabeth for technical clarification. :ix: ================ = Introduction = ================ Why learn machine language? There are three reasons. First, for speed; machine language programs are fast. Second, for versatility; all other languages are limited in some way, but not machine language. Third, for comprehension; since the computer really works in machine language only, the key to understanding how the machine operates is machine language. Is it hard? Not really. It's finicky, but not difficult. Individual machine language instructions don't do much, so we need many of them to do a job. But each instruction is simply, and anyone can understand it if he or she has the patience. Some programmers who started their careers in machine language find "higher level" languages such as BASIC quite difficult by comparison. To them, machine language instructions are simple and precise, whereas BASIC statements seem vague and poorly defined by comparison. Where will this book take you? You will end up with a good understanding of what machine language is, and the principles of how to program in it. You won't be an expert, but you'll have a good start and will no longer be frightened by this seemingly mysterious language. Will the skills you learn be transportable to other machines? Certainly. Once you understand the principles of programming, you'll be able to adapt. If you were to change to a non-Commodore machine that used the 6502 chip (such as Apple or Atari), you'd need to learn about the architecture of these machines an about their machine language monitors. They would be different, but the same principles would apply on all of them. Even if you change to a computer that doesn't use a chip from the 6502 family, you will be able to adapt. As you pick through the instructions and bits of the Commodore machine, you will have learned about the principles of all binary computers. You will need to learn the new microprocessor's instruction set, but it will be much easier the second time around. Do you need to be a BASIC expert before tackling machine language? Not at all. This book assumes you know a little about programming fundamentals: loops, branching, subroutines, and decision making. But you don't need to be an advanced programmer to learn machine language. :1: Chapter 1 First Concepts This chapter discusses: o The inner workings of microcomputers o Computer notation: binary and hexadecimal o The 650x's inner architecture o Beginning use of a machine language monitor o First machine language commands o Writing and entering a simple program :2: The Inner Workings of Microcomputers ------------------------------------ All computers contain a large number of electrical circuits. Within any binary computer, these circuits may be in only two states: "on" or "off." Technicians will tell you that "on" usually means full voltage on the circuit concerned, and "off" means no voltage. There's no need for volume control adjustments within a digital computer: each circuit is either fully on or fully off. The word "binary" means "based on two," and everything that happens within the computer is based on the two possibilities of each circuit: on or off. We can identify these two conditions in any of several ways: ON or OFF TRUE or FALSE YES or NO 1 or 0 The last description, 1 or 0, is quite useful. It is compact and numeric. If we had a group of eight circuits within the computer, some of which were "on" and others "off," we could describe their conditions with an expression such as: 11000111 This would signify that the two leftmost wires were on, the next three off, and the remaining three on. The value 11000111 looks like a number; in fact, it is a binary number in which each digit is 0 or 1. It should not be confused with the equivalent decimal value of slightly over 11 million; the digits would look the same, but in decimal each digit could have a value from 0 to 9. To avoid confusion with decimal numbers, binary numbers are often preceded by a percent sign, so that the number might be shown as %11000111. Each digit of a binary number is called a bit, which is short for "binary digit." The number shown above has eight bits; a group of eight bits is a byte. Bits are often numbered from the right, starting at zero. The right- hand bit of the above number would be called "bit 0," and the left-hand bit would be called "bit 7." This may seem odd, but there's a good mathematical reason for using such a numbering scheme. :3: The Bus ------- It's fairly common for a group of circuits to be used together. The wires run from one microchip to another, and then on to the next. Where a group of wires are used together and connect to several different points, the group is called a bus (sometimes spelled "buss"). [um, isn't a "buss" another word for a "kiss"? better check what's going on inside my machine... HEY! -wf] The PET, CBM, and VIC-20 use a microprocessor chip called the 6502. The Commodore 64 uses a 6510. The Commodore B series uses a 6509 chip, and the Commodore PLUS/4 uses a chip called the 7501. All these chips are similar, and there are other chips in the same family with numbers like 6504; every one works on the same principles, and we'll refer to all of them by the family name 650x. Let's take an example of a bus used on any 650x chip. A 650x chip has little built-in storage. To get an instruction or perform a computation, the 650x must call up information from "memory"--data stored within other chips. The 650x sends out a "call" to all memory chips, asking for information. It does this by sending out voltages on a group of sixteen wires called the "address bus." Each of the sixteen wires may carry either voltage or no voltage; this combination of signals is called an address. Every memory chip is connected to the address bus. Each chip reads the address, the combination of voltages sent by the processor. One and only one chip says, "That's me!" In other words, the specific address causes that chip to be selected; it prepares to communicate with the 650x. All other chips say, "That's not me!" and will not participate in data transfer. +------+ | | -> -> -> -> | o-------o-----------o-----------o-------- | o-------+o----------+o----------+o------- | 650x o-------++o---------++o---------++o------ | o-------+++o--------+++o--------+++o----- | | |||| |||| |||| | | ||||| ||||| ||||| +------+ v|||| v|||| v|||| |||| |||| |||| +--oooo--+ +--oooo--+ +--oooo--+ | | | | | | | Memory | | Memory | | Memory | | Chip | | Chip | | Chip | | | | | | | +--------+ +--------+ +--------+ Figure 1.1 Address bus connecting 650x & 3 chips :4: The Data Bus ------------ Once the 650x microprocessor has sent an address over the address bus and it has been recognized by a memory chip, data may flow between memory and 650x. This data is eight bits (it flows over eight wires). It might look like this: 01011011 The data might flow either way. That is, the 650x might read from the memory chip, in which case the selected memory chip places information onto the data bus which is read by the microprocessor. Alternatively, the 650x might wish to write to the memory chip. In this case, the 650x places information onto the data bus, and the selected memory chip receives the data and stores it. +------+ | | -> ADDRESS BUS -> -> -> | o--------o--------------o--------------o-------- | o--------+o-------------+o-------------+o------- | o--------++o------------++o------------++o------ | o--------+++o-----------+++o-----------+++o----- | | |||| |||| |||| | | ||||| ||||| ||||| | 650x | v|||| v|||| v|||| | | |||| |||| |||| | |DATA BUS|||| <-> |||| <-> |||| <-> | o------o-++++---------o-++++---------o-++++----- | o-----o+-++++--------o+-++++--------o+-++++----- | o----o++-++++-------o++-++++-------o++-++++----- | o---o+++-++++------o+++-++++------o+++-++++----- | | |||| |||| |||| |||| |||| |||| +------+ ^|||| |||| ^|||| |||| ^|||| |||| ||||| |||| ||||| |||| ||||| |||| v|||| |||| v|||| |||| v|||| |||| |||| |||| |||| |||| |||| |||| +--oooo-oooo-+ +-oooo-oooo-+ +-oooo-oooo-+ | | | | | | | Memory | | Memory | | Memory | | Chip | | Chip | | Chip | |("Selected")| | (not | | (not | | | | selected) | | selected) | +------------+ +-----------+ +-----------+ Figure 1.2 Two-way data bus All other chips are still connected to the data bus, but they have not been selected, so they ignore the information. The address bus is accompanied by a few extra wires (sometimes called the control bus) that control such things as data timing and the direction in which the data should flow: read or write. :5: Number Ranges ------------- The address bus has sixteen bits, each of which might be on or off. The possible combinations number 65536 (two raised to the sixteenth power). We then have 65536 different possibilities of voltages, or 65536 different addresses. The data bus has eight bits, which allows for 256 possibilities of voltages. Each memory location can store only 256 distinct values. It is often convenient to refer to an address as a decimal number. This is especially true for PEEK and POKE statements in the BASIC language. We may do this by giving each bit a "weight." Bit zero (at the right) has a weight of 1; each bit to the left has a weight of double the amount, so that bit 15 (at the left) has a weight of 32768. Thus, a binary address such as 0001001010101100 has a value of 4096 + 512 + 128 + 32 + 8 + 4 or 4780. A POKE to 4780 decimal would use the above binary address to reach the correct part of memory. +-----+-----+-----+-----+-----+-----+-----+-----+ | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | +-----+-----+-----+-----+-----+-----+-----+-----+ EIGHT BITS +-----+-----+----+====+----+----+===+---+===+---+===+---+===+===+---+---+ |32768|16384|8192|4096|2048|1024|512|256|128| 64| 32| 16| 8 | 4 | 2 | 1 | +-----+-----+----+====+----+----+===+---+===+---+===+---+===+===+---+---+ SIXTEEN BITS Figure 1.3 Direct conversion between decimal and binary is seldom needed. Such conversions usually pass through an intermediate number system, called hexadecimal. :6: Hexadecimal Notation -------------------- Binary is an excellent system for the computer, but it is inconvenient for most programmers. If one programmer asks another, "What address should I use for some activity?", an answer such as "Address %0001001010101100" might be correct but would probably be unsatisfactory. There are too many digits. Hexadecimal is a code used by humans to conveniently represent binary numbers. The computer uses binary, not hexadecimal; programmers use hexadecimal because binary is cumbersome. To represent a binary number in hexadecimal, the bits must be grouped together four at a time. If we take the binary value given above and split it into groups of four, we get 0001 0010 1010 1100 Now each group of four bits is represented by a digit as shown in the following table: 0000 - 0 0100 - 4 1000 - 8 1100 - C 0001 - 1 0101 - 5 1001 - 9 1101 - D 0010 - 2 0110 - 6 1010 - A 1110 - E 0011 - 3 0111 - 7 1011 - B 1111 - F Thus, the number would be represented as hexadecimal 12AC. A dollar sign is often prefixed to a hexadecimal number so that it may be clearly recognized: $12AC. The same type of weighting is applied to each bit of the group of four as was described before. In other words, the rightmost bit (bit zero) has a weight of 1, the next left a weight of 2, the next a weight of 4, and the leftmost bit (bit three) a weight of 8. If the total of the weighted bits exceeds nine, an alphabetic letter is used as a digit: A represents ten; B, eleven; C, twelve; and F, fifteen. Eight-bit numbers are represented with two hexadecimal digits. Thus, %01011011 may be written as $5B. Hexadecimal to Decimal ---------------------- As we have seen, hexadecimal and binary numbers are easily interchangeable. Although we will usually write values in "hex," occasionally we will need to examine them in their true binary state to see a particular information bit. Hexadecimal isn't hard to translate into decimal. You may recall that in early arithmetic we were taught that the number 24 meant, "two tens and four units." Similarly, hexadecimal 24 means "two sixteens and four units," or a decimal value of 36. By the way, it's better to say hex numbers as "two four" rather than "twenty-four," to avoid confusion with decimal values. :7: The formal procedure, or algorithm, to go from hex to decimal is as follows. Step 1: Take the leftmost digit; if it's a letter A to F, convert it to the appropriate numeric value (A equals 10, B equals 11, and so on). Step 2: If there are no more digits, you're finished; you have the number. Stop. Step 3: Multiply the value so far by sixteen. Add the next digit to the result, converting letters if needed. Go back to step 2. Using the above steps, let's convert the hexadecimal number $12AC. Step 1: The leftmost digit is 1. Step 2: There are more digits, so we'll continue. Step 3: 1 times 16 is 16, plus 2 gives 18. Step 2: More digits to come. Step 3: 18 times 16 is 288, plus 10 (for A) gives 298. Step 2: More digits to come Step 3: 298 x 16 is 4768, plus 12 (for C) gives 4780. Step 2: No more digits: 4780 is the decimal value. This is easy to do by hand or with a calculator. Decimal to Hexadecimal ---------------------- The most straightforward method to convert from decimal to hexadecimal is to divide repeatedly by 16; after each division, the remainder is the next hexadecimal digit, working from right to left. This method is not too well suited to small calculators, which usually don't give remainders. The following fraction table may offer some help: .0000 - 0 .2500 - 4 .5000 - 8 .7500 - C .0625 - 1 .3125 - 5 .5625 - 9 .8125 - D .1250 - 2 .3750 - 6 .6250 - A .8750 - E .1875 - 3 .4375 - 7 .6875 - B .9375 - F If we were to translate 4780 using this method, we would divide by 16, giving 298.75. The fraction tells us the last digit is C; we now divide 298 by 16, giving 18.625. The fraction corresponds to A, making the last two digits AC. Next we divide 18 by 16, getting 1.125--now the last three digits are 2AC. We don't need to divide the one by 16, although that would work; we just put it on the front of the number to get an answer of $12AC. :8: There are other methods of performing decimal-to-hexadecimal conversions. You may wish to look them up in a book on number systems. Alternatively, you may wish to buy a calculator that does the job electronically. Some programmers get so experienced that they can do conversion in their heads; I call them "hex nuts." [ groan... ;-) -wf] Do not get fixed on the idea of numbers. Memory locations can always be described as binary numbers, and thus may be converted to decimal or hexadecimal at will. But they may not mean anything numeric: the memory location may contain an ASCII coded character, an instruction, or any of several other things. Memory Elements --------------- There are generally three types of devices attached to the memory buses (address, data, and control buses): o RAM: Random access memory. This is the read and write memory, where we will store the programs we write, along with values used by the program. We may store information into RAM, and may recall the information at any time. o ROM: Read only memory. This is where the fixed routines are kept within the computer. We may not store information into ROM; its contents were fixed when the ROM was made. We will use program units (subroutines) stored in ROM to do special tasks for us, such as input and output. +------+ ADDRESS BUS | |------------------------------------------------------- | | -> -> -> -> | |-------------+ +-------------+ +-------------+ +------- TO | 650x | | | | | | | OTHER | | MEMORY BUS | | | | | | CHIPS | |-------------|||-------------|||-------------|||------- | | <-> |v| <-> |v| <-> |v| <-> | |-------+ +---| |-------+ +---| |-------+ +---| |------- +------+ | | | | | | | | | | | | |^| | | |^| | | |^| | | ||| | | ||| | | ||| | | |v| | | | | | | |v| | | | | | | | | | | | | | | +-----------+ +-----------+ +-----------+ | RAM | | ROM | | IA | | (read and | |(read only)| | (special) | | write) | | | | | +-----------+ +-----------+ +-----------+ ||||||| ^^^^^^^ ||||||| vvvvvvv ||||||| CONNECTIONS TO "OUTSIDE WORLD" Figure 1.4 :9: o IA: Interface adapter chips. These are not memory in the usual sense; but, these chips are assigned addresses on the address bus, so we call them "memory-mapped" devices. Information may be passed to and from these devices, but the information is generally not stored in the conventional sense. IA chips contain such functions as: input/output (I/O) interfaces that serve as connections to the "outside world"; timing devices; interrupt control system; and sometimes specialized functions, such as video control or sound generation. IA chips come in a wide variety of designs, including the PIA (peripheral interface adapter), the VIA (versatile interface adapter), the CIA (complex interface adapter), the VIC (video interface chip), and the SID (sound interface device). Within a given computer, some addresses may not be used at all. Some devices may respond to more than one address, so that they seem to be in two places in memory. An address may be thought of as split in two parts. One part, usually the high part of the address, selects the specific chip. The other part of the address selects a particular part of memory within the chip. For example, in the Commodore 64, the hex address $D020 (decimal 53280) sets the border color of the video screen. The first part of the address (roughly, $D0...) selects the video chip; the last part of the address (...20) selects the part of the chip that controls the border color. Microprocessor Registers ------------------------ Within the 650x chip are several storage areas called registers. Even though they hold information, they are not considered "memory" since they don't have an address. Six of these registers are important to us. Briefly, they are: PC: (16 bits) The program counter tells where the next instruction will come from. A, X and Y: (8 bits each) These registers hold data. SR: The status register, sometimes called the PSW (processor status word), tells about the results of recent tests, data handling, and so on. SP: The stack pointer keeps track of a temporary storage area. We will talk about each of these registers in more detail later. At the moment, we will concentrate on the PC (program counter). :10: +-------------+ | | | +---------+ | ADDRESS BUS | | PC | |------------- | +----+----+ | -> | | A | |------------- | +----+ | | | X | | | +----+ | | | Y | | | +----+ | DATA BUS | | SR | |------------- | +----+ | <-> | | SP | |------------- | +----+ | | | +-------------+ 650x CHIP Figure 1.5 Instruction Execution --------------------- Suppose that the 650x is stopped (not an easy trick), and that there is a certain address, say $1234, in the PC. The moment we start the micro- computer, that address will be put out to the address bus as a read address, and the processor will add one to the value in the PC. Thus, the contents of address $1234 will be called for, and the PC will change to $1235. Whatever information comes in on the data bus will be taken to be an instruction. The microprocessor now has the instruction, which tells it to do something. The action is performed, and the whole action now repeats for the next instruction. In other words, address $1235 will be sent to memory, and the PC will be incremented to $1236. +-----------+ +--------------+ | PC | | PC | | +-------+ | | +-------+ | | | $1234 | |ADDRESS | | $1235 | |ADDRESS | +-------+ |BUS | +-------+ |BUS | \ |-------- | |-------- | ---> | -> | $1234--->| -> | |-------- | |-------- | | | | | | | |DATA | | | |BUS | | | |-------- | | |INSTRUCTION<--| <- | | | |-------- | | | | +-----------+ +--------------+ Figure 1.6 Arrow to address bus :11: You can see that the processor works in the same way that most computer languages do: an instruction is executed, and then the computer proceeds to the next instruction, and then the next, and so on. We can change the sequence of execution by means of a "jump" or "branch" to a new location, but normally, it's one instruction after another. Data Registers: A, X, and Y ---------------------------- Any of three registers can be used to hold and manipulate eight bits of data. We may load information from memory into A, X, or Y; and we may store information into memory from any of A, X, or Y. Both "load" and "store" are copying actions. If I load A (LDA) from address $2345, I make a copy of the contents of hex 2345 into A; but 2345 still contains its previous value. Similarly, if I store Y into $3456, I make a copy of the contents of Y into that address; Y does not change. The 650x has no way of moving information directly from one memory address to another. Thus, this information must pass via A, X, or Y; we load it from the old address, and store it to the new address. Later, the three registers will take on individual identities. For example, the A register is sometimes called the accumulator, since we perform addition and subtraction there. For the moment, they are interchangeable: we may load to any of the three, and we may store from any of them. First Program Project --------------------- Here's a programming task: locations $0380 and $0381 contain information. We wish to write a program to exchange the contents of the two locations. How can we do this? We must make up a plan. We know that we cannot transfer information directly from memory to memory. We must load to a register, and then store. But there's more. We must not store and destroy data in memory until that data has been safely put away. How can we do this? Here's our plan. We may load one value into A (say, the contents of $0380), and load the other value into X (the contents of $0381). Then we could store A and X back, the other way around. :12: We could have chosen a different pair of registers for our plan, of course: A and Y, or X and Y. But let's stay with the original plan. We can code our plan in a more formal way: LDA $0380 (bring in first value) LDX $0381 (bring in second value) STA $0381 (store in opposite place) STX $0380 (and again) You will notice that we have coded "load A" as LDA, "load X" as LDX, "store A" as STA, and "store X" as STX. Every command has a standard three-letter abbreviation called a mnemonic. Had we used the Y register, we might have needed to use LDY and STY. One more command is needed. We must tell the computer to stop when it has finished the four instructions. In fact, we can't stop the computer, but if we use the command BRK (break), the computer will go to the machine language monitor (MLM) and wait for further instructions. We'll talk about the MLM in a few moments. We have written our program in a notation styled for human readability, called assembly language. But the computer doesn't understand this notation. We must translate it into machine language. The binary code for LDA is %10101101, or hexadecimal AD. That's what the computer recognizes; that's the instruction we must place in memory. So we code the first line: AD 80 03 LDA $0380 It's traditional to write the machine code on the left, and the source code on the right. Let's look closely at what has happened. LDA has been translated into $AD. This is the operation code, or op code, which says what to do. It will occupy one byte of memory. But we need to follow the instruction with the address from which we want the load to take place. That's address $0380; it's sixteen bits long, and so it will take two bytes to hold the address. We place the address of the instruction, called the operand, in memory immediately behind the instruction. But there's a twist. The last byte comes first, so that address $0380 is stored as two bytes: 80 first, and the 03. This method of storing addresses--low byte first--is standard in the 650x. It seems unusual, but it's there for good reason. That is, the computer gets extra speed from this "backwards" address. Get used to it; you'll see it again, many times. :13: Here are some machine language op codes for the instructions we may use. You do not need to memorize them: LDA - AD LDX - AE LDY - AC BRK - 00 STA - 8D STX - 8E STY - 8C Now we can compete the translation of our program: AD 80 03 LDA $0380 AE 81 03 LDX $0381 8D 81 03 STA $0381 8E 80 03 STX $0380 00 BRK On the right, we have our plan. On the left, we have the actual program that will be stored in the computer. We may call the right side assembly code and the left side machine code, to distinguish between them. Some users call the right-hand information source code, since that's where we start to plan the program, and the left-hand program object code, since that's the object of the exercise--to get code into the computer. The job of translating source code to object code is called assembly. We performed this translation by looking up the op codes and translating by hand; this is called hand assembly. The code must be placed into the computer. It will consist of 13 bytes: AD 80 08 AE 81 03 8D 81 03 8E 80 03 00. That's the whole program. But we have a new question: where do we put it? Choosing a Location ------------------- We must find a suitable location for our program. It must be placed into RAM memory, of course, but where? For the moment, we'll place our program into the cassette buffer, starting at address $033C (decimal 828). That's a good place to put short test programs, which is what we will be writing for a while. Now that we've made that decision, we face a new hurdle: how do we get the program in there? To do that, we need to use a machine language monitor. :14: Monitors: What They Are ------------------------ All computers have a built-in set of programs called an operating system that gives the machine its style and basic capabilities. The operating system takes care of communications--reading the keyboard, making the proper things appear on the screen, and translating data between the computer and other devices, such as a disk, tape, or printer. When we type on the computer keyboard, we use the operating system, which detects the characters we type. But there's an extra set of programs built into the computer that must decide what we mean. When we are using the BASIC language, we'll be communicating with the BASIC monitor, which understands BASIC commands such as NEW, LOAD, LIST, or RUN. It contains editing features that allow us to change the BASIC program that we are writing. But when we switch to another system--often another language--we'll need to use a different monitor. Commands such as NEW or LIST don't have any meaning for a machine language program. We must leave the BASIC monitor and enter a new environment: the machine language monitor. We'll need to learn some new commands because we will be communicating with the computer in a different way. The Machine Language Monitor ---------------------------- Most PET/CBM computers have a simple MLM (machine language monitor) built in. It may be extended with extra commands. The Commodore PLUS/4 contains a very powerful MLM. The VIC-20 and Commodore 64 do not have a built-in MLM, but one can be added. Such a monitor may be either loaded into RAM or plugged in as a cartridge. Monitors may be purchased or obtained from user clubs. Most machine language monitors work in a similar way, and have about the same commands. To proceed, you'll need an MLM in your computer. Use the built-in one, plug it in, load it in, or load and run...whatever the instructions tell you. On a PET/CBM machine, typing the command SYS 4 will usually switch you to the built-in monitor. After an MLM has been added to a VIC or Commodore 64, the command SYS 8 will usually get you there. On the Commodore PLUS/4, the BASIC command MONITOR will bring the monitor into play. :15: Monitor Display --------------- The moment you enter the MLM, you'll see a display that looks something like this: B* PC SR AC XR YR SP .; 0005 20 54 23 6A F8 . The cursor will be flashing to the right of the period on the bottom line. The exact appearance of the screen information may vary according to the particular monitor you are using. Other material may be displayed--in particular, a value called IRQ--which we will ignore for the time being. The information you see may be interpreted as follows: B*--we have reached the MLM by means of a "break." More about that later. PC--The value shown below this title is the contents of the program counter. This indicates where the program "stopped." In other words, if the value shown is address 0005, the program stopped at address 0004, since the PC is ready to continue at the following address. The exact value (0004 versus 0005) may vary depending on the particular MLM. SR--The value shown below shows the status register, which tells us the results of recent tests and data operations. We'd need to split apart the eight bits and look at them individually to establish all the information here; we will do this at a later time. AC, XR, and YR--The values shown below these three titles are the contents of our three data registers: A, X, and Y. SP--The value shown below is that of the stack pointer, which indicates a temporary storage area that the program might use. A value of F8, for example, tells us that the next item to be dropped into the stack would go to address $01F8 in memory. More on this later. The period is roughly the equivalent of the READY statement in BASIC. It indicates that the computer is ready to receive a command from you. You will notice that the display printed by the monitor (called the register display) shows the internal registers within the 650x chip. Sometimes there is another term of information, titled IRQ, in this display. It doesn't belong, since it does not represent a microprocessor register. IRQ tells us to what address the computer will go to if an interrupt occurs; this information is stored in memory, not within the 650x. MLM Commands ------------ The machine language monitor is now waiting for you to enter a new command. The old BASIC commands don't work any more; LIST or NEW or SYS are not known to the MLM. We'll list some popular commands in a moment. First, let's discuss the command that takes us back to BASIC. :16: .X exits the MLM and returns to the BASIC monitor. Try it. Remember to press RETURN after you've typed the X, of course. You will return to the BASIC system, and the BASIC monitor will type READY. You're back in familiar territory. Now go back to the monitor with SYS4 or SYS8 or MONITOR as the case may be. BASIC ignores spaces: it doesn't matter if you type SYS8 or SYS 8; just use the right number for your machine (4 for PET/CBM, 8 for VIC/64). Remember: BASIC commands are no good in the MLM, and machine language monitor commands (such as .X) are no good in BASIC. At first, you'll give the wrong commands at the wrong time because it's hard to keep track of which monitor system is active. If you type in an MLM command when you're in BASIC, you'll probably get a ?SYNTAX ERROR reply. If you type in a BASIC command when you're in the machine language monitor, you'll probably get a question mark in the line you typed. Some other MLM commands are as follows (the prompting period is included): .M 1000 1010 (display memory from hex 1000 to 1010) .R (display registers ... again!) .G 033C (go to 033C and start running a program) Do not enter this last (.G) command. There is no program at address 033C yet, so the computer would execute random instructions and we would lose control. There are two other fundamental instructions what we won't use yet: they are .S for save and .L for load. These are tricky. Until you learn about BASIC pointers (Chapter 6), leave them alone. Displaying Memory Contents -------------------------- You'll notice that there is a command for displaying the contents of memory, but there doesn't seem to be one for changing memory. You can do both, of course. Suppose we ask to display memory from $1000 to $1010 with the command .M 1000 1010 Be careful that you have exactly one space before each address. You might get a display that looks something like this: :17: .:1000 11 3A E4 00 21 32 04 AA .:1008 20 4A 49 4D 20 42 55 54 .:1010 54 45 52 46 49 45 4C 44 The four-digit number at the start of each line represents the address in memory being displayed. The two-digit numbers to the right represent the contents of memory. Keep in mind that all numbers used by the machine language monitor are hexadecimal. In the example above, $1000 contains a value of $11; $1001 contains a value of $3A, and so on, until $1007, which contains a value of $AA. We continue with address $1008 on the next line. Most monitors show eight memory locations on each line, although some VIC-20 monitors show only five because of the narrow screen. We asked for memory locations up to address $1010 only; but we get the contents of locations up to $1017 in this case. The monitor always fills out a line, even if you don't ask for the extra values. Changing Memory Contents ------------------------ Once we have displayed the contents of part of memory, we can change that part of memory easily. All we need to do is to move the cursor until it is positioned over the memory contents in question, type over the value displayed, and then press RETURN. This is quite similar to the way BASIC programs may be changed; you may type over on the screen, and when you press RETURN, the new line replaces the old. The general technique is called screen editing. If you have displayed the contents of memory, as in the example above, you might like to change a number of locations to zero. Don't forget to strike RETURN so that the change on the screen will take effect in memory. Give another .M memory display command to confirm that memory has indeed been changed. Changing Registers ------------------ We may also change the contents of registers by typing over and pressing RETURN. You may take a register display with command .R, and then change the contents of PC, AC, XR, and YR. Leave the contents of SR and SP unchanged-- tricky things could happen unexpectedly if you experiment with these two. :18: Entering the Program -------------------- We might rewrite our program one last time, marking in the addresses that each instruction will occupy. You will recall that we have decided to put our program into memory starting at address $033C (part of the cassette buffer). 033C AD 80 03 LDA $0380 033F AE 81 03 LDX $0381 0342 8D 81 03 STA $0381 0345 8E 80 03 STX $0380 0348 00 BRK Remember that most of the above listing is cosmetic. The business end of the program is the set of two-digit hex numbers shown to the left. At the extreme left, we have addresses--that's information, but not the program. At the right, we have the "source code"--our notes on what the program means. How do we put it in? Easy. We must change memory. So, we go to the MLM, and display memory with .M 033C 0348 We might have anything in that part of memory, but we'll get a display that looks something like .:033C xx xx xx xx xx xx xx xx .:0344 xx xx xx xx xx xx xx xx You won't see "xx," of course; there will be some hexadecimal value printed for each location. Let's move the cursor back and change this display so that it looks like this: .:033C AD 80 03 AE 81 03 8D 81 .:0344 03 8E 80 03 00 xx xx xx Don't type in the "xx"--just leave whatever was there before. And be sure to press RETURN to activate each line; if you move the cursor down to get to the next line without pressing RETURN, the memory change would not happen. Display memory again (.M 033C 0348) and make sure that the program is in place correctly. Check the memory display against the program listing, and be sure you understand how the program is being transcribed into memory. :19: If everything looks in order, you're ready to run your first machine language program. Preparation ----------- There's one more thing that we need to do. If we want to swap the contents of addresses $0380 and $0381, we'd better put something into those two locations so that we'll know that the swap has taken place correctly. Display memory with .M 0380 0381 and set the resulting display so that the values are .:0380 11 99 xx xx xx xx xx xx Remember to press RETURN. Now we may run our program; we start it up with .G 033C The program runs so quickly that it seems instantaneous (the run time is less that one fifty thousandth of a second). The last instruction in our program was BRK for break, and that sends us straight to the MLM with a display of B* (for break, of course) plus all the registers. Nothing seems to have changed. But wait. Look carefully at the register display. Can you explain the values you see in the AC and XR registers? Can you explain the PC value? Now you may display the data values we planned to exchange. Give the memory display command .M 0380 0831--have the contents of the two locations changed? They'd better have changed. Because that's what the writing of our program was all about. Things You Have Learned ----------------------- - Computers use binary. If we want to work with the inner fabric of the computer, we must come to terms with binary values. - Hexadecimal notation is for humans, not for computers. It's a less clumsy way for people to cope with binary numbers. - The 650x microprocessor chip communicates with memory by sending an address over its memory bus. - The 650x has internal work areas called registers. - The program counter tells us the address from which the processor will get its next instruction. :20: - Three registers, called A, X, and Y, are used to hold and manipulate data. They may be loaded from memory, and stored into memory. - Addresses used in 650x instructions are "flipped:" the low byte comes first, followed by the high byte. - The machine language monitor gives us a new type of communications path into the computer. Among other things, it allows us to inspect and change memory in hexadecimal. Detail: Program Execution -------------------------- When we say .G 033C to start up our program, the microprocessor goes through the following steps: 1. It asks for the contents of $03cc; it receives $AD, which it recognizes as the op code "load A." It realizes that it will need a two-byte address to go with this instruction. 2. It asks for the contents of $033D, and then $033E. As it receives the values of $80 and $03 it gathers them into an "instruction address." 3. The microprocessor now has the whole instruction. The PC has moved along to $033F. The 650x now executes the instruction. It sends address $0380 to the address bus; when it gets to the contents (perhaps $11), it delivers this to the A register. The A register now contains $11. 4. The 650x is ready to take on the next instruction; the address $033F goes from the PC out to the address bus; and the program continues. Questions and Projects ---------------------- Do you know that your computer has a part of memory called "screen memory"? Whatever you put into that part of memory appears on the screen. You'll find this described in BASIC texts as "screen POKE-ing." The screen on the PET/CBM is at $8000 and up; on the VIC, it's often (but not always) at $1E00 and up; on the Commodore 64, it's usually at $0400; and on the PLUS/4, it may be found at $0C00. If you write a program to store information in the screen memory address, the appropriate characters will appear on the screen. You might like to try this. You can even "swap" characters around on the screen, if you wish. Two pitfalls may arise. First, you might write a perfect program that places information near the top of the screen; then, when the program finishes, the screen might scroll, and the results would disappear. Second, the VIC and Commodore 64 use color, and you might inadvertently produce white-on-white characters; these are hard to see. :21: Here's another question. Suppose I asked you to write a program to move the contents of five locations, $0380 to $0384, in an "end-around" fashion, so that the contents of $0380 moved to $0381, $0381 to $0382, and so on, with the contents of $0384 moved to $0380. At first glance, we seem to have a problem: we don't have five data registers, we have only three (A, X, and Y). Can you think of a way of doing the job? :22: :23: Chapter 2 Controlling Output This chapter discusses: o Calling machine language subroutines o The PRINT subroutine o Immediate addressing o Calling machine language from BASIC o Tiny assembler programs o Indexed addressing o Simple loops o Disassembly :24: Calling Machine Language Subroutines ------------------------------------ In BASIC, a "package" of program statements called a subroutine may be brought into action with a GOSUB command. The subroutine ends with a RETURN statement, which causes the program to return to the calling point, i.e., the statement immediately following GOSUB. The same mechanism is available in machine language. A group of instructions may be invoked with a jump subroutine (JSR) command. The 650x goes to the specified address and performs the instructions given there until it encounters a return from subroutine (RTS) command, at which time it resumes execution of instructions at the calling point: the instruction immediately following JSR. For example, if at address $033C I code the instruction JSR $1234, the 650x will change its PC to $1234 and start to take instructions from that address. Execution will continue until the instruction RTS is encountered. At this time, the microprocessor would switch back to the instruction following the JSR, which in this case would be address $033F (the JSR instruction is three bytes long). As in BASIC, subroutines may be "nested;" that is, one subroutine may call another, and that subroutine may call yet another. We will deal with subroutine mechanisms in more detail later. For the moment, we'll concern ourselves with calling prewritten subroutines. Prewritten Subroutines ---------------------- A number of useful subroutines are permanently stored in the ROM memory of the computer. All Commodore machines have a standard set of subroutines that may be called up by your programs. They are always at the same addresses, and perform in about the same way regardless of which Commodore machine is used: PET, CBM, Commodore 64, PLUS/4, or VIC-20. These routines are called the kernal subroutines. Details on them can be found in the appropriate Commodore reference manuals, but we'll give usage information here. The original meaning of the term kernal seems to be lost in legend. It was originally an acronym, standing for something like "Keyboard Entry Read, Network and Link." Today, it's just the label we apply to the operating system that makes screen, keyboard, other input/output and control mechanisms work together. To describe this central control system, we might choose to correct the spelling so as to get the English word, "kernel." For now, we'll use Commodore's word. :25: The three major kernal subroutines that we will deal with in the next few chapters are shown here: Address Name What it does ------------------------------------------------- $FFD2 CHROUT Output an ASCII character $FFE4 GETIN Gets an ASCII character $FFE1 STOP Checks the RUN/STOP key With the first two subroutines, we can input and output data easily. The third allows us to honor the RUN/STOP key, to guard against certain types of programming error. In this chapter, we'll use CHROUT to print information to the screen. CHROUT--The Output Subroutine ----------------------------- The CHROUT subroutine at address $FFD2 may be used for all types of output: to screen, to disk, to cassette tape, or to other devices. It's similar to PRINT and PRINT#, except that it sends only one character. For the moment, we'll use CHROUT only for sending information to the computer screen. *---------------------------------------------------------------------------* | | | Subroutine: CHROUT | | Address: $FFD2 | | Action: Sends a copy of the character in the A register to the | | output channel. The output channel is the computer screen | | unless arrangements have been made to switch it. | | | | The character sent is usually ASCII (or PET ASCII). When sent to the | | screen, all special characters--graphics, color codes, cursor movements-- | | will be honored in the usual way. | | | | Registers: All data registers are preserved during a CHROUT call. Upon | | return from the subroutine, A, X, and Y will not have changed. | | | | Status: Status flags may be changed. In the VIC and Commodore 64, the C | | (carry) flag indicates some type of problem with output. | | | *---------------------------------------------------------------------------* To print a letter X on the screen, we would need to follow these steps: 1. Bring the ASCII letter X ($58) into the A register. 2. JSR to address $FFD2 :26: Why Not POKE? ------------- It may seem that there's an easier way to make things appear on the screen. We might POKE information directly to screen memory; in machine language, we would call this a store rather than a POKE, of course. The moment we change something in this memory area, the information displayed on the screen will change. Screen memory is generally located at the following addresses: PET/CBM: $8000 and up (decimal 32768) Commodore 64: $0400 and up (decimal 1024) 264/364: $0C00 and up (decimal 3072) VIC-20: $1E00 and up (decimal 7680) The screen memory of the VIC-20 in particular may move around a good deal, depending on how much additional RAM memory has been fitted. Occasionally, screen POKEs are the best way to do the job. But most of the time we'll use the CHROUT, $FFD2 subroutine. Here are some of the reasons why: o As with PRINT, we don't need to worry about where to place the next character; it will be positioned automatically at the cursor point. o If the screen is filled, scrolling will take place automatically. o Screen memory needs special characters. For example, the character X has a standard ASCII code of $58, but to POKE it to the screen we'd need to use the code $18. The CHROUT subroutine uses $58. o Screen memory may move around, depending on the system and the program. The POKE address would need to change; but CHROUT keeps working. o Special control characters are honored: $0D for RETURN, to start a new line; cursor movements; color changes. We can even clear the screen by loading the screen-clear character ($93) and calling $FFD2. o To POKE the screen of the VIC or Commodore 64, the corresponding color nybble memory must also be POKEd (see the appropriate memory map in Appendix B). With the subroutine at $FFD2, color is set automatically. A Print Project --------------- Let's write some code to print the letter H on the screen. Once again, we'll use address $033C, the cassette buffer, to hold our program. Reminder: be sure to have your monitor loaded and ready before you start this project. :27: First, the plan; we lay out the instructions LDA #$48 We're using a new symbol (#) to signal a special type of information. It goes by a variety of names: pounds sign, sharp, hash mark, or numbers sign. A more formal name for the symbol is octothorpe, meaning "eight points." Whatever you call it, the symbol means "the following information is not an address, it's a value." In other words, we don't want the computer to go to address $48, we want it to load the A register with the value $48, which represents the ASCII letter H. This type of information access is called immediate addressing. In other words, take information immediately, don't go to memory for it. JSR $FFD2 The previous instruction brought the letter H into the A register; this one prints it to the screen. Now all we need to do is quite. BRK takes us to the machine language monitor. Monitor Extensions ------------------ We could repeat the steps of the previous chapter: hand-assembling the source code into machine language, and then placing it into memory. We would need to know the instruction codes, and then do a careful translation. But there's an easier way. Most machine language monitors contain extra commands to help us do this type of mechanical translation. We'll use the assembler feature of these monitors. Most monitors contain the assemble (.A) command. The notable exception is the built-in monitors with the PET/CBM; these, however, can be extended by loading in a "monitor extension" program such as Supermon. The Commodore PLUS/4 series contains an extended monitor, which includes the .A command. These assemblers are often called nonsymbolic assemblers. This means that whenever an address is needed, you must furnish that exact address. You cannot type in a name such as CHROUT and expect the tiny assembler to know what address that represents; instead, you must type $FFD2. Load your monitor or monitor extension. Do any setup that may be needed. Then type the following monitor command: .A 033C LDA #$48 :28: We are asking the computer to assemble (.A) at address $033C (note we don't use the $ here) the command LDA, Load A, the immediate value of $48, which represents the ASCII letter H. When you type RETURN after entering this line, the computer may do either of two things: 1. It may do nothing except print a question mark somewhere on the line. The question mark indicates an error in your coding. If the question mark appears directly after the letter A, your monitor does not understand the .A assemble instruction; get another monitor or properly set up the one you have. 2. Or, it will correctly translate your instruction, and put the object code into memory starting at the address specified. In this case, that would happen to be $A9 at address $033C and $48 at address $033D. It would then help you by printing part of the next expected instruction. The computer expects that you will type a line starting with .A 033E It places the first part of this line on the screen to save you typing. The screen should now look like this: .A 033C LDA #$48 .A 033E You may now complete the instruction by typing in JSR $FFD2 and pressing RETURN. Again, the computer will anticipate the next line by printing .A 0341, which allows you to type in the final command, BRK. The screen now looks like this: .A 033C LDA #$48 .A 033E JSR $FFD2 .A 0341 BRK .A 0342 The computer is still waiting for another instruction. We have no more instructions, so we press RETURN to signal that we're finished. At this point, our program is stored in memory. The instructions have been assembled directly into place, and the object code is hopefully ready to go. Note that this saves us the trouble of remembering--or looking up--the op codes for each instruction. And we don't need to keep track of how long each instruction should be; the assembler does it for us. If you like, you can display memory and look at the object program with the .M 033C 0341. You'll see the bytes of your program in memory: .:033C A9 48 20 D2 FF 00 xx xx :29: The first six bytes are your program. The last two bytes don't matter: they were whatever was in that part of memory before. We don't care what is there, since the program will stop when it reaches the BRK ($00) at address $0341; it won't be concerned with the contents of memory at $0342 or $0343. Checking: The Disassembler --------------------------- When we changed our source code into object code, we called this process of translation assembly, and we called a program that did the job an assembler. Now we're written a program and it's safely stored in memory. We have inspected memory and have seen the bytes there; but they are hard to read. It would be convenient if we could perform an inverse assembly, that is, take the contents of memory and translate it into source code. The monitor has this capability, called a disassembler. If we ask the computer to disassemble the code starting at $033C, it will examine the code there and establish that the contents ($A9) correspond to an LDA immediate command. It will then print for our information LDA #$48, which is much more readable than the original two bytes, A9 48. Give the command .D 033C and press RETURN. D stands for disassemble, of course, and the address must follow. The computer will now show a full screen of code. On the left is the address followed by the bytes making up the instruction. On the right is the reconstructed source code. The screen shows much more memory than our program needs. Again, we ignore all lines beyond address $0341, which is the last instruction of our program. Anything following is "junk" left in memory that the program does not use. An interesting feature of most disassembly listings is that the cursor is left flashing on the last line of the disassembly rather than on the line below. When you have a large program, this allows you to type the letter D followed by RETURN and the next part of your program will immediately be displayed. On the other hand, if you don't want to disassemble more code, press the cursor down key and move to a "clean" line before typing your next instruction. A disassembly is a good way to check for errors. If you find an error in the listing, you may correct that line by re-assembling it, using the .A command once again. Minor errors may be corrected directly on the left-hand side of the disassembly listing. In other words, suppose that you had incorrectly coded LDA #$58 during the assembly phase; when you perform the disassembly, this line will show as :30: .,033C A9 58 LDA #$58 You recognize that the 58 should be 48; you may move the cursor up--use the cursor home if you wish--and type over the value on the left-hand side. In this case, you place the cursor over the 5, type 4 to change the display to 48, and press RETURN. You will see from the display that the problem has been fixed. Running the Program ------------------- If necessary, move the cursor down to an empty line. Type the command .G 033C and the program will run. Again, it doesn't take long; the break back to the MLM seems instantaneous. Where's the letter H that we were supposed to print? It's hard to see, but it's there. Look at your .G 033C command and you'll see it. Project for enthusiasts: Can you add to the program and print HI? The ASCII code for the letter I is $49. Can you add again and print HI on a separate line? The ASCII code for a RETURN is $0D. Remember that you can find all ASCII codes in Appendix D; look in the column marked ASCII. Linking with BASIC ------------------ So far we have started up our programs with a .G (go) command from the MLM, and we have terminated our programs with a BRK command that returns us to the monitor. That's not a convenient way to run a program; most users would prefer to say RUN out of BASIC and have the computer do everything. We can link to a machine language program from BASIC and when the program is finished, it can return to BASIC and allow the BASIC program to continue to run. The commands we need are (BASIC) SYS--Go to a machine language subroutine at the stated address; (Machine language) RTS--Return to whoever called this subroutine. Let's change our machine language program first. We must change from BRK at the end to RTS (return from subroutine) so that when the program is finished it will return to BASIC. If you like, you may change it directly on the disassembly listing: disassemble and then type over the 00 byte that represents BRK with a value of 60. Press RETURN and you'll see that the instruction has now changed to RTS. Alternatively, you may re-assemble with :31: .A 033C LDA #$48 .A 033E JSR $FFD2 .A 0341 RTS Now return to BASIC (using the .X command). The computer will say READY; you may now call your program with a SYS command. Address $033C is 828 in decimal. Thus, we type SYS 828. When we press RETURN, the letter H will be printed. We're not finished. Any machine language subroutine may be called from a BASIC program. Type NEW, which clears out the BASIC work area; our machine language program is left untouched, since NEW is a BASIC command. Now enter the following program: 100 FOR J=1 TO 10 110 SYS 828 120 NEXT J How many times will our program at 828 ($033C) be called? How may times will the letter H be printed? Will they be on the same line or separate lines? Type RUN and see. Project for enthusiasts: Again, change the machine language program to say HI. Use your imagination. What else would you like the computer to say? Would you like to use colors or reverse font? We've achieved an important new plateau: BASIC and machine language working together. It's easier on the user, who doesn't have to learn specialized monitor commands. It's easier on the programmer, too, since things that are easy to do in BASIC can be written in that language; things that are clumsy or slow in BASIC can be written in machine language. We can get the best of both worlds. Let's distinguish our three different types of subroutine calls: GOSUB--calls a BASIC subroutine from a BASIC program. SYS--calls a machine language subroutine from a BASIC program. JSR--calls a machine language subroutine from machine language. :32: Loops ----- We know how to send characters to the screen, one at a time. But long messages, such as THE QUICK BROWN CAT..., might lead to tedious coding if we had to write an instruction for each letter to be sent. We need to set up a program loop to repeat the printing activity. Let's write a program to print the word HELLO followed by a RETURN. We must store the word HELLO somewhere in memory. It doesn't matter where, provided it doesn't conflict with anything else. I'll arbitrarily choose address $034A to $034F. We'll put it there in a moment. Remember that the characters that make up the word HELLO (plus the RETURN) are not program instructions; they are simple data. We must put them in a place with a memory change--we must not try to assemble them. We will need to count the characters as we send them. We wish to send six characters, so a count of six is our limit. Let's use the X register to keep track of the count. First, we must set X to zero: .A 033C LDX #$00 Note that we use the # symbol to denote an immediate value: we want to load X with the value zero, not something from address 0. Now, we'll do something new. I want to take a character to be printed from address $034A. But wait, that's only the first time around. When we come back to this point in the loop, I want to take a character from $034B, and then from $034C, and so on. How can we do this? It seems that we must write one address into the LDA instruction, and that address can't change. But there is a way. We can ask the computer to take the address we supply, and add the contents of X or Y to this address before we use it. The computed address is called an effective address. Let's look at our position. The first time around the loop, X is counting the characters and has a value of zero. If we specify our address as 034A+X, the effective address will be 034A. That's where we will have stored the letter H. When we come back around the loop--we haven't written that part yet--X should now equal one. An address of 034A+X would give an effective address of 034B: the computer would go there and get the letter E. As we go around the loop, the letters, L, L, O, and RETURN will be brought in as needed. As we enter the LDA instruction, we don't type the plus sign. Instead, we signal indexing with a comma: LDA $034A,X. We may use either X or Y for indexing: they are sometimes called index register. In this case, of course, we use X. So we code :33: .A 033E LDA $034A,X .A 0341 JSR $FFD2 The first time, the computer loads the contents of address $034A (the letter H of HELLO) and prints it. When the loop comes back here, with X equal to one, this instruction will load the contents of $034B and print the letter E. The X register counts the number of letters printed, so we must add one to the contents of X. There's a special command that will add one to the contents of X: INX, for increment X. A similar code, INY, allows Y to be incremented; and DEX (decrement X) and DEY (decrement Y) allow X or Y to be decremented, or reduced, by one. At the moment, INX is the one we need for counting: .A 0344 INX Now we can test X to see if it is equal to six yet. The first time around, it won't be since X started at zero and was incremented to a value of 1. If X is not equal to six, we'll go back to $033E and print another letter. Here's how we code it: .A 0345 CPX #$06 .A 0347 BNE $033E CPX stands for compare X; note that we are testing for an immediate value of six, so we use the # symbol. BNE means branch not equal; if X is not equal to six, back we go to address $033E. A little careful thought will reveal that the program will go back five times for a total of six times around the loop. It's exactly what we want. Let's show the whole code, completing it with RTS: .A 033C LDX #$00 .A 033E LDA $034A,X .A 0341 JSR $FFD2 .A 0344 INX .A 0345 CPX #$06 .A 0347 BNE $033E .A 0349 RTS We may now put the characters for HELLO into memory. These are data, not instructions, so we must not try to assemble them. Instead, we change memory in the usual way, by displaying and then typing over. We give the command .M 034A 034F, and type over the display to show .:034A 48 45 4C 4C 4F 0D xx xx :34: By a lucky coincidence, this data fits exactly behind our program. Everything should be ready now. Disassemble the program at $033C and check it. You may note that the data at $034A doesn't disassemble too well, but that's to be expected; these bytes are not instructions and cannot be decoded. When all looks well, return to BASIC (with .X) and try SYS 828. The computer should say HELLO. Once again, set up a BASIC loop program: 100 FOR J=1 TO 3 110 SYS 828 120 NEXT J A Comment on SAVE ----------------- If you wished to save the program to cassette tape, you'd have a problem on the VIC or Commodore 64. The machine language program is in the cassette buffer; a save-to-tape command would cause the contents of the buffer to be destroyed before the program could be written to tape. Even disk commands would not be completely safe: 4.0 BASIC disk commands use the cassette buffer area as a work area; using these commands would probably destroy our machine language program. But saving the program is not the main problem. A correctly saved program can give trouble when you try to bring it back and run it safely. The difficulty is related to BASIC pointers, especially the start-of-variables pointer. The problem, and how to solve it, will be discussed in some detail in Chapter 6. A Stopgap SAVE -------------- We can preserve short programs by making them part of DATA statements. The procedure is not difficult if screen editing is used intelligently. We note that the program extends from $033C to $034F, including the message (HELLO) at the end. The decimal equivalents to these addresses are 828 to 847. Enter the following BASIC line: FOR J=828 TO 847:PRINT PEEK(J);:NEXT J Study the above line. You will see that it asks BASIC to go through the part of memory containing your machine language program, and display the contents (in decimal notation, of course). You'll see a result that looks something like this: :35: 162 0 189 74 3 32 210 255 232 224 6 208 245 96 72 69 76 76 79 13 These are indeed the bytes that make up your program. With a little study, you could reconstruct the 162-0 combination to be LDX #$00, or the 72-69-76- 76-69 at the end to be the word HELLO in ASCII. It looks different when it's in decimal, but it's still the same numbers. You may try a little skill and artistry, using screen editing to perform the next activity, or you may just retype the numbers into data lines as shown. Either way, arrange the numbers as follows: 50 DATA 162,0,189,74,3,32,210,255,232,224,6 60 DATA 208,245,96,72,69,76,76,79,13 We now have a copy of our program, exactly the way it appears in memory, but stored within DATA statements. The DATA statements are part of a normal BASIC program, of course, and will SAVE and LOAD with no trouble at all. We can now reconstruct our machine language program, placing it back into memory, with a simple BASIC POKE program: 80 FOR J=828 TO 847:READ X:POKE J,X:NEXT J Now our program is safe and sound--it handles like BASIC, but it will do a machine language task for us as desired. Let's display the entire BASIC program 50 DATA 162,0,189,74,3,32,210,255,232,224,6 60 DATA 208,245,96,72,69,76,76,79,13 80 FOR J=828 TO 847:READ X:POKE J,X:NEXT J 100 FOR J=1 TO 3 110 SYS 828 120 NEXT J This method of saving a machine language program is clean and trouble free, but it becomes awkward where long programs are involved. More advanced methods will be discussed in Chapter 6. Things You Have Learned ----------------------- - Subroutines can be called from machine language using the JSR command. There are several useful kernal subroutines permanently available. - A BASIC program may call a machine language program as a subroutine: the BASIC command is SYS. The machine language subroutine returns to the calling point with an RTS (return from subroutine) instruction. :36: - The CHROUT subroutine at address $FFD2 allows output of a character, usually to the screen. In addition to printable characters, special cursor- and color-control characters may be sent. - Most machine language monitors have a small assembler to help program preparation, and a disassembler to assist in program checking. - Immediate mode is signaled by use of the # symbol. The computer is asked to take the value given, instead of going to a specified address for its data. - X and Y are called index registers. We may add the contents of X or Y to a specified address, to create an effective address that changes as the program runs. This addition is called indexing. - X and Y also have special instructions that increase or decrease the selected register by one. These are called increment and decrement instructions, and are coded INX, INY, DEX, and DEY. Questions and Projects ---------------------- Look through the table of ASCII characters in Appendix D. note that hex 93 is "clear screen." Write a program to clear the screen and print "HO HO!". You may have noticed that in our example, we had register X counting up from zero to the desired value. What would happen if you started X at 5 and counted down? Try it if you like. Remember that you can also include cursor movements, color codes (if your machine has color), and other special ASCII characters. Could you lay out the coding to draw a box? (Try it in BASIC first). Draw a box with the word HELLO inside it. :37: :38: :39: Chapter 3 Flags, Logic, and Input This chapter discusses: o Flags that hold status information o Testable flags: Z, C, N, and V o Signed numbers o The status register o First concepts of interrupt o Logical operators: OR, AND, EOR o The GETIN subroutine for input o The STOP subroutine :40: Flags ----- Near the end of Chapter 2, we coded a program that had the seemingly natural sequence CPX #$06 BNE $.... It made sense: compare X for a value of 6, and if not equal, branch back. Yet it implies something extraordinary; the two instructions are somehow linked. Let's flash forward for a moment. Even when you have a machine language program running, the computer "freezes" sixty times a second. The computer undertakes a special activity, called interrupt processing. It stops whatever it was doing, and switches to a new set of programs that do several tasks: flashing the cursor, checking the keyboard, keeping the clock up to date, and checking to see whether the cassette motor needs power. When it's finished, it "unfreezes" the main program and lets it continue where it left off. This interrupt might take place between the two instructions shown above, that is, after the CPX and before the BNE. Hundreds of interrupt instructions might be executed between the two, yet nothing is harmed. The two instructions work together perfectly to achieve the desired effect. How can the computer do this? The two instructions are linked by means of a flag--a part of the 650x that records that something has happened. The CPX instruction tests X and turns a special flag on or off to signal how the comparison turned out: equal or unequal. The BNE instruction tests that flag. If it's on (meaning equal), no branch will take place and the program will continue with the next instruction; if it's off (meaning not equal), a branch will take place. In other words, some instructions leave a "trail" of status information; other instructions can check this information. The status information is called "flags." There are four flags that may be tested: Z, C, N, and V. They are discussed below. Z Flag ------ The Z (zero) flag is probably misnamed, and should have been called the E flag (for "equals"). After any comparison (CPX to compare X, CPY to compare Y, or CMP to compare A), the Z flag will be set to "on" if the compared values are equal; otherwise it will be reset to "off." :41: Sometimes the X flag checks for equal to zero, hence its name, Z for zero. This happens for every activity that may change one of the three data registers. Thus, any load command will affect the Z flag status. The same is true of increment and decrement instructions, which obviously change registers. And later, when we meet other operations such as addition and subtraction, they too will affect the Z flag. There are many instructions that don't affect the Z flag (or any flag, for that matter). Store instructions (STA, STX, STY), never change a flag. Branch instructions test flags but don't change them. An example will help illustrate the way that some instructions change flags and others do not. Examine the following coding: LDA #$23 (Load 23 to A) LDX #$00 (Load zero to X) STA $1234 (store 23 to address $1234) BEQ $.... Will the branch (BEQ) be taken, or will the 650x continue with the next instruction? Let's analyze the Z flag's activity step by step. The first instruction (LDA #$23) resets the Z flag, since 23 is not equal to zero. The second instruction (LDX #$00) sets the Z flag because of the zero value. The third instruction (STA $1234) does not affect the Z flag; in fact, store instructions do not affect any flags. Thus, by the time we reach the BEQ instruction, the Z flag is set "on" and the branch will be taken. 650x reference manuals show the specific flags that are affected by each instruction. In case of doubt, they are easy to check. The Z flag is quite busy--it clicks on and off very often since many instructions affect it. It's an important flag. If the Z flag is set "on," the BEQ (branch equals) instruction will branch to the specified address; otherwise it will be ignored and the next instruction in sequence will be executed. If the Z flag is reset "off," the BNE (branch not equals) instruction will branch. We can see in more detail how our program from Chapter 2 worked. CPX #$06 causes the Z flag to be set "on" if X contains the value 6; otherwise it causes the Z flag to be reset "off." BNE tests this flag, and branches back to the loop if the Z flag is off--in other words, only if the contents of X is not equal to six. :42: C Flag ------ The C (carry) flag is probably misnamed, too. It should have been called the GE (greater/equal) flag, since after a comparison (CPX, CPY or CMP), the C flag is set "on" if the register (X, Y, or A) is greater than or equal to the value compared. If the register concerned is smaller, the C flag will be reset "off." The C flag is not as busy as the Z flag. The C flag is affected only by comparison instructions and by arithmetic activities (add, subtract, and a type of multiplication and division called rotate or shift). When used in arithmetic, the C flag is properly named, since it acts as a "carry" bit between various columns as they are calculated. For example, an LDA instruction always affects the Z flag since a register is being changed, but never affects the C flag since no arithmetic or comparison is being performed. If the C flag is set "on," the BCS (branch carry set) instruction will branch to the specified address; otherwise it will be ignored and the next instruction in sequence will be executed. If the C flag is reset "off," the BCC (branch carry clear) instruction will branch. The C flag may be directly set or reset by means of the instructions SEC (set carry) and CLC (clear carry). We will use these instructions when we begin to deal with addition and subtraction. If you examine the last program of Chapter 2, you will see that the BNE instruction could be replaced by BCC. Instead of "branch back if not equal to 6," we could code "branch back if less than 6." The operation would be the same in either case. N Flag ------ The N (negative) flag is also probably misnamed. It should have been called the HB (high bit) flag, since numbers are positive or negative only if they are used in a certain way. The N flag is set to indicate that a register has been given a value whose high bit is set. The N flag is as busy as the Z flag; it changes with every instruction that affects a register. The N flag is affected by comparisons, but in this case its condition is not usually meaningful to the computer. To sort out the operation of the N flag, it's important to become familiar with hexadecimal-to-binary conversion. For example, will LDA #$65 set the N flag? Rewrite it into binary: $65 equals %01100101. We can see that the high bit is not set, meaning that the N flag will be off after loading this value. As another example, suppose we LDX #$DA. Hex DA is 11011010 binary. We see that the high bit is on and thus the N flag is set. :43: If the N flag is set "on," the BMI (branch minus) instruction will branch to the specified address; otherwise it will be ignored and the next instruction in sequence will be executed. If the N flag is reset "off," the BPL (branch plus) instruction will branch. A Brief Diversion: Signed Numbers ---------------------------------- How can a location--which is usually thought to contain a decimal value from 0 to 255--contain a negative number? It's up to the programmer to decide whether a memory value is unsigned, having a value range from 0 to 255, or signed, having a value range from -128 to +127. There are still a total of 256 possibilities. The computer's memory simply holds bits, while the programmer decides how the bits are to be used in a specific case. Mathematically, it's described this way: signed numbers, if desired, are held in two's-complement form. We can hold -1 as hex FF, and -2 as hex FE, all the way down to -128 as hex 80. You may have noticed that in all the examples, the high bit is set for these negative numbers. We may need more intuitive help, however. If the computer loads the decimal value 200 into the A register with LDA #$C8, the N flag will be set and will seemingly indicate that 200 is a negative number. It may be more comfortable to simply think of 200 as a number with the high bit set. But in a sense, 200 could be a negative number if we wanted it to be. Let's examine the situation by means of examples. If I were asked to count down in hexadecimal from 10, I'd start out with $10, $0F, $0E, and $0D, continuing down to $02, $01, and $00. If I needed to keep going, I'd continue past $00 with $FF; in this case, hex FF would clearly represent negative one. Continuing, FE, FD, and FC would represent -2, -3, and -4. And the high bit is set on all these "negative" numbers. Let's discuss a decimal analogy. Suppose you have a cassette recorder with a counter device attached, and the counter reads 0025. If you rewind the unit a distance of 30 units, you would not be surprised to see a value of 9995 on the counter and would understand that it meant a position of -5. If you had a car with 1,500 miles on the odometer, and "rolled back" the mileage by 1,501 miles, you'd see a reading of 99999, which would mean -1. (The author does not know this from personal experience, but is assured by many machine language students that it is so.) In these cases, based on the decimal system, the negative numbers are called "ten's complement." :44: V Flag ------ As with the other flags, the V (overflow) flag is probably misnamed. It should have been called the SAO (signed arithmetic overflow) flag, since it is affected only by addition and subtraction commands, and is meaningful only if the numbers concerned are considered to be signed. The V flag is used only occasionally in typical 650x coding. Many machine language programs don't use signed numbers at all. The most typical use of the V flag is in conjunction with a rather specialized command, BIT (bit test). For this instruction, the V flag signals the condition of bit 6 of the memory location being tested. In this case, V and N work in a similar way: N reflects the high bit, bit 7, and V represents the "next bit down," bit 6. The BIT command is used primarily for testing input/output ports on IA (interface adapter) chips. If the V flag is set "on," the BVS (branch overflow set) instruction will branch to the specified address; otherwise it will be ignored and the next instruction in sequence will be executed. If the V flag is reset "off," the BVC (branch overflow clear) instruction will branch. The V flag may be directly reset by means of the CLV (clear overflow) instruction. Oddly, there is no equivalent instruction to set the flag. One special feature of the V flag: on some 650x chips, the V flag can be set by hardware. There is a pin on the chip that can be used so that an external logic signal will trigger the V flag. A Brief Diversion: Overflow ---------------------------- The term overflow means "the result is too big to fit." For example, if I add 200 to 200, the result is 400...but this won't fit in a single byte. If we have only a single byte to store the result, we say that the addition has encountered overflow, and we can't produce a meaningful answer. If we are using unsigned numbers, the C flag tells us about overflow. If we are using signed numbers, V tells the story. We'll take this up again in the next chapter. :45: Flag Summary ------------ A brief table may help review the four testable flags. Flag Brief Activity Branch taken if: Name Meaning Level Set Not Set ------------------------------------------------------------------ Z Zero, equal Busy BEQ BNE C Carry, greater/equal Quiet BCS BCC N Negative, high-bit Busy BMI BPL V Signed arithmetic overflow Quiet BVS BVC The Status Register ------------------- The preceding flags--and three others--may be viewed within the status register (SR). You may recall that the machine language monitor gives an SR display. If you know how to read it, you can see the condition of all flags. Each flag is a bit within the status register. Again, it's useful to be able to easily translate the hexadecimal display, so as to view the individual flags. Here's a chart of the flags within the status register: 7 6 5 4 3 2 1 0 N V - B D I Z C Taking the bits one at a time, starting at the high bit: N--the N flag, as above. V--the V flag, as above. Bit 5--unused. You'll often find that this bit is "on." B--"Break" indicator. When an interrupt occurs, this signals whether or not the interrupt was caused by a BRK instruction. D--Decimal mode indicator. This changes the manner in which the add and subtract instructions operate. In Commodore machines, this flag will always be off. Don't turn it on unless you know exactly what you're doing. This flag may be turned on with the SED (set decimal) instruction, and turned off with the CLD (clear decimal) instruction. I--Interrupt disable. More exactly, this bit disables the IRQ (interrupt request) pin activity. More on this control bit much later. This flag may be turned on with the SEI (set interrupt disable) instruction, and turned off with the CLI (clear interrupt disable) instruction. Z--the Z flag, as above. C--the C flag, as above. :46: Flags B, D, and I are not testable flags in that there are no branch instructions that test them directly. D, the decimal mode flag, and I, the interrupt lockout flag, may be considered "control" flags. Instead of reporting conditions found as the program runs, they control how the program operates. When we see a value displayed in the SR, or status register, we may examine it to determine the condition of the flags, especially the testable flags Z, C, N, and V. For example, if wee see an SR value of $B1, we translate to binary %10110001 an know that the N flag is on, the V flag is off, the Z flag is off, and the C flag is on. You may change these flags by typing over the displayed value in the machine language monitor. Be careful you don't accidentally set the D or I flags. A Note on Comparison -------------------- If we wish to compare two bytes with each other, we must perform a comparison. One value must be in a register (A, X, or Y); the other must be either stored in memory, or must be an immediate value we use in the instruction. We will use the appropriate compare instruction depending on the register involved; CMP for the A register, CPX for the X register, and CPY for the Y register. Following the comparison, we may use any of the following branch tests: BEQ--branches if the two bytes are equal. BNE--branches if the two bytes are not equal. BCS--branches if the value in the register is greater than or equal to the other value. BCC--branches if the value in the register is less than the other value. We can use more than one branch instruction after a comparison. Suppose our program wanted to test the Y register for a value equal or less than 5. We might code CPY #$05 BEQ ..somewhere BCC ..somewhere We can see that our code will branch if the value is equal to 5 (using the BEQ) or less than 5 (using the BCC); otherwise it will continue without branching. In this case, we could make the coding more efficient by changing it to read :47: CPY #$06 BCC ..somewhere A little common sense will tell us that testing a number to see if it is less than 6 is the same as testing it to see if it is less than or equal to 5. Common sense is a valuable programming tool. Instructions: A Review ----------------------- We have looked at the three data registers--A, X, and Y--and have seen three types of operation we can perform with them: Load: LDA LDX LDY Store: STA STX STY Compare: CMP CPX CPY Up to this point, the registers have identical functions, and we can use any of them for any of these functions. But new instructions are creeping in that give a different personality to each of the three. We have noted that INX, INY, DEX, and DEY for increment and decrement are restricted to X and Y only; and we've also mentioned that X and Y can be used for indexing. Soon, we'll start to examine some of the functions of the A register, which is often called the accumulator because of its ability to do arithmetic. We have seen JSR, which allows us to call a subroutine of prewritten instructions. We've used RTS, which says, "Go back to the calling point," even if the calling point is a BASIC program. And we've almost abandoned the BRK instruction, which stops the program and goes to the machine language monitor. BRK will be useful in checking out programs. Specifically, we can stop a program at any time by inserting a BRK instruction, allowing us to see whether the program is behaving correctly and whether it has done the things we planned. There are eight branch instructions. They have already been discussed, but there is one additional piece of information that is important to keep in mind. All branches are good only for short hops of up to a hundred memory locations or so. So long as we write short program, that won't be a limitation; but we'll look at this more closely in Chapter 5. Logical Operations ------------------ Three instructions perform what are called logical operations. They are: AND (Logical AND); ORA (Logical OR); and EOR (Exclusive OR). These instructions work on the A register only. :48: Mathematicians describe these operations as commutative. For example, a value of $3A "AND" $57 gives exactly the same result as $57 "AND" $3A. The order doesn't matter. But we often use these functions--and think of them-- in a particular order. It's the same as with addition, where we think of a "total" to which is added an "amount" to make a "new total." With the logical operators we often think of a "value," which we manipulate with a "mask" to make a "modified value." Logical operators work in such a way that each bit within a byte is treated independently of all the other bits. This makes these instructions ideal for extracting bits, or manipulating certain bits while leaving others alone. We'll look at formal definitions, but the following intuitive concepts are useful to programmers: AND--turns bits off. ORA--turns bits on. EOR--flips bits over. AND--Logical AND to A --------------------- For each bit in the A register, AND performs the following action: Original A Bit Mask Resulting A Bit ------------------------------------------ 0 0 0 1 0 0 0 1 0 1 1 1 Examine the upper half of this table. When the mask is zero, the original bit in A is changed to zero. Examine the lower half. When the mask is one, the original bit is left unchanged. Hence, AND can selectively turn bits off. Example: Turn off bits 4, 5, and 6 in the following value: $C7 Original value: 11000111 Mask: AND 10001111 (hex 8F) ------------ Result: 10000111 ^^^ Note that the bits marked have been forced to "off," while all the other bits remain unchanged. :49: ORA--Logical OR to A -------------------- For each bit in the A register, ORA performs the following action: Original A Bit Mask Resulting A Bit ------------------------------------------ 0 0 0 1 0 1 0 1 1 1 1 1 Examine the upper half of this table. When the mask is zero, the original bit in A is left unchanged. Examine the lower half. When the mask is one, the original bit is forced to "on." Hence, ORA can selectively turn bits on. Example: Turn on bits 4, 5, and 6 in the following value: $C7 Original value: 11000111 Mask: ORA 01110000 (hex 70) ------------ Result: 11110111 ^^^ Note that the bits marked have been forced to "on," while all the other bits remain unchanged. EOR--Exclusive OR to A ---------------------- For each bit in the A register, EOR performs the following action: Original A Bit Mask Resulting A Bit ------------------------------------------ 0 0 0 1 0 1 0 1 1 1 1 0 Examine the upper half of this table. When the mask is zero, the original bit in A is left unchanged. Examine the lower half. When the mask is one, the original bit is inverted; zero becomes one and one becomes zero. Hence, EOR can selectively flip bits over. Example: Invert bits 4, 5, and 6 in the following value: $C7 Original value: 11000111 Mask: EOR 01110000 (hex 70) ------------ Result: 10110111 ^^^ :50: Note that the bits marked have been flipped to the opposite value, while all the other bits remain unchanged. Why Logical Operations? ----------------------- We use these three commands--AND, ORA, and EOR--to change to control individual bits within a byte of information. The commands are unusual in that each bit may be manipulated independently of the others. We don't seem to be working with numbers when we use these commands. Rather, we're working with each individual bit, turning it on or off as we wish. Why would we turn individual bits on or off? There are several possible reasons. For example, we might wish to control external devices through the IA's (interface adapters). Within the IA's input and output ports each of the eight bits might control a different signal; we might want to switch one control line on or off without affecting other lines. When we're looking at input from an IA port, we often read several input lines mixed together within a byte. If we want to test a specific bit to see if it is on or off, we might mask out all the other bits with the AND instruction (changing unwanted bits to zero); if the remaining bit is zero, the whole byte will now be zero and the Z flag will be set. Why would we want to flip bits over? Many "oscillating" effects--screen flashing or musical notes--can be accomplished this way. Finally the logical operators can be useful in code translation. For example, here are the values for ASCII 5 and binary 5: ASCII %00110101 Binary %00000101 We must use the ASCII value for input or output. We must use the binary value for arithmetic, particularly addition and subtraction. How could we get from one to the other? By taking bits out (AND) or putting bits in (ORA). Alternatively, we could use addition or subtraction; the logical operators, however, are simpler. Input: The GETIN Subroutine ---------------------------- We have seen how we can use CHROUT at $FFD2 to produce output to the screen. Now we'll look at the input side--how to use the GETIN subroutine at $FFE4 to get characters from the keyboard buffer. :51: You may be familiar with the GET statement in BASIC. If so, you'll find the same characteristics in GETIN: o Input is taken from the keyboard buffer, not the screen. o If a key is held down, it will still be detected once only. o The subroutine returns immediately. o If no key is found, a binary zero is returned in A. o If a key is found, its ASCII value will be in A. o Special keys, such as RETURN, RVS, or color codes, will be detected. To call for a key from the keyboard, code JSR $FFE4. Values in X and Y are not guaranteed to be preserved, so if you have important information in either register, put it away into memory. *---------------------------------------------------------------------------* | | | Subroutine: GETIN | | Address: $FFE4 | | Action: Takes a character from the input channel and places it into | | the A register. The input channel is the keyboard input | | buffer unless arrangements have been made to switch it. | | | | The character received is usually ASCII (or PET ASCII). When read from | | the keyboard, the action is similar to a BASIC GET statement: one | | character will be taken from the buffer; it will not be shown on the | | screen. If no character is available from the keyboard input buffer, a | | value of binary zero will be put into the A register. The subroutine | | will not wait for a key to be pressed but will always return immediately. | | | | Registers: The A register will or course always be affected. X and Y | | are likely to be changed; do not have data in these when calling GETIN. | | | | Status: Status flags may be changed. In the VIC and Commodore 64, the C | | (carry) flag indicates some type of problem with output. | | | *---------------------------------------------------------------------------* If we want keyboard input to appear on the screen, we should follow a call to GETIN, $FFE4, with a call to CHROUT, $FFD2, so that the received character is printed. STOP ---- Machine language programs will ignore the RUN/STOP key...unless the program checks this key itself. It may do so with a call to STOP, address $FFE1. This checks the RUN/STOP key at that moment. To make the key operational, $FFE1 must be called frequently. :52: A call to FFE1 should be followed by a BEQ to a program exit so that the program will terminate when RUN/STOP is pressed. The RUN/STOP key is often brought into play while programs are being tested, so that unexpected "hang-ups" can still allow the program to be terminated. Coding to test the RUN/STOP key is often removed once testing is complete, on the assumption that no one will want to top a perfect program. Incidentally, if you plan to write nothing but 100 percent perfect programs, you will not need to use this subroutines. *---------------------------------------------------------------------------* | | | Subroutine: STOP | | Address: $FFE1 | | Action: Check the RUN/STOP key. If RUN/STOP is being pressed at | | that instant, the Z flag will be set when the subroutine | | returns. | | | | In PET/CBM, the system will exit to BASIC and say READY if the RUN/STOP | | key is being pressed. In this case, it will not return to the calling | | machine language program. | | | | Registers: A will be affected. X will be affected only if the RUN/STOP | | key is being pressed. | | | | Status: Z signals whether RUN/STOP is being pressed. | | | *---------------------------------------------------------------------------* Programming Project ------------------- Here's our task: we wish to write a subroutine that will wait for a numeric key to be pressed. All other keys (except RUN/STOP) will be ignored. When a numeric key is pressed, it will be echoed to the screen, and then the subroutine will be finished. One more thing. The numeric character will arrive in ASCII from the keyboard: we wish to change it to a binary value before giving the final RTS statement. This last operation has no useful purpose yet, except as an exercise, but we'll connect it up in the next chapter. Coding sheets ready? Here we go. .A 033C JSR $FFE1 We will check the RUN/STOP key first. But wait. Where will we go if we find that the key is pressed? To the RTS, of course; but we don't know where that is, yet. In these circumstances, we usually make a rough guess and correct it later. Make a note to check this one... :53: .A 033F BEQ $0351 .A 0341 JSR $FFE4 Now we've gotten a character; we must check that it's a legitimate numeric. The ASCII number set 0 to 9 has hexadecimal values $30 to $39. So if the value is less than $30, it's not a number. How do we say "less than?" After a compare, it's BCC (branch carry clear). So we code .A 0344 CMP #$30 .A 0346 BCC $033C Did you spot the use of immediate mode at address $0344? Make sure you follow the logic on this. Another point: what if no key has been pressed? We're safe. There will be a zero in the A register, which is less than hex 30; this will cause us to go back and try again. Now for the high side. If the number is greater than hex 39, we must reject it since it cannot be an ASCII numeric. Our first instinct is to code CMP #$39 and BCS. But wait! BCS (branch carry set) means "branch if greater than or equal to." Our proposed coding would reject the digit 9, since the carry flag would be set when we compared to a value of hex 39. We must check against a value that is one higher than $39. Be careful, though, for we're in hexadecimal. The next value is $3A. Code it: .A 0348 CMP #$3A .A 034A BCS $033C If we get this far, we must have an ASCII character from 0 to 9; let's print it to the screen so that the user gets visual feedback that the right key has been pressed: .A 034C JSR $FFD2 Now for our final task. We are asked to change the ASCII character into true binary. We may do this by knocking off the high bits. We remember, of course, that to turn bits off we must use AND: .A 034F AND #$0F .A 0351 RTS It's a good thing that we printed the character first, and then converted to binary; the character must be ASCII to print correctly. One last thing. We had a branch (on the RUN/STOP key) that needed to connect up with the RTS. Did you make that note about going back and fixing up the branch? Now is the time to do it, but before you go back, terminate the assembly with an extra RETURN on the keyboard (the assembler gets confused if it prompts you for one address and you give another; get out before you go back). :54: By a fortunate stroke of luck, we happen to have guessed the right address for the BEQ at address $033F. But if we hadn't, you know how to change it, don't you? Check your coding, disassemble, go back to BASIC and run with a SYS 828. Tap a few letter keys and note that nothing happens. Press a number, and see it appear on the screen. The program will terminate. SYS it again and see if the RUN/STOP works. Try a BASIC loop to confirm that BASIC and machine language work together. Project for enthusiasts: Try modifying the program so that it checks for alphabetic characters only. Alphabetic characters run from $41 to $5A, inclusive. Things You Have Learned ----------------------- - Flags are used to link instructions together. This might be an activity such as load or compare, followed by a test such as branch on a given condition. - Some instructions affect one or more flags, and some do not affect flags. Thus, and instruction that sets a flag might not be followed immediately with the instruction that tests or uses that flag. - There are four testable flags: Z (zero, or equals); C (carry, or greater/ equal); N (negative, or high bit); and V (signed arithmetic overflow). The flags are checked by means of "branch" instructions such as BEQ (branch equal) or BNE (branch not equal). - Flags are stored in the status register, sometimes called the processor status word. The SR contains the four testable flags, plus three other flags: B (break indicator); D (decimal mode for add/subtract); and I (interrupt lockout). The hexadecimal value in SR can be changed to binary and used to determine the exact condition of all flags. - Usually, the processor is interrupted sixty times a second to do special high-priority jobs. Everything, including the status register flags, is carefully preserved so that the main program can continue as though nothing had happened. - A number stored in memory can be considered as signed if we decide to handle it that way. The value of a signed number is held in two's- complement form. The high bit of the number is zero if the number is positive, one if the number is negative. The computer doesn't care. It handles the bits whether the number is considered signed or not, but we must write our program keeping in mind the type of number being used. :55: - There are three logical operator instructions: AND, ORA, and EOR. These allow us to modify bits selectively within the A register. AND turns bits off; ORA turns bits on; and EOR inverts bits, or flips them over. Questions and Projects ---------------------- Write extra coding to allow both numeric and alphabetic characters, but nothing else. Write a program to accept only alphabetic characters. As each ASCII character is received, turn on its high bit with ORA #$80 and then print it. How has the character been changed? Write a program to accept only numeric digits. As each ASCII character is received, turn off its lowest bit with AND #$FE and then print it. What happens to the numbers? Can you see why? :56: :57: Chapter 4 Numbers, Arithmetic, and Subroutines This chapter discusses: o Numbers: signed and unsigned o Big numbers: multiple bytes o Arithmetic: add and subtract o Rotate and shift instructions o Multiplication o Home grown subroutines :58: Numbers: Signed and Unsigned ----------------------------- We have looked briefly at the question of signed versus unsigned numbers. The most important concept is that you, the programmer, choose whether or not a number is to be considered a signed number (for a single byte, in the decimal range -128 to +127) or an unsigned integer (single-byte range 0 to 255). It makes no difference to the computer. If you consider a number signed, you may wish to test the sign using the N flag. If not, you won't do such a test. Big Numbers: Multiple Bytes ---------------------------- You may use more than one byte to hold a number. Again, it's your decision. If you think the numbers may go up to a million, you might allocate three bytes (or more or fewer). If you are doing arithmetic on multi-byte numbers, the computer will help you by signaling in the carry flag that there's something to be carried across from a lower byte to a higher one. But it's up to you to write the code to handle the extra bytes. You may size numbers by using the following table: Unsigned: Signed: ----------------------------------------------------------- 1 byte 0 to 255 -128 to +127 2 bytes 0 to 65,535 -32,768 to +32,767 3 bytes 0 to 16,777,215 -8,388,608 to +8,388,607 4 bytes 0 to over 4 billion -2 billion to +2 billion It's possible to work with binary fractions, but that is beyond the scope of this book. Many applications "scale" numbers, so that dollar-and-cents amounts are held as integer quantities of pennies. Thus, two bytes unsigned would hold values up to $655.35, and three bytes up to $167,772.15. When signed numbers are held in multiple bytes, the sign is the highest bit of the highest byte only. We will concentrate on single-byte arithmetic principles here, touching on multiple-byte numbers as a generalization of the same ideas. Addition -------- Principles of addition are similar to those we use in decimal arithmetic; for decimal "columns," you may substitute "bytes." Let's look at a simple decimal addition: :59: 142856 + 389217 -------- Rule 1: We start at the right-hand column (the low-order byte). Rule 2: We add the two values, plus any carry from the previous column. A new carry may be generated; it can never be greater than one. (ADC includes any carry from a previous activity, and may generate a new carry bit, which is either 0 or 1.) Rule 3: When we start at the right-hand column, there is no carry for the first addition. (We must clear the carry with CLC before starting a new addition.) Rule 4: When we have finished the whole addition, if we have a carry and no column to put it in, we say the answer "won't fit." (If an addition sequence of unsigned numbers ends up with the carry flag set, it's an overflow condition.) HIGH BYTE LOW BYTE Start: 00101011 10111001 No carry 00001010 11100101 / -------------------------/ 10011110 / / Carry / / 00110110 Figure 4.1 How do we translate these rules into machine language addition? 1. Before we start an addition sequence, clear the carry with CLC. 2. If the numbers are more than one byte in size, start at the low byte and work up to the high ones. Addition will take place in the A register only; you may add the contents of an address or an immediate value. The carry flag will take care of any carries. 3. When the addition sequence is complete, check for overflow: a) if the numbers are unsigned, a set C flag indicates overflow; b) if the numbers are signed, a set V flag indicates overflow. Thus, to add two unsigned numbers located at addresses $0380 and $0381 and to place the result at $0382, we might code :60: CLC LDA $0380 ADC $0381 STA $0382 We might also BCS to an error routine, if desired. To add a two-byte number located at $03A0 (low) and $03A1 (high) to another two-byte number located at $03B0 (low) and $03B1 (high), placing the result at $03C0/1, we might code CLC LDA $03A0 ADC $03B0 STA $03C0 LDA $03A1 ADC $03B1 STA $03C1 Again, we might BCS to an overflow error routine. If we had two-byte signed numbers in the same locations, we'd add them exactly the same way, using the same code as above. In this case, however, we'd check for overflow by adding the instruction BVS, which would branch to an error routine. The carry flag would have no meaning at the end of the addition sequence. Subtraction ----------- Subtraction might be defined as "upside down" addition. The carry flag again serves to link the parts of a multi-byte subtraction, but its role is reversed. The carry flag is sometimes called an "inverted borrow" when used in subtraction. Before performing a subtraction, we must set the C flag with SEC. If we are worried about unsigned overflow, we look to confirm that the carry is set at the completion of the subtraction operation. If the carry is clear, there's a problem. Thus, to perform a subtraction, we follow these rules: 1. Before we start a subtraction sequence, set the carry with SEC. 2. If the numbers are more than one byte in size, start at the low byte and work up to the high ones. Subtraction will take place in the A register only; you may subtract the contents of an address or an immediate value. The C flag will take care of any "borrows." 3. When the subtraction sequence is complete, check for overflow: a) if the numbers are unsigned, a clear C flag indicates overflow; b) if the numbers are signed, a set V flag indicates overflow. :61: Thus, to subtract two unsigned numbers located at addresses $0380 and $0381 and to place the result at $0382, we might code SEC LDA $0380 SBC $0381 STA $0382 A BCC could go to an error routine. Comparing Numbers ----------------- If we have two unsigned numbers and wish to know which one is larger, we can use the appropriate compare instruction--CMP, CPX, or CPY--and then check the carry flag. We've done this before. If the numbers are more than one byte long, however, it's not quite so easy. We must then use a new technique. The easiest way to go about such a comparison is to subtract one number from the other. You need not keep the result; all you care about is the carry flag when the subtraction is complete. If the C flag is set, the first number (the one you are subtracting from) is greater than or equal to the second number. Why? Because carry set indicated that the unsigned subtraction was legal; we have subtracted the two numbers and have obtained a positive (unsigned) result. On the other hand, if the C flag ends up clear, this would mean that the first number is less than the second. The subtraction couldn't take place correctly since the result--a negative number--can't be represented in unsigned arithmetic. Left Shift: Multiplication by Two ---------------------------------- If we write the decimal numbers 100 and 200 in binary, we see an interesting pattern: 100: %01100100 200: %11001000 To double the number, each bit has moved one position to the left. This makes sense, since each bit has twice the numeric "weight" of the bit to its right. The command to multiply a byte by two is ASL (arithmetic shift left). A zero bit is pushed into the low (or "right") side of the byte; all bits move left one position; and the bit that "falls out" of the byte--in this case, a zero bit--moves into the carry. It can be diagrammed like this: :62: +-----+----+----+----+----+----+----+-----+ | | | | | | | | | CARRY <---- <--- <--- <--- <--- <--- <--- <--- <---- 0 (C FLAG) | | | | | | | | | +-----+----+----+----+----+----+----+-----+ ASL IN AN ASL (ARITHMETIC SHIFT LEFT), EACH BIT MOVES ONE POSITION LEFT. A ZERO MOVES INTO THE LOW-ORDER BIT. Figure 4.2 That's good for doubling the value of a single byte. If a "one" bit falls into the carry flag, we can treat that as an overflow. What about multiple bytes? It would be ideal if we had another instruction that would work just like ASL. Instead of pushing a zero bit into the right hand site of the byte, however, it would push the carry bit, that is, the bit that "fell out" of the last operation. We have such an instruction: ROL. ROL (rotate left) works exactly like ASL except that the carry bit is pushed into the next byte. We can diagram it as follows: CARRY +-----+----+----+----+----+----+----+-----+ | | | | | | | | | | | ,----- <--- <--- <--- <--- <--- <--- <--- <----' | | | | | | | | | | | +-----+----+----+----+----+----+----+-----+ v ROL CARRY IN A ROL (ROTATE LEFT), THE CARRY MOVES INTO THE LOW ORDER BIT; EACH BIT MOVES LEFT; AND THE HIGH ORDER BIT BECOMES THE NEW CARRY. Figure 4.3 Thus, we can hook to or more bytes together. If they hold a single multi-byte number, we can double that number by starting at the low-order end. We ASL the first value and ROL the remainder. As the bits fall out of each byte, they will be picked up in the next. Multiplication -------------- Multiplying by two may not seem too powerful. We can build on this starting point, however, and arrange to multiply by any number we choose. :63: ASL 0 +---------+ | | | | ,---- <- <- <---' | | | ROL | +---------+ +---------+ | LOW ORDER BYTE | | v ,---- <- <- <---CARRY | | | ROL | +---------+ +----------+ | | | v ,---- <- <- <---CARRY | | | | +----------+ | HIGH ORDER BYTE v TO MULTIPLY A THREE-BYTE NUMBER BY TWO, WE SHIFT THE LOW ORDER BYTE WITH ASL; THEN WE USE ROL TO ALLOW THE C FLAG TO "LINK" FROM ONE BYTE TO THE NEXT. Figure 4.4 We won't deal with a generalized multiplication routine here, but a couple of specific examples can be shown. How can we multiply by four? Multiply by two, twice. How can we multiply by eight? Multiply by two, three times. Here's an important one. We often want to multiply by ten. For example, if a decimal number is being typed in at the keyboard, the number will arrive one digit at a time. The user might type 217, for example. The program must then input the two and put it away; when the one arrives, the two must be multiplied by ten, giving twenty, and the one added; when the seven is typed, the twenty-one must be multiplied by ten before the seven is added. Result: 217 in binary. But we must first know how to multiply by ten. To multiply by ten, you first multiply by two; then multiply by two again. At this point, we have the original number times four. Now, add the original number, giving the original number times five. Multiply by two one last time and you've got it. We'll see an example of this in Chapter 7. Right Shift and Rotate: Dividing by Two ---------------------------------------- If we can multiply by two by shifting (and rotating) left, we can divide by two by moving the bits the other way. If we have a multi-byte number, we must start at the high end. LSR (logical shift right) puts a zero into the left (high-order) bit, moves all the bits over to the right, and drops the leftover bit into the carry. ROR (rotate right) puts the carry into the left bit, moves everything right, and drops the leftover bit into the carry once again. At the end of a right- shifting sequence, the final carry bit might be considered a remainder after dividing by two. :64: LSR +-----+----+----+----+----+----+----+-----+ | | | | | | | | | 0 -----> ---> ---> ---> ---> ---> ---> ---> ----> CARRY | | | | | | | | | (C FLAG) +-----+----+----+----+----+----+----+-----+ IN AN LSR, ZERO MOVES INTO THE HIGH BIT, AND ALL BITS MOVE RIGHT ONE POSITION; THE LOWEST BITS BECOME THE CARRY. CARRY ROL | +-----+----+----+----+----+----+----+-----+ | | | | | | | | | | `-----> ---> ---> ---> ---> ---> ---> ---> -----. | | | | | | | | | | +-----+----+----+----+----+----+----+-----+ | v CARRY IN A ROL (ROTATE LEFT), THE CARRY MOVES INTO THE LOW ORDER BIT; EACH BIT MOVES LEFT; AND THE HIGH ORDER BIT BECOMES THE NEW CARRY. 0 LSR | +----------+ | | | `---> -> -> ----. | | | +----------+ | ROR | +---------+ v | | CARRY---> -> -> ----. | | | +---------+ | ROR | +---------+ v | | CARRY---> -> -> ----. | | | +---------+ | | v TO DIVIDE A THREE-BYTE NUMBER BY TWO, WE SHIFT THE HIGH-ORDER BYTE WITH LSR; THEN WE USE ROR TO ALLOW THE C FLAG TO "LINK" FROM BYTE TO BYTE. Figure 4.5 Comments on Shift and Rotate ---------------------------- As you might expect of arithmetic instructions, the shift and rotate instructions normally operate in the A register. But there's an extra bonus: these instructions also can operate directly on memory. In other words, the computer can go to any address in memory and shift the bits at that address directly, without loading the data into a register. For this reason, you'll often see the instructions coded with the identity of the A register coded in the address part of the instruction. We would code LSR A so as to distinguish from LSR $1234, where the contents of memory is being shifted. :65: When a rotate or shift is performed directly on a memory location, the Z, N, and C flags are affected according to the contents of memory. Z will be set if the contents of the location ends up as zero; N if the high bit is set; and C performs its standard role of catching the leftover bit. Some programmers wonder about the terms logical and arithmetic, used as part of the definition. The distinction is related to the way that signed numbers are treated. "Logical" means that the sign of a number will be probably lost if the number was intended to be signed. "Arithmetic" means that the sign will probably be preserved. It's purely a terminology question: the bits themselves move exactly as you would expect them to do. Subroutines ----------- We have written programs that are subroutines called by BASIC. We have written subroutine calls to built-in operations such as $FFD2 or $FFE4. Can we also write our own subroutine and arrange to call it? Of course we can. RTS (return from subroutine) does not mean "return to BASIC." It means "return to whoever called this routine." If BASIC called up the machine language routine, RTS takes you back to BASIC. If another machine language program called up the subroutine, RTS will return to the calling point. We wrote a successful subroutine in the last chapter. It's purpose was to accept only numeric keys, echo them to the screen, and convert the ASCII value to binary. Now we'll use this subroutine to build a more powerful program. Here its. Be sure it's entered in your computer. .A 033C JSR $FFE1 .A 033F BEQ $0351 .A 0341 JSR $FFE4 .A 0344 CMP #$30 .A 0346 BCC $033C .A 0348 CMP #$3A .A 034A BCS $033C .A 034C JSR $FFD2 .A 034F AND #$0F .A 0351 RTS :66: The Project ----------- Here is our mission: using the above subroutine, we wish to build a simple addition program. Here's how we want it to work. The user will touch a numeric key, say "3". Immediately, "3+" will appear on the screen. Now the user will touch another key, say "4", and the program will complete the addition so that the screen shows "3+4=7'. We will assume that the total is in the range 0 to 9 so that we don't have to worry about printing a two-digit answer--don't try 5+5 or you'll get a wrong answer. Here we go. We must start our coding at address $0352 so as not to disturb our subroutine. We'll need to give SYS 850 to make this one go. .A 0352 JSR $033C We call our prewritten subroutine, which waits for a numeric key, echoes it to the screen, and converts the value to binary in the A register. Our next action is to print the plus sign. We know how to do this, once we look up the ASCII code for this character. Appendix D tells us that it's $2B, so we'll need to LDA $#2B and JSR $FFD2. But wait a minute! Our binary value is in the A register, and we don't want to lose it. Let's store the value somewhere: .A 0355 STA $03C0 .A 0358 LDA #$2B .A 035A JSR $FFD2 .A 035D JSR $033C We picked $03C0, since nobody seems to be using it, and put the binary number safely away there. Now we print the plus sign, and go back to ask for another digit. When the subroutine returns, it has a new binary value in the A register; the digit has been neatly printed on the screen behind the plus sign. Now we need to print the equal sign. But again, wait! We must put our binary away first. We could place the value into memory--perhaps $03C1 would do--but there's another way. We don't seem to be using X or Y for anything at the moment, so let's slip the value across into one or the other. We have four "transfer" commands that will move information between A and either index register: :67: TAX--Transfer A to X TAY--Transfer A to Y TXA--Transfer X to A TYA--Transfer Y to A Like the load series of commands, these instructions make a copy of the information. Thus, after TAX, whatever information was in A is now also in X. Again like the load commands, the Z and N status flags are affected by the information transferred. It doesn't matter whether we use X or Y: Let's pick X: .A 0360 TAX .A 0361 LDA #$3D .A 0363 JSR $FFD2 We have put our second value into X and printed the equal sign ($3D). Now we can bring the value back and do our addition. The next two instructions can come in any order: .A 0366 TXA .A 0367 CLC .A 0368 ADC $03C0 We have our total in the A register. It's almost ready to print, except for one thing: it's in binary. We want it in ASCII. Assuming the total is in the range 0 to 9, we can convert it directly to a single ASCII digit with an ORA operation. (If it's greater than nine, you're cheating and the answer won't make sense.) .A 036B ORA #$30 .A 036D JSR $FFD2 Are you basically a neat person? Then you'll want to print a RETURN to start a new line: .A 0370 LDA #$0D .A 0372 JSR $FFD2 .A 0375 RTS Check it with a disassembly. If you disassemble starting with the subroutine, you'll need more than one screen full of instructions to see it all. No problem. When the cursor flashes at the bottom of the screen, press the letter D and RETURN and you'll see a continuation of the listing. Back to BASIC. This time we do not give SYS 828--that's the subroutine and we want the main routine, remember? Give the SYS 850 command. Tap a couple of numeric keys that total nine or less. Watch the result appear instantly on the screen. :68: If you like, set up a BASIC loop and call the routine several times. Project for enthusiasts: You couldn't resist, could you? You had to type in two digits that totaled over 9 and got a silly result. OK, your project is to try to expand the above code to allow for two-digit results. It's not that hard, since the highest possible total is 9+9 or 18; so if there are two digits, the first one must be the digit 1. You'll need to compare for the result over binary nine, and then arrange for printing the one and subtracting ten if necessary. Sounds like fun. Things You Have Learned ----------------------- - We may decide to use a number as a signed value; in this case, the high bit of the number will be 0 if the number is positive and 1 if the number is negative. It's up to us. As far as the computer is concerned, it's just bits in either case. - When a number might have a value that won't fit into an eight-bit byte, we may use more than one byte to hold the value. We have already done this to hold addresses in two bytes: there's a high byte to hold the high part of the value and a low byte to hold the low part. - We may add two numbers together using the ADC instruction with the A register; we should always clear the carry flag before starting an addition. The carry flag will take care of multi-byte numbers for us, providing we remember to start the addition at the low end. - We may subtract two numbers using the SBC instruction with the A register; we should always set the carry flag before starting a subtraction. The carry--which is sometimes called an inverted borrow--will take care of multi-byte numbers for us, providing we remember to start the subtraction at the low end. - For unsigned numbers, the carry should end up as it started (clear for addition, set for subtraction); otherwise we have overflow in the result. For signed numbers, the carry doesn't matter; the V flag will be set if we have overflow. - We may multiply a byte by two with the ASL (arithmetic shift left) instruction. If we have a multiple-byte number, we may carry the multiplication through to other bytes by using the ROL (rotate left) instruction, starting at the low byte of the number. - We may divide a byte by two with the LSR (logical shift right) instruction. If we have a multiple-byte number, we may carry the division through to other bytes by using the ROR (rotate right) instruction, starting at the high byte of the number. - The shift and rotate instructions may be used on the contents of the A register or directly on memory. The N and Z flags are affected, and the C flag plays an important role in the shift/rotate action. :69: - If we wish to multiply by a value other than two, we may need to do more work but we can get there. - As we might have expected, we may write subroutines in machine language and then call them from machine language. It's a good way to organize your code. Questions and Projects ---------------------- Write a program to subtract two single-digit numbers, similar to the one in the above exercise. You may continue to use the subroutine from the previous chapter. Write a program to input a single-digit number. If the number is less than five, double it and print the result. If the number is five or over, divide it by two (discarding any remainder) and print the result. Try to produce a neat output. Write a program to input a single-digit number. Print the word ODD or EVEN behind the number, depending on whether it is odd or even. Use the LSR instruction followed by a BCC or BCS test to check for odd or even. If you've been following the logic, you have developed quite a bit of capability in machine language. You can input, you can output, and you can do quite a bit of arithmetic in between. By now, you should have developed skills with the machine language monitor and feel much more comfortable zipping in and out. These skills are not difficult, but they are important to the beginner. Without them, you can never get comfortably into the real meat: how to code machine language itself. :70: :71: Chapter 5 Address Modes This chapter discusses: o Non-addresses: implied, immediate, register o Absolute and zero-page o Indexing o The relative address for branches o Indirect addressing o Indirect, indexed :72: Addressing Modes ---------------- Computer instructions come in two parts: the instruction itself, or op code, and the address, or operand. The term "address" is a little misleading, since sometimes the operand does not refer to any memory address. The term address mode refers to the way in which the instruction obtains information. Depending on how you count them, there are up to 13 address modes used by the 650x microprocessor. They may be summarized as follows: 1. No memory address: implied, accumulator. 2. No address, but a value supplied: immediate. 3. An address designating a single memory location: absolute, zero-page. 4. An indexed address designating a range of 256 locations: absolute,x; absolute,y; zero-page,x; zero-page,y. 5. A location in which the real (two-byte) jump address may be found: indirect. 6. An offset value (e.g., forward 9, back 17) used for branch instructions: relative. 7. Combination of indirect and indexed addresses, useful for reaching data anywhere in memory: indirect, indexed; indexed, indirect. No Address: Implied Mode ------------------------- Instructions such as INX (increment X), BRK (break), and TAY (transfer A to Y) need no address; they make no memory reference and are complete in themselves. Such instructions occupy one byte of memory. We might say that such instructions have "no address." The precise term is "implied address," which seems to say that there is in fact an address, but we do not need to state it. Perhaps the word "implied" is used in this manner: an instruction such as INX implies the use of the address register; and an instruction such as BRK implies the address of the machine language monitor. If so, there's an instruction that still defies this definition: NOP. The Do-Nothing Instruction: NOP -------------------------------- NOP (no operation) is an instruction that does nothing. It affects no data registers or flags. When a NOP instruction is given, nothing happens and the processor continues to the next instruction. It seems inappropriate to me that we say that NOP has an implied address. It doesn't do anything; it doesn't have an address at all. On the other hand, I suppose that logicians might say, "Yes, but it does nothing to the X register." :73: The NOP instruction, whose op code is #EA, is surprisingly useful. It's not simply that if you're a contract programmer getting paid by the byte you might be tempted to put a large number of NOP instructions into your program. NOP can serve two important program testing functions: taking out unwanted instructions, or leaving space for extra instructions. It's not as easy to change a machine language program as it is to change a BASIC program. As you have seen, the instructions are placed in specific locations. If we wish to eliminate an instruction, we must either move all the following instructions down or fill in the space with NOP instructions. If we move the instructions, we may need to correct some of the addresses. Examine the following code: 0350 LDA #$00 0352 STA $1234 0355 ORA $3456 If we decide to eliminate the instruction at 0352 (STA $1234), we must remove all three bytes. So we place code $EA in locations 0352, 0353, and 0354. Suppose we are testing a moderately large program. Most programs will break into distinct "modules," each of which does a specific job. One module might clear a portion of memory to zero, another might do a calculation, and so on. When we are checking out this program, it might be wise to look at each module as it runs. In this case, we might deliberately code a BRK (break) command between each program module. The program will start to run, and then it will break to the machine language monitor. Within the monitor, we can examine memory to ensure that this module has done the job as we planned it. When we are satisfied, we can start the next module using the .G command. In this way, we can have tight testing control over our program. That's all very well, but when we have finished testing our program and are satisfied that it runs correctly, we don't want the BRK instructions there. That's easy to fix. We replace the BRK codes ($00) with NOP's ($EA), and the program will run through to the end. If we are writing a program and suspect that we may need to insert one or two extra instructions within a certain area of the code, we can put a number of NOP instructions there. The space will be available for use when we need it. :74: No Address: Accumulator Mode ----------------------------- We have observed that the shift and rotate instructions, ASL, ROL, LSR, and ROR, allow data manipulation in either the A register or directly in memory. When we want to use the A register, or accumulator, you should note this fact as you code your program. For example, you would write ASL A. Where accumulator mode addressing is used, it has the same characteristics as implied addressing: the whole instruction fits into one byte. Where the shift/rotate instructions refer to a memory location, an address will of course be needed. These address modes will be described later. Other than the shift and rotate instructions, there is one other set of instructions that manipulates memory directly. You may recall INX, INY, DEX, and DEY increment or decrement an index register. INC (increment memory) adds one to any memory location. DEC (decrement memory) subtracts one from any memory location. Both instructions affect the N and Z flags. When an instruction modifies memory, the address mode is neither implied nor accumulator. Memory reference addressing will be discussed later. Not Quite an Address: Immediate Mode ------------------------------------- Coding such as LDA #$34 does not reference a memory address. Instead, it designates a specific value (in this case, $34). An instruction with immediate addressing takes up two bytes: one for the op code and the second for the immediate value. We have used immediate addressing several times. It has a "natural" feel, and it's fast and convenient. There is one potential pitfall: immediate addressing is so easy that it may be abused. Each time you code an immediate address, ask yourself, "Could this value ever change?" By writing a value into a program, rather than a variable, you may be freezing that value forever. An example: a program is written for a VIC-20, which has 22 columns on the screen. At various places in the program, values are compared to 22 (hex 16), and 22 is added or subtracted to various screen addresses. In each case, immediate mode addressing is used to provide the value of 22. Some time later, the programmer decides to convert to the Commodore 64, which has 40 columns on the screen. The programmer must change each immediate mode reference from 22 to 40 (hex 28). :75: If the value 22 had been stored in a memory location so as to be used as a variable, all this recoding would not be needed. The moral is clear: excessive use of immediate mode can call for extra programming work at a later time. There are certain instructions for which immediate addressing is not possible. For example, we can LDA #$00, that is, bring in the actual value zero rather than the contents of an address, but we cannot STA immediate--we must store the information somewhere in memory. A Single Address: Absolute Mode -------------------------------- An instruction might specify any address within memory--from $0000 to $FFFF-- and handle information from that address. Giving the full address is called absolute addressing; if you like, you can deal with information absolutely anywhere in memory. +----------------------------------------------------------------------+ | MEMORY | +----------------------------------------------------------------------+ ^ | Figure 5.1 Absolute Mode Specifies One Address Anywhere Within Memory. We have used absolute addresses several times. When we exchanged the contents of memory locations $0380 and $0381, we named these addresses as we used them. When we stored a value from the keyboard, we named location $03C0. We have also used absolute addresses for program control: subroutines at $FFD2 and $033C were called up simply by giving the address. The JSR (jump subroutine) instruction calls up a subroutine anywhere in memory by using absolute addressing. There is also a JMP (jump) instruction, which can transfer program execution to any location in memory; it's similar to the BASIC GOTO statement. JMP can use absolute addressing--it can go anywhere. There's a limitation to absolute addressing, however. Once you have written the instruction, you can go only to the address stated. You cannot reach a range of locations; only one. :76: One-location addressing can be good for any of several jobs. On the PET/CBM, we might want to switch between text and graphics modes by manipulating address 59468 (hexadecimal E84C). On the VIC-20, we might like to set the volume level of the sound generator by placing a value into location 36878 (hex 900E). On a Commodore 64, the screen's background color can be changed by manipulating address 53281 (hex D021). In each case, it's one specific address that we want; absolute addressing will do the job for us. And we will also use absolute addressing to reference the various RAM locations that we have picked for our own program "variables." Zero-Page Mode -------------- A hexadecimal address such as $0381 is sixteen bits long and takes up two bytes of memory. We call the high byte (in this case, $03), the "memory page" of the address. We might say (but usually don't) that this address is in page 3 at position $81. $00 $FF $100 +-----------+----------------------------------------------------------+ | | MEMORY | +-----------+----------------------------------------------------------+ ^ | Figure 5.2 Zero-Page Mode Specifies A Single Address from $00 to $FF. Addresses such as $004C and $00F7 are in page zero; in fact, page zero consists of all addresses from $0000 to $00FF. Page-zero locations are very popular and quite busy. There's an address mode specifically designed to quickly get to these locations: zero-page addressing. We may think of it as a short address, and omit the first two digits. Instead of coding LDA $0090, we may write LDA $90, and the resulting code will occupy less space and run slightly faster. Zero-page locations are so popular that we'll have a hard time finding spare locations for our own programs. As a result, we tend to conserve zero-page locations on Commodore machines. We'll need the few that are available for a special addressing mode, indirect, indexed, that will be discussed later. There are many locations in zero page that are useful to read. For example, the BASIC system variable ST, which is important in input/output handling, may be examined there (location $96 in PET/CBM, location $90 in VIC-20 and Commodore 64). If you need to know whether the user is holding down a key, there's an address in zero page that will tell you that (location $97 in PET/CBM, $CB in VIC and 64). Zero-page addressing, like absolute addressing, references one location only. It's good for a specific value; but for a range of values we need something more. :77: A Range of 256 Addresses: Absolute, Indexed Mode ------------------------------------------------- Indexing has already been used in Chapter 2. We give an absolute address, and then indicate that the contents of X or Y should be added to this address to give an effective address. +----------------------------------------------------------------------+ | MEMORY | +----------------------------------------------------------------------+ ^ ^ | | +--------' | INDEX BASE VALUE ADDRESS Figure 5.3 Indexing is used only for data handling: it's available for such activities as load and store, but not for branch or jump. Many instructions give you a choice of X or Y as an index register; a few are limited specifically to X or Y. Instructions that compare or store X and Y (CPX, CPY, STX, and STY) do not have absolute, indexed addressing; neither does the BIT instruction. An instruction using absolute, indexed addressing can reach up to 256 locations. Registers X and Y may hold values from 0 to 255, so that the effective address may range from the address given to 255 locations higher. Indexing always increases the address; there is no such thing as a negative index when used with an absolute address. If the address given is above $FF00, a high value in the index may cause the address to "wrap around" and generate an effective address in the region of $0000; otherwise, the effective address is never lower than the instruction address. We've seen the use of indexing. An instruction can reference a certain address, then, as the program loops or as the need for information changes, the same instruction can reference the contents of a different address. The maximum range of 256 locations is an important limitation. :78: The "reach" of absolute, indexed instruction allows it to handle information in buffers (such as the input buffer, keyboard buffer, cassette buffer); tables (such as the active file table); and short messages (such as HELLO or error messages). It's not big enough, however, to reach all parts of screen memory, all parts of a BASIC program, or all of RAM. For that, we'll use indirect, indexed addressing, which will be described later. All of Zero Page: Zero-Page, Indexed ------------------------------------- Zero-page, indexed addressing seems at first glance to be similar to absolute, indexed mode. The address given (this time in zero-page) has the contents of the selected index added to it. But there's a difference: in this case, the effective address can never leave zero page. This mode usually uses the X register; only two instructions, LDX and STX, use the Y register for zero-page, indexed addressing. In either case, the index is added to the zero-page address; if the total goes beyond zero page, the address "wraps around." As an example, if an instruction is coded LDA #$E0,X and the X register contains 50 at the time of execution, the effective address will be $0030. The total ($E0 + $50 or $130) will be trimmed back to zero page. $00 $FF $100 +-----------+----------------------------------------------------------+ | | MEMORY | +-----------+----------------------------------------------------------+ ^ ^ ^ | | | | +-' |-' +---| | BASE ADDRESS Figure 5.4 Thus, any zero page address can be indexed to reach any other place in zero page; the reach of 256 locations represents the whole of zero page. This creates a new possibility: with zero-page, indexed addressing, we can achieve negative indexing. For this address mode only, we can index in a downward direction by using index register values such as $FF for -1, $FE for -2, and so on. On Commodore machines, zero page is fairly well occupied. There is limited opportunity to use zero-page, indexed addressing. :79: Branching: Relative Address Mode --------------------------------- We have written several branch instructions already; the assembler allowed us to enter the actual addresses to which we want to branch. The assembler translates it to a different form--the relative address. ,---------. | ,---. | v | v | +----------------------------------------------------------------------+ | MEMORY | +----------------------------------------------------------------------+ Figure 5.5 Relative address means, "branch forward or backwards a certain number of bytes from this point." The relative address is one byte, making the whole instruction two bytes long. Its value is taken as a signed number. A branch instruction with a relative address of $05 would mean, "if the branch is taken, skip the next 5 bytes." A branch instruction with a relative address of $F7 would mean, "if the branch is taken, back up 9 bytes from where you would otherwise be." As a signed number, $F7 is equal to a value of -9. We can calculate a branch by performing hexadecimal subtraction; the "target" address is subtracted from the PC address. If we have a branch at $0341 that should go to $033C, we would work as follows: $033C (the target) minus $0343 (the location following the branch instruction) would give a result of $F9, or minus 7. This is tedious to do, and often results in mistakes; such mistakes in calculating a branch address are often fatal to the program run. We are much better off using an assembler to work out the arithmetic for us. The longest branches are: $7F, or 127 locations ahead; and $80, or 128 locations back. This poses no difficulties with short programs, such as the ones we are writing here. But in larger programs, the branch may not be able to reach far enough. The usual solution to this is to place a JMP (jump) instruction nearby, which is capable of going anywhere in memory; JMP uses absolute addressing. The appropriate branch instruction will go to the JMP, which in turn will take the program to the desired location. :80: Advocates of programming style make the following argument. All programs should be written into neat small modules. Logic block should be broken into subroutines, and the subroutines into even smaller subroutines; this way, everything is neat and testable. If you should find a branch that won't reach, ask yourself whether it's time to break your program into smaller chunks before the logic gets too messy. By the liberal use of subroutines, you can arrange your code so that all branches are short and easily within reach. If you do break up the program structure, the branches will then always reach. It's up to you to choose your coding style, but you might give the question some thought. An interesting aspect of relative addressing is that code containing branches is easy to relocate. A piece of code containing a branch to six locations ahead will work perfectly if the whole code is moves to a different location. This is not true of jumps and subroutine calls, or any code using absolute addressing--if the location changes, the address must be changed. The ROM Link--Jumps in Indirect Mode ------------------------------------ We have mentioned the JMP instruction that will take the program to any specified address. JMP has another address mode: indirect addressing. Indirect addressing is signaled by the use of parentheses around the address. It works this way. An address is supplied, but it's not the one we will eventually use. We take this address, and at the location it specifies, we'll find the effective address, or indirect address. The indirect address is two bytes long, of course, and is stored in the usual 650x manner of low byte first. An example will help to make things clear. Suppose that at address $033C we have the instruction JMP ($1234). The parentheses tell us that indirect addressing is involved. The machine code is hex 6C 34 12; as always, the address is "turned around." Now suppose that at addresses $1234 and $1235 we have stored values $24 and $68. The jump instruction would behave as follows: it would go to $1234 and $1235, get the contents, and the program would transfer to address $6824. +-------+---+----------------------------------------------------------+ | | | MEMORY | +-------+---+----------------------------------------------------------+ ^ | ^ | | | | `------------' INDIRECT ADDRESS Figure 5.6 :81: The JMP indirect has a somewhat specialized use. Normally, if we want to transfer control to some location, we just JMP there; no need for the indirect step. But there's one quite important case where indirect jumps serve an important function. Within ROM, there are a large amount of permanent instructions that the computer uses to perform tasks. Since it's in ROM, we can never change this code. If the various programs were linked only be means of JMP and JSR statements, they could not be changed, and we would not be able to modify behavior of the machine. Built into the ROM program, there are a series of carefully planned indirect jumps. Instead of the ROM leaping from one instruction directly to another, it jumps indirectly via an address stored in RAM. We can change the contents of RAM; and if we change the address stored in RAM, we can modify the behavior of the system. The best-known indirect address is that associated with the interrupt sequence: it's at $0090 in PET/CBM and $0314 in VIC, 64, and PLUS/4. You might not code many indirect jumps, but you'll be glad that they are there in ROM. Data From Anywhere: Indirect, Indexed -------------------------------------- The problems with indexed addressing have been noted: the reach of only 256 bytes limits the data capability of this method. Indirect addressing seems to offer a total solution. We can write an instruction that points at an indirect address. Since we can change the indirect address at will, or add to or subtract from it, we can cause our instruction to deal with data anywhere in memory. In fact, we get a limitation and a bonus. First, the limitation: for indirect, indexed instructions the indirect address must be in zero-page--two bytes, of course, organized low byte first, as always. Next, the bonus: after the indirect address is obtained, it will be indexed with the Y register to form the final effective address. Let's step our way through the mechanism and see how it works. Suppose I code LDA ($C0),Y with values $11 in address $00C0 and $22 in address $00C1. If the Y register contains a value of 3, the instruction will follow these steps: The address at $00C0-1 is extracted, giving $2211; then the contents of Y are added to give the effective address of $2214. If the contents of Y changed, the effective address would change slightly. If the indirect address at $C0 and $C1 was changed, the effective address would change radically. :82: The combination of indirect and indexing may seem like overkill. If you can designate any location in memory with an indirect address, why bother with indexing? After all, anywhere plus one is still anywhere. Indirect addressing plus indexing proves to be an ideal combination for the manipulation of data. Almost all data breaks up into logical chunks of some sort: records, table entries, screen lines, words, and so on. Here's the technique. We position the indirect address at the start of a given logical data chunk, and use the Y register to scan through the information. When we're ready to move to the next item, we move the indirect address along, and repeat the same scanning of the Y register through the new data. $00 $FF +-------+---+------+---------------------------------------------------+ | | | | MEMORY | +-------+---+------+---------------------------------------------------+ ^ | ^ ^ | | | | | | +--' | `------------' Y INDIRECT ADDRESS Figure 5.7 One may think of it as a fishing analogy: We anchor the boat in a certain spot (fix the indirect address) and then use the fishing line (the Y register) to reach the data we need. When we're ready for the next item, we pull up the anchor and move along to a new place. .--------------DATA IN MEMORY--------------. | | v v +--------------+--------------+--------------+ | NAME, ETC. | NAME, ETC. | NAME, ETC. | +--------------+--------------+--------------+ ^ ^ ^ | | | A B Figure 5.8 We'll be working through an elaborate example that uses indirect, indexed addressing to manipulate the computer screen. First, a brief diversion. :83: A Rarity: Indexed, Indirect ---------------------------- There is another addressing mode that is little used in Commodore computers: indexed, indirect. It uses the X register rather than the Y, and is coded as in the following example: LDA ($C0,X). In this case, indexing takes place first. The contents of X are added to the indirect address (in this case, $C0) to make an effective indirect address. If X were equal to 4 in this example, the effective indirect address would be $00C4, and the contents of $00C4 and $00C5 would be used as the effective address of the data. $00 $FF ++--+--+--+--------+---------------------------------------------------+ ||..|..|..| | MEMORY | ++--+--+--+--------+---------------------------------------------------+ ^ ^| ^ | || | | X |`--------------------' +-----' | INDEXED, INDIRECT ALLOWS ONE OF SEVERAL INDIRECT ADDRESSES TO BE CHOSEN USING THE X INDEX REGISTER Figure 5.9 In certain types of control processing, this is a quite useful address mode. X will contain an even number; since each indirect address is two bytes long, we will need to skip from one to the other, two bytes at a time. Let's take a hypothetical communications system that is connected to four telecommunications lines and see how indexed, indirect addressing might be used. Characters are being received from the four lines almost simultaneously. As each character arrives, it must be put away into a memory buffer belonging to that particular line; in that way, traffic received from the various sources won't get mixed together. Zero-page will contain four indirect addresses, one for each line; each indirect address points at an input area for one line. Suppose a character is received into the A register from one of the lines; the line number (times two) is in the X register. We could then put the character away with the instruction STA ($60,X). Thus, if line zero was involved, its indirect address at address $60/61 would be used; for line 1, the address at $62/63 would be used; and so on. After we had stored the character concerned, we'd need to bump the indirect pointer so that the next character will go into a new position: INC $60,X would do the trick. :84: The above example is a rather specialized use of the indexed, indirect address mode. You may never need to use this mode. Indeed, most programmers lead full, rich lives without ever writing code that uses indexed, indirect addressing. The Great Zero-Page Hunt ------------------------ Indirect, indexed addresses are very important. They are your gateway to reaching any part of memory from a single instruction. But you must have two bytes available in zero-page for each indirect address you want to use. The Commodore ROM system helps itself to liberal amounts of zero-page memory. You don't have much empty space left over. How can you find space for these indirect pointers? First, look for unused locations. There are only a few of them: on the VIC and Commodore 64, you'll find four locations at locations $00FC to $00FF. That's enough for two indirect addresses. If you need more, look through the memory maps for locations designated as "work areas" or "utility pointers." They can usually be put to work for a temporary job. Finally, you can take working parts of zero-page and copy them to some other parts of memory. You can use these locations, carefully putting back the original contents before returning to BASIC. Don't try this with any values that are used by the interrupt routines (involved with the screen, keyboard, or RS-232); the interrupt can and does strike while your machine language program is running. And if the interrupt program changes these zero-page values, your program is going to behave badly. Project: Screen Manipulation ----------------------------- This project is intended to show how indirect, indexed addressing can be used effectively. We'll change something on the screen--enough so that we reach more than 256 addresses. Ordinary indexing, therefore, won't do. :85: We'll select a number of lines on the screen; within each line, we'll change a certain group of characters. In other words, we will write the code so as to manipulate a window on the screen. To do this, we'll need to code two steps: setting up the start of a screen line, and later moving on to the next line when needed. Within each line, we'll work our way through the range of screen columns that we have selected. In fact, it's a big loop (for the lines) containing a small loop (for the columns within that line). We'll use indirect addressing to point to the start of each line, and indexing (the Y register) to select the portion of that line to change. Since there's a variety of Commodore machines, we have some problems to resolve. All Commodore screens are "memory mapped," that is, the information appearing on the screen is copied directly from some part of memory. We may change the screen by changing the appropriate memory. But different machines use different memory addresses; and in VIC and Commodore 64, the screen may be moved around. Another thing to consider is that the length of the line varies between different machines--it might be 22 or 40 or 80 columns. No problem. If you have a 40-column machine, 40 equals $28; code .A 033C LDA #$28 For a 22-column machine, change the above to LDA #$16; and for an 80-column PET, code LDA #$50. Have you coded the correct value? Let's proceed with our next decision. In the PET/CBM, screen memory starts at address $8000; in VIC or Commodore 64, the screen starts at whatever page is designated in address $0288. Let's code as follows: PET/CBM: .A 033E LDX #$80 .A 0340 NOP VIC/Commodore 64: .A 033E LDX $0288 The NOP instruction does nothing, but it makes the coding the same length so that we may continue with address $0341 in either case. The A register tells us our line length, and the X register tells us the page number on which the screen starts. Let's put them away. The line length will be needed for addition later, so we may put it anywhere safe; the screen address will be part of an indirect address, so it must go into zero-page. :86: It's hard to find a zero-page address that may be used in all Commodore machines; we'll choose $00BB and $00BC. $BB contains the low byte of the address, of course. Let's code .A 0341 STA $03A0 .A 0344 STX $BC Note that we are using zero-page addressing mode for the instruction at address $0344. That puts the high byte of the address in place. Now we'll set the low byte to zero: .A 0346 LDA #$00 .A 0348 STA $BB Our indirect address is now pointing at the start of screen memory. Let's discuss in more detail what we want to do with the screen. Specifically, we want to change a number of lines, let's say 14, on the screen. We will step along our indirect address by adding to the indirect address: maybe 22, maybe 40, maybe 80; whatever is in address $03A0. And we won't do the whole line; we'll start in column 5 and go to column 18. Let's count the lines in the X register; we'll start X at zero. .A 034A LDX #$00 Now we're ready to do a screen line. Later, we'll adjust the indirect address and come back here to do another line. We should make a note to ourselves: "Come back to $034C for the next screen line." The indirect address is pointing at the start of the line. We want to start work in column 5. That means that Y should start with an offset of 4 (the start of the line plus 4). Let's do it: .A 034C LDY #$04 We're going to walk Y up, and loop back to this point for the next character on the line. We might note: "Come back to $034E for the next character." We're ready to go. Let's dig out the character that's currently on the screen: .A 034E LDA ($BB),Y This is worth a review. Locations $BB and $BC contain the address of the start of screen memory; on the PET/CBM, for example, this would be $8000. To this, we add the contents of Y (value 4) to create an effective address of $8004; and from location $8004 we get the screen character. We decide that we will leave spaces alone. The space character shows on the screen as a value of decimal 32, hex 20. Let's skip the next operation if it's a space: :87: .A 0350 CMP #$20 .A 0352 BEQ $0356 We have to guess at the address to which we will skip ahead, since we haven't gotten there yet. Make a note: "This address may need correction." .A 0354 EOR #$80 This is where we manipulate the character. The EOR is a "flip-over" command; we're flipping the high bit of the screen value. You may look up screen codes to see what this does, or you may wait and see what happens. At this point, our code from $0352 joins up. As it happens, we were lucky again: the address is exactly right to rejoin at $0356. But if it were not, you know how to fix it, don't you? Exit the assembler, then go back and type over. Now we put the modified character back to the screen: .A 0356 STA ($BB),Y We have done one character. Let's move along the line to the next character, and if we have passed column 18 (Y=17) we should quite and go to the next line. .A 0358 INY .A 0359 CPY #$12 .A 035B BCC $034E Y moves along to the next character position: five, then six the next time around, and so on. So long as Y is less than 18 (hex 12) we'll go back, since BCC means "branch less than." If we get past this point, we have completed the line and must move to the next one. We move to the next line by adding to the indirect address. We must add 22, or 40, or 80; the value is in address $03A0 (you may remember that we stored it with the instruction at $0341). We must remember to clear the carry flag before starting the addition, and to add starting at the low byte of the address (at $BB). .A 035D CLC .A 035E LDA $BB .A 0360 ADC $03A0 .A 0363 STA $BB .A 0365 LDA $BC .A 0367 ADC #$00 .A 0369 STA $BC :88: The last three instructions seem odd. Why would we add zero to the contents of $BC? Surely that changes nothing. The answer is obvious after a little thought: there might be a carry from the previous addition. Now we're ready to count the lines: we had decided to use X as a counter. Let's add one to X, and test to see whether we have done the 14 lines: .A 036B INX .A 036C CPX #$0E .A 036E BNE $034C If we've done the required number of lines, we have nothing more to do other than return to BASIC: .A 0370 RTS Disassemble and check it. Again, you'll find that the code occupies more than one full screen. Return to BASIC. This time, we'll write a small BASIC program to exercise the machine language code. Type NEW to clear out any old BASIC code, and enter 100 FOR J=1 TO 10 110 SYS 828 120 FOR K=1 TO 200 130 NEXT K,J The extra loop is to slow things down. Machine language runs so fast that the effect might not be properly visible if run at full speed. Project for enthusiasts: Can you change the program to do a different set of columns? Could you change it so that it affected only the letter "S" wherever it appeared on the screen? Comment for VIC-20 and Commodore 64 ----------------------------------- This exercise will work as intended. Other types of screen work might call for you to set the color nybble memory values before you can successfully work directly with screen memory. The rules for machine language are no different from those for BASIC: if you wish to POKE to the screen, you may need to take the color nybble area into account. Things You Have Learned ----------------------- - Three address modes are not addresses at all. Implied addressing means no address at all; accumulator addressing uses the A register and means the same thing; and immediate addressing uses a value, not an address. :89: - Absolute addresses reference one location only, somewhere in memory. Zero- page addresses reference a single address in the range $0000 to $00FF--the high byte of the address (00) is the memory page. These address modes are used for fixed locations containing work values or system interfaces. - Absolute, indexed and zero-page, indexed allows the named address to be adjusted by the contents of an index register--X or Y. These instructions can reach a range of up to 256 addresses. They are commonly used for tables of data or temporary storage areas. - Relative addresses are used exclusively with branch instructions. They have a limited "reach" of about 127 locations forward or backward. It takes a little arithmetic to calculate the proper values, but the computer usually works this out for us. - Indirect addressing is used only for jumps, most often to allow a fixed ROM program to take a variable jump. The average machine language programmer will seldom need these, but the principle of indirect addressing is worth learning. - Indirect, indexed addressing is the most important way to deal with data anywhere in memory. We may reach anywhere by setting the indirect address, then we may "fine adjust" that address by indexing it with the contents of Y. - Indirect, indexed addressing requires the indirect address to be in zero- page. We need to conserve zero-page locations for this use. - An addressing mode called indexed, indirect is rarely used when programming Commodore computers, but it's there if you want it. Questions and Projects ---------------------- Write a program to clear the screen of your computer--check Appendix C for the location of screen memory if you've forgotten. Don't just print the clear screen character ($93); do it another way. Can you write the entire program without using indirect, indexed addressing? Write the program again using indirect, indexed addressing. The program may be a little shorter. Can you think of any other advantages of writing this way? A user wishes to type in a line of text on the keyboard, ending with a RETURN. He then wants to have the program repeat the line ten times on the screen. What addressing mode or modes would you use to handle the user's text? Why? You may try your hand at writing the program if you wish. Take one of the previous exercises and try to write it again without using immediate addressing. Is it hard to do? Can you see any reason to want to code without using immediate addressing at all? :90: :91: Chapter 6 Linking BASIC and Machine Language This chapter discusses: o Where to put a machine language program o BASIC memory layout o Loading and the SOV pointer o BASIC variables: fixed, floating, string o Exchanging data with BASIC :92: Siting the Program ------------------ Up to this point, we have been placing all programs in the cassette buffer. This is a good place for short test programs, but we need to examine alternatives that are often more attractive. BASIC Memory Layout ------------------- BASIC RAM is organized according to the diagram below. The following locations are of particular interest: 1. Below the BASIC area, we have the cassette buffer area. This is available to us, providing we are not engaged in input/output activity. 2. Start-of-BASIC (SOB) is usually a fixed address within the machine. In PET/CBM, it's at $0401 (decimal 1025). In Commodore 64, it's at $0801 (decimal 2049). In the PLUS/4 series, it's at $1001 (decimal 4097). In the VIC-20, it may be at one of several places: $0401, $1001, or $1201. A pointer marks this location. The pointer is located at $28/$29 (decimal 40 and 41) in PET/CBM, and at $2B/$2C (decimal 43 and 44), in VIC-20, Commodore 64, and PLUS/4. 3. End-of-BASIC is always signaled by three zero bytes somewhere after the SOB. If you command NEW in BASIC, you'll find the three bytes right at the start of BASIC; there is no program, so start and end are there. There is no pointer that indicates end-of-BASIC, just the three zeros; but the next location (SOV) will often be directly behind the end-of- BASIC. The BASIC program that you type in will occupy memory space from start- of-BASIC to end-of-BASIC. If you add lines to a program, end-of-BASIC will move up as extra memory is taken up by your programs. If you delete lines, end-of-BASIC will move down. __________________BASIC RAM_______________________ / \ -+--------+-------+-+---------+---+-----------+-------+------+---------+-- |Cassette| | | BASIC |0 | BASIC | BASIC | | DYNAMIC | | | |0| | 0 | | | FREE | | | Buffer | | | PROGRAM | 0| VARIABLES | ARRAYS| | STRINGS | -+--------+-------+-+---------+---+-----------+-------+------+---------+-- ^ ^ ^ ^ ^ ^ | | | | | | SOB SOV SOA EOA BOS TOM Figure 6.1 :93: 4. Start-of-variables (SOV) is often positioned directly behind the end- of-BASIC. When the BASIC program runs, the variables will be written into memory starting at this point; each variable is exactly seven bytes long. A pointer marks this location. The pointer is located at $2A/$2B (decimal 42 and 43) in PET/CBM, and at $2D/$2E (decimal 45 and 46) in VIC-20, Commodore 64, and PLUS/4. The SOV pointer is extremely important during BASIC load and save activities. If we give the BASIC command SAVE in direct mode, the computer will automatically save all memory from SOB to just before the SOV. Thus, it saves the whole BASIC program, including the end-of- BASIC marker of three zero bytes, but does not save any variables. If we give the BASIC command LOAD in direct mode, the computer will automatically load the program, and then place the SOV pointer to just behind the last byte loaded. In this way, variables will never be stored over the BASIC program; they will be written above the end-of- BASIC. More on this later. If the BASIC program is changed, the SOV may move up or down as needed. 5. Start-of-arrays (SOA) also represents one location beyond the end-of- BASIC variables, and thus could be named end-of-variables. Arrays created by the BASIC program, either by use of a DIM statement or by default dimensioning, will occupy memory starting at this point. A pointer marks this location. The pointer is located at $2C/$2D (decimal 44 and 45) in PET/CBM, and at $2F/$30 (decimal 47 and 48) in VIC-20, Commodore 64, and PLUS/4. If the BASIC program is changed, the SOA pointer is set to match the SOV. Thus, all BASIC variables are wiped out the moment a change is made to the program. 6. End-of-arrays (EOA) is set one location beyond the last array location in BASIC. Above this point is seemingly "free" memory--but it's not really free, as we'll see soon. A pointer marks this location. The pointer is located at $2E/$2F (decimal 46 and 47) in PET/CBM, and at $31/$32 (decimal 49 and 50) in VIC-20, Commodore 64, and PLUS/4. If the BASIC program is changed, the EOA pointer is set to match the SOA and SOV. Thus, all BASIC arrays are wiped out the moment a change is made to the BASIC program. Let's change direction and start to work our way down from the top of BASIC memory. 7. Top-of-memory (TOM) is set one location beyond the last byte available to BASIC. On the PET/CBM and VIC-20, its location depends on the amount of memory fitted; a 32K PET would locate TOM at $8000. On the Commodore 64, the TOM will normally be located at $A000. A pointer marks this location. The pointer is located at $34/$35 (decimal 52 and 53) in PET/CBM, and at $37/$38 (decimal 55 and 56) in VIC-20, Commodore 64, and PLUS/4. :94: If you examine the TOM pointer, you may find that it does not point at the expected position. That may be because of the machine language monitor, which has taken up residence at the top of memory and stolen away some memory. 8. Bottom-of-strings, (BOS) is set to the last "dynamic" string that has been created. If there are no BASIC strings, the BOM will be set to the same address as TOM. As new dynamic strings are created, this pointer moves down from the top-of-memory towards the EOA address. A pointer marks this location. The pointer is located at $30/$31 (decimal 48 and 49) in PET/CBM, and at $33/$34 (decimal 51 and 52) in VIC-20, Commodore 64, and PLUS/4. A dynamic string is one that cannot be used directly from the program where it is defined; you might like to think of it as a manufactured string. If, within a BASIC program, I type: 100 X$="HAPPY NEW YEAR", the BASIC interpreter will not need to store the string in upper memory; it will use the string directly from where it lies within the program. On the other hand, if I define strings with commands such as R$=R$+"*" or INPUT N$, the strings must be built into some spare part of memory. That's where the BOS pointer comes in: the computed string is placed high in memory, and the BOS moved down to mark the next free place. If the BASIC program is changed, the BOS pointer is set to match the TOM. Thus, all strings are wiped out the moment a change is made to the BASIC program. Free Memory: The Dangerous Place --------------------------------- It seems to beginners that there is a great deal of free memory available above the end-of-arrays and below the bottom-of-strings, and that this would be an ideal place to put a machine language program. This is a pitfall: it usually won't work. Here's the danger. As more and more dynamic strings are created, the bottom- of-strings location keeps moving down. Even when strings are no longer needed, they are abandoned and left dead in memory, taking up space. The BOS keeps moving down. Only when it touches the EOA will the dead strings be cleaned up and the good ones repacked, an action called garbage collection. It's important for BASIC programmers to know about garbage collection: except on BASIC 4.0 and Commodore PLUS/4 systems, it can be a cause of serious program slowdown. :95: It's evident that the space between EOA and BOS is not safe. If you put a program there, the strings will eventually destroy it. We must look elsewhere. Where to Put Your ML Program ---------------------------- First, you may put your program in the cassette buffer. Providing you are not performing input/output activity, your program will be safe. Your space here is limited to 190 characters or so. SOB SOV SOA EOA BOS TOM | | | | | | -+========+----+----------+-----+-----+-- --+-----+-- |C.B.====| | BASIC... | VAR | ARR | ... | STR | -+========+----+----------+-----+-----+-- --+-----+-- Figure 6.2 Second, move down the top-of-memory pointer and place the program in the space that has been freed. Your space here is unlimited. Programs placed here will take up permanent residence until the power is turned off. Many monitors, such as Supermon, live here. NEW OLD SOB SOV SOA EOA BOS TOM TOM | | | | | | | -+--------+----+---------+----+----+-- --+----+====+-- |C.B. | | | | | ... | |====| -+--------+----+---------+----+----+-- --+----+====+-- Figure 6.3 OLD NEW SOB SOV SOV SOA EOA BOS TOM | | | | | | | -+--------+----+---------+====+----+----+-- --+----+-- |C.B. | | 000|====| | | ... | | -+--------+----+---------+====+----+----+-- --+----+-- Figure 6.3 Third, move up the start-of-variables pointer, and place the program after the end of BASIC and before the new start-of-variables. Your space here is unlimited Programs placed here will tend to "join company" with the BASIC program; the two will save and load together. :96: After moving a pointer--as was done in the last two methods--it's a good idea to return to BASIC and command CLR, so that all other variable pointers will align correctly with the ones that have moved. These three areas will be discussed more in a few moments. First there are one or two extra locations available to VIC-20 and Commodore 64. Extras for VIC and Commodore 64 ------------------------------- The Commodore 64 has a free block of RAM at locations $C000 to $CFFF (decimal 49152 to 53247). That's 4k of RAM not being used; you may write your programs there. Before you do so, check to make sure that the memory is not being used by any other programs. It's a popular place in the Commodore 64, and many utilities and commercial programs zero in on this available memory. If you intend to write programs entirely in machine language, with no BASIC content at all, you may completely remove BASIC from the Commodore 64 system and claim the space as available RAM. This gives you the whole block from $0801 up to $CFFF for programs and data--a whopping 50K--and even more could be liberated if necessary. BASIC may be made to disappear from the Commodore 64 with the equivalent of POKE 1,54 (LDA #$36, STA $01). It may be reinstated with the equivalent of POKE 1,55 (LDA #$37, STA $01). Be very careful. With BASIC gone, the computer doesn't even know how to say READY. On all Commodore machines it's possible to move up the start-of-BASIC pointer and use the space freed below. To do so, it's essential to store a value of zero in the location immediately before the new start-of-BASIC, and to align all other pointers, usually by going to BASIC and commanding NEW. This works, and will make as much space available as is needed. BASIC programs will relocate as they load. But since the computer needs to be reconfigured before the main program is loaded, and often needs to be restored to its original configuration after the program is run, the method is not popular in most Commodore machines. It's used fairly often in the VIC-20, however. The video chip in the VIC-20 can "see" RAM memory only in the memory space $0000 to $1FFF (decimal 0 to 8191). Whatever variable information appears on the screen must be taken from this memory area. The VIC-20 can also get information from $8000 to $9FFF, but there's no RAM there; we can't manipulate this memory area. :97: If we want to perform special visual effects on the VIC-20, we must manipulate data in the area $0000 to $1FFF. Let's look at what's available. $0000 to $03FF is used by the "system;" other than the cassette buffer, we must leave it alone. $0400 to $0FFF contains no memory unless a 3K RAM expansion is added. $1000 to $1DFF contains the BASIC program, and $1E00 to $1FFF is screen memory. Details may vary, but the answer always comes out the same: there's no space to do our video effects. A popular VIC-20 solution, especially where 8K or more of RAM expansion has been added, is to increase the start-of-BASIC pointer, thus liberating space in low memory. This may now be used for visual effects and for machine language programming, too, if any space is left over. In the VIC-20, this approach is necessary, but it's still a bit clumsy. The Wicked SOV -------------- The start-of-variables pointer can be the cause of many troubles, if it's not understood. The rules covering it are as follows: 1. Variables are written starting at the SOV. 2. BASIC SAVEs will save from memory beginning at start-of-BASIC and stopping at SOV. 3. Direct command BASIC LOADs will bring a program into memory, relocating if appropriate, and then set the SOV pointer to the location following the last byte loaded. 4. Changes to BASIC programs cause memory to be moved--up or down-- starting from the point where the change is made and stopping at the SOV. The SOV will then be moved the appropriate distance up or down. These seem to be innocent rules. Rule 1 defines the purpose of the SOV. Rule 2 shows how the SOV controls the SAVE command so that the entire BASIC program is saved, but not the variables. Rule 3 arranges that short programs will have a large amount of variable space available; long ones will have less. Rule 4 ensures that a BASIC change makes extra room in memory or reclaims memory space. But if the SOV gets the wrong address, we're in trouble. The rules work against us. Variables may be written into disastrous places. SAVEs will cause too much or too little to be saved. LOADs may fix things, since SOV will be changed by the load action. An attempt to change a program with a bad SOV may cause too little or far too much memory to be moved around. We must get the SOV right. :98: How can the SOV go bad on us? Let's take three examples, corresponding to the three major places that we might put machine language programs: We have a program in the cassette buffer, and a BASIC program that goes with it. We enter or load the BASIC program (the SOV is all right so far), and then we LOAD the machine language program; the SOV ends up disastrously somewhere in the cassette buffer area. We're in trouble. The program seems to list correctly, but it's sick. If we RUN, variables will start to be placed in the cassette buffer area; as more variables area created, they are placed in progressively higher memory locations. Eventually, the variables start to write over the BASIC program. Everything stops. The poor programmer says LIST to see what's happening; his BASIC program is gone, and all that's left is gibberish. We're in more trouble. Alternatively, the programmer decides to save his BASIC program and commands SAVE. BASIC starts to save memory beginning at start-of-BASIC...and keeps saving, and saving, and saving. It won't stop until it reaches the SOV, but that's below where we started. We won't get there until the address "wraps around" and comes back up through zero. The poor programmer--if he or she waits long enough--discovers that the tiny five-line BASIC program has been saved as over 250 blocks on disk, or fifteen minutes worth of tape. And the saved program is useless. We're in still more trouble. Alternatively, the programmer lists the program, and decides to delete one character from a line of BASIC. BASIC immediately starts to move memory, starting at the change point. It won't stop moving memory until it reaches SOV, but that, again, is below where we started. It will move everything that can be moved. RAM will be moved along, which may not hurt anything; then the IA chips will be moved, which may scramble colors or make the display go crazy; then it will try to move ROM, which won't work because ROM can't be changed; then it will wrap around to zero-page and move everything there, which is fatal to the system. Eventually, it will collapse before reaching SOV since it destroys its own working pointers. Quiet Interlude --------------- It's easy to see how the problem occurs, once you understand about the SOV and its role. But if you don't understand the SOV, the results can shake your self-confidence. Many programmers have given up on machine language because of a bad experience with SOV. :99: It works this way. The student writes a perfect program into the cassette buffer and saves it using the machine language monitor. Later, with a BASIC program in place, the student recalls the program and inadvertently moves SOV to an impossible location. When BASIC runs, the variables will start to be written behind the machine language program, ahead of the BASIC program. As more and more variables come into play, they creep relentlessly toward the BASIC coding. Our eager student--with a perfect machine language program and a perfect BASIC program--now decides to say RUN. The BASIC program runs for a while, and then grinds to a halt, usually with a crazy screen or reporting an error in a nonexistent line. We know what's happened, of course: the variables have started to write over the BASIC program. But our unfortunate student doesn't know that. The command LIST is entered, and out comes nonsense. What goes through the programmer's mind at this time? "I was so sure that the program is correct [in fact, it is]; but it's so bad that it's destroyed memory! I suppose that machine language is much more difficult than I thought." And the student loses hope and gives up, not knowing that there's only one small piece of information needed to fix everything up. This is only one of the things that might go wrong when the SOV pointer is improperly placed; even an attempt to change or save a BASIC program can cause system failure. Such experiences destroy confidence. They are responsible for the myth that machine language is hard and only super-clever programmers can cope with it. The Machine Language Monitor SAVE --------------------------------- Now that we're becoming aware of the SOV pitfall, we're ready to discuss how to save a program in machine language. You probably understand why I've been delaying this command until this time. The MLM save command typically goes .S"PROGRAM",01,033C,0361 This would be the tape format. The command is .S and is followed by the program name. The device is tape, so we type 01--be sure to give two digits. Next comes the beginning address (in the example $033C) followed by the end address plus one. In the last example, the last location saved will be $0360. For disk saves, we might want to add the drive number: :100: .S"0:PROGRAM",08,033C,0361 These programs, once saved, may be loaded directly from BASIC, but watch the SOV carefully. VIC-20 and Commodore 64 BASIC LOAD commands should contain the extra field to defeat relocation: LOAD"PROGRAM",8,1 will insist that the program load back into the same memory locations from which it was saved. More on LOAD ------------ There is a machine language .S command to do a program load without changing any pointer (especially SOV). There are a number of different machine language monitors around, and the .L command does not work the same way on all of them. You might check out the one you are using: ideally, the .L command (format: .L"PROGRAM",01) should bring back the program without relocation. The .L command is of limited value. A program user often cannot be expected to load up a machine language monitor and use it to go through a .L sequence. The program should take care of things for the user. We have been careful to say that the BASIC LOAD command changes the SOV when given as a direct command. If a LOAD command is given from within a program, SOV is not changed; but there's a new item to be taken care of. Programmed LOAD has been carefully designed to perform a function called "chaining." That's a BASIC technique, and not within the scope of this book. Chaining, however, has two important characteristics: 1. No pointers are affected. The program will not lose any variables when it performs a LOAD. That's good: we will not lose any of our computations. 2. Once a LOAD is complete, the BASIC program will resume execution at the first statement. It will not continue from where it left off; it will go back to the beginning. For our application, that's bad; we seem to have lost our place in BASIC. If we understand the problem that item 2 creates, we can easily fix it by using item 1. Here's an example to illustrate the problem: we have a program on disk written for the cassette buffer called "ML", and we want to have a BASIC program bring it in. We could code as a first line: 100 LOAD "ML",8--but we'd have a problem. First, the program would load ML. Then it would go back to the beginning and load ML. Then it would go back to the beginning...and so on. This is not satisfactory. Let's use rule 1 to fix everything: :101: 100 IF A=1 GOTO 130 110 A=1 120 LOAD"ML",8,1 130 ... continues When we say RUN, the first line is executed. A is not equal to one, so we continue on line 110. A is set to one, and line 120 causes a load of the desired program. BASIC goes back to the beginning, but all variables are preserved, so A is still equal to 1. Line 100 tests A and goes to line 130, the next statement beyond the load. Everything works as required. If there are multiple LOADs, line 100 might be changed to 100 ON A GOTO 130,150,170... as necessary. Caution: we are discussing the programmed LOAD command only in the context of loading machine language modules. If you want to have a program load in another BASIC program (chaining or loading) the above rules still apply but may need to be used differently. Other SOV Blunders ------------------ We have discussed the horrible results of loading a machine language program into the cassette buffer (using a direct command) after BASIC has been loaded. By now, we should have learned to avoid making this mistake. What about programs stored in other areas, such as high memory or after BASIC? Suppose we want to place a program into high memory, either by moving the top-of-memory pointer down to make room, or by using the spare RAM at $C000 to $CFFF of the Commodore 64. We also have a BASIC program to load. Will loading in the wrong order harm SOV? The answer is yes, although the problem is not so severe. You can see that after loading a program to high memory using a direct command, SOV will be positioned immediately above it. But that's too high--there's no room for variables and we'll get an OUT OF MEMORY error for almost anything we do. Obviously, we can't leave SOV in the upper stratosphere. We must load the high memory first, and then the BASIC program. The second load will straighten out the SOV pointer. If you try this, you'll find that it is necessary to fix up the top-of-memory pointer and command NEW between the two loads; you cannot even give the next LOAD command if you're apparently out of memory. :102: Review: Fixing Pointers ------------------------ If in doubt, examine the pointers by displaying them with a .M command. For VIC/64/PLUS/4, the command would be .M 002B 003A; with PET/CBM, use .M 0028 0037; in either case, be sure that the start-of-variables pointer is set to a "sound" value. As always, you can change an incorrect memory value--in this case, an incorrect vector--by moving the cursor back, typing over the values to be changed, and pressing RETURN. After End-of-BASIC--Harmony --------------------------- Suppose we place the machine language program behind the end-of-BASIC--that's the three zeros in memory--and move up the SOV so that variables don't disturb this program. How will everything work now? Things will work very well indeed. This time, we need to load our BASIC program first; the SOV will go immediately behind BASIC. Then we may load our machine language program, and the SOV moves right behind it. The SOV is in exactly the right place, assuming we load in the right order. (If we don't, the variables will destroy our machine language program.) Once our two programs are together, and we say SAVE, the combination program--BASIC and machine language together--will be saved. A little thought will reveal that memory from start-of-BASIC to just before start-of- variables contains everything we need. A subsequent load will bring everything back in, and position SOV to exactly the right place. We now have a "unit" program--BASIC and machine language working together, loading and saving as one program. There's one small problem in this arrangement. once we have married the BASIC and machine language programs, we must not change the BASIC program. If we added to or subtracted from this program, the machine language program would move up or down--the relocation of memory goes right up to SOV. The program might not be able to work in the new place, and, of course, our SYS commands would be wrong. :103: BASIC Variables --------------- There are four types of entry in the BASIC variable table. All variables, regardless of type, occupy seven bytes; the first two bytes are the name, and the remaining five bytes (not always fully used) contain the value or definition. The variable type is signaled as part of the name: high bits are set over one or both letters of the name to signal a specific type. SOV SOA | | v v --+----------+----------+----------+----------+----------+----------+---- | | | | | | | --+----------+----------+----------+----------+----------+----------+---- EACH VARIABLE IS EXACTLY 7 BYTES LONG. VARIABLES APPEAR IN THE ORDER IN WHICH THEY ARE USED. Figure 6.5 For example, if a floating point variable had a name AB, the name would be stored in the two bytes as $41, $42--the ASCII codes for A and B. The same would be true if the variable were named ABACUS, since only the first two letters of the name are kept. In contrast, if the variable were named AB%, meaning that it was an integer variable, the name would be stored as $C1, $C2. The ASCII codes are the same, but the high bit has been set over them. To complete the picture, a string variable named AB$ would be coded with the name $41, $C2--the high bit is set over the second character only. ,-----------> HIGH BIT SET FOR INTEGER VARIABLES AND FUNCTIONS | | ,-------> HIGH BIT SET FOR INTEGER AND STRING VARIABLES | | +-------+-------------------+ | NAME | VALUE | | | | |2 BYTES| 5 BYTES | | | | | | | | | | | | +---+---+---+---+---+---+---+ Figure 6.6 There's a fourth type of entry that can go into the variable table, but it's not a variable: it's a function definition. If we give the variable command DEF FNA(... an entry will be made in this table. It will be distinguished by the high bit being set over the first character only. String variables use only three of the five bytes provided; the first byte signals the length of the string, and the next two bytes give the string's address. This group of three bytes is called a descriptor. :104: There are two types of numeric variables: floating point and integer. Floating point variables use all five bytes; integer variables use the first two bytes only. It's possible to extract the value from a floating point variable and put it to work, but it's not a simple procedure. A description of how to do this is given in Appendix F. In contrast, it's quite easy to take the value from an integer variable and use it. Let's try an example. Type NEW, followed by A=5:B%=5. This creates two different variables: A and B%. Now go to the machine language monitor. The variables should be near the start-of-BASIC, but if you wish you can find their exact address by examining the SOV pointer ($2A/$2B on PET/CBM, or $2D/ $2E on VIC, Commodore 64 or PLUS/4). On the Commodore 64, we might find that the variables start at $0803; to display both of them, we type .M 0803 0810. We see the floating point variable, A: 41 00 83 20 00 00 00 The first two bytes are the name--41 is ASCII for A, and the zero signifies no second letter--but where's the 5? Embedded within the 83 20 00 00 00, that's where; and it's a good deal of work to extract the 5 for further processing. Behind this variable, we can see the integer variable, B%: C2 80 00 05 00 00 00 Hex C2 is the ASCII for the letter B (42) with the high bit set. 80 is zero with the high bit set--again, there's no second letter. The value is in the next two bytes, and it's easy to read. The last three bytes are not used. Which is easier for machine language to interface with? Obviously the integer variable. It's often quite suitable for the program work at hand: counting characters, setting pointers, and similar tasks. Exchanging Data: BASIC and Machine Language -------------------------------------------- If BASIC and machine language wish to pass data back and forth, there are several approaches. Perhaps the simplest is to have BASIC POKE the values into a given location, and machine language load them as needed; in the opposite direction, machine language will store the values and BASIC will PEEK them. :105: Another method is more sophisticated. BASIC variables are stored in memory; why can't a machine language program go after the variables exactly where they lie and extract their value or change them? It sounds like a good idea. By now, we know how to ask machine language to search for a specific BASIC variable. Given the name, we can get the address of the first variable from the SOV pointer and store it as an indirect address. Using indirect, indexed addressing and stepping the Y register from 0 to 1 we can see if the name matches. If not, we add seven to the indirect address to take us to the next variable. If it does match, our indirect address is set up at the start of the variable; we can set Y to 2, 3, 4, 5, and 6 and extract the whole value. If the variable type is integer, we need only extract the first two bytes (Y=2 and 3). If the variable is not in the variable table, we'll step our indirect address until it matches the start-of-arrays; at that point, we know that we have missed the variable. For a small number of variables, there's a short cut. Variables are placed into the variable table in the order in which they are defined: whichever variable is defined first in the BASIC program will be first in the variable table. So if we arrange for our variables to be defined in a certain order, we can streamline our machine language to "first variable," "second variable," and so on, with no need to examine the names. Let's take this one step further. If we want to use the first variable, all we need to have is the address of the first variable somewhere in zero-page so that we may use it as an indirect address. We already have that address-- it's the SOV, the start-of-variables, and it's there pointing helpfully at the first variable for us. By increasing the value of Y appropriately, we can reach beyond the first variable and into the second, or for that matter, the third or the thirty-sixth. Project: We plan to place the machine language program behind the end-of- BASIC. This will vary, depending on the machine being used. The following code shows the correct addresses for the Commodore 64. Refer to Appendix E for other machines. First, let's do our BASIC coding to estimate its size. We need to guess at the location of the end-of-BASIC so as to place our machine language program. This program will ask machine language to take a value, V%, and multiply it by ten. Remember to say NEW. We write the BASIC program as follows: 100 V%=0 110 FOR J=1 TO 5 120 INPUT "VALUE";V% 130 SYS ++++ 140 PRINT "TIMES TEN =";V% 150 NEXT J :106: It seems likely that our BASIC program will occupy less than 127 bytes. We may check this later, but it seems safe to plan to start our machine language program around 2049+127, or 2176 (hexadecimal 880). On that basis, we may change line 130 to SYS 2176. Do not try to run the program yet. At this point, we could save the BASIC program to tape or disk and develop the machine language program. This would allow us to refine each of the two parts independently. For the sake of brevity--and because our example is an easy one and won't need touching up--we'll write the machine code directly into memory. Switch into the machine language monitor. Assemble the following code: .A 0880 LDY #$02 .A 0882 LDA ($2D),Y .A 0884 STA $033C .A 0887 STA $033E .A 088A LDY #$03 .A 088C LDA ($2D),Y .A 088E STA $033D .A 0891 STA $033F We have now extracted two bytes from the first variable, V%. The high byte has been stored at both $033C and $033E; we'll see why in a moment. The low byte of the value has gone to $033D and $033F. Project for enthusiasts: You might be able to code the above more compactly by more effective use of indexing. .A 0894 ASL $033D .A 0897 ROL $033C .A 089A ASL $033D .A 089D ROL $033C We have multiplied the contents of $033D/$033C by two, and then we have multiplied it by two again. These locations now contain the original value times four. Note that we ASL the low byte and then ROL the high byte. Perhaps we should be checking for overflow; but let's trust the number to be in range for now. :107: Since we have the original number times four in $033D/$033C, we can add it to the original number in $033F/$033E to get the original number times five: .A 08A0 CLC .A 08A1 LDA $033D .A 08A4 ADC $033F .A 08A7 STA $033D .A 08AA LDA $033C .A 08AD ADC $033E .A 08B0 STA $033C Now locations $033C/$033D contain the original number times five. If we double the number one last time, we'll have the value times ten. .A 08B3 ASL $033D .A 08B6 ROL $033C We have multiplied the number by ten. Now let's put it back into the variable .A 08B9 LDY #$02 .A 08BB LDA $033C .A 08BE STA ($2D),Y .A 08C0 LDY #$03 .A 08C2 LDA $033D .A 08C5 STA ($2D),Y .A 08C7 RTS The numbers go back exactly the same way we drew them out. We must be careful to keep the high and low bytes correct. Integer variables have the high-order byte first, followed by the low-order byte; this is exactly the reverse way of the way we use 650x addresses. We must perform one more task before wrapping up the program. We must change the start-of-variables pointer to a location above the machine language program. That would be $08C8, and so we display the SOV pointer with .M 002D 002E and change the pointer to .:002D C8 08 .. .. .. .. .. .. Check...disassemble...and then back to BASIC. LIST, and you'll see your BASIC program again. There's no sign of the machine language program, of course, but SAVE will now save everything together. RUN the BASIC program. Enter the numbers as requested. Confirm that they are multiplied by ten. :108: You may recall that our machine language program does not check for overflow. RUN the program again, and see if you can find the highest number that can be multiplied by ten without error. What happens at time of overflow? Is that what you expected? Project for enthusiasts: Can you add checks for overflow to the above program? You must decide what to do if overflow occurs: print a message; set the value to zero; or whatever you decide. But you shouldn't stop the program or break to the monitor. Such a thing would upset the program user. Your program will be longer. Don't forget, therefore, to change the SOV pointer at $2D/$2E so that your program is safe from variables. Things You Have Learned ----------------------- - Small machine language programs can be conveniently written and checked out in the cassette buffer. We have been doing this during the exercises. This area is not satisfactory for large programs, or programs we want to save on tape. - Programs can take up semi-permanent residence near the top-of-BASIC memory; the top-of-memory pointer needs to be moved down to protect it. These programs often need a separate "setup" to place them. - Programs can be placed behind the end-of-BASIC, which is marked by three consecutive zero bytes in memory. The start-of-variables pointer must be increased so that variables don't write over the program. Care must be taken not to change the BASIC program after this is done. - The VIC-20 frequently has the start-of-BASIC pointer moved up to make room for video information in lower memory. As long as we're moving this pointer, we might move it a little further and make room for some machine code. - The Commodore 64 has an unused block of RAM at addresses $C000 to $CFFF; check to see that no other programs are using this area. - The start-of-variables pointer is intimately tied in with BASIC's SAVE and LOAD commands. It is extremely important to ensure that any LOAD sequence leaves this pointer in a safe place, so that variables cannot write over program code and thus cause program destruction. - Machine language monitor .S (save) and .L (load) commands can be used for staging programs in various parts of memory. Again, great care should be taken to ensure that the pointers are sound after the use of such instructions. - A BASIC program may contain LOAD commands that will bring in any of the following: a different BASIC program, a machine language program, or data. Again, careful handling is needed. - BASIC variables are of three major types: integer, real (floating point), and string. Machine language programs are capable of reading and using any of them; in particular, integer variables are quite straightforward. :109: - If we want, we can simplify the task of searching for BASIC variables by deliberately creating them in a certain sequence. Questions and Projects ---------------------- Write a simple BASIC and machine language program set that allows BASIC to input a number less than 256; POKE it somewhere in memory; call machine language that will divide the number by two; PEEK it back and print it. A program that brings in other programs is called a "boot," or, more accurately, a bootstrap program. Write a simple BASIC boot program to bring in a previous program exercise that was located in a cassette buffer (say, the program from Chapter 2 that printed HELLO), and then call it with a SYS. Bootstrap programs are especially popular with VIC, Commodore 64, and PLUS/4 for bringing in chunks of data such as sprites, new character sets, or whole display screens of information. You might like to try your hand at setting up such a system. Try your hand at this. I have a BASIC program that reads 100 X=5 110 SYS ... 120 PRINT A Write the machine language to be called by the SYS so that it changes the name of the variable X to A. Caution: this may be fun, but it's dangerous in real programs since you may end up with two variables that have the same name. :110: :111: Chapter 7 Stack, USR Interrupt, and Wedge This chapter discusses: o The stack for temporary storage o USR: an alternative to SYS o Interrupts: IRQ, NMI, and BRK o The IA chips: PIA and VIA o Infiltrating BASIC: the wedge :112: A Brief Intermission -------------------- If you have been following along and performing the various projects, you should know a great deal about the principles of machine language. You should be capable of trying your hand at a number of small projects, and investigating areas that may be of special interest. This is a good time to stop and take stock. The remaining chapters are "icing on the cake" ... they give extra detail and fine tuning on aspects of machine language. If you feel uncertain about any material covered so far, go back. Fix the fundamentals firmly in focus before you proceed and plunge into ponderous points of interest. Temporary Storage: The Stack ----------------------------- The stack is a convenient place to put temporary information. It works like a stack of documents: you may drop (or "push") an item onto the stack; when you take an item back again (or "pull"), you'll get the last one that you put there. Formally, it's called a last-in, first-out (LIFO) discipline; it's natural and easy to understand. The important rule to keep in mind about the stack is: "Leave these premises as clean as when you found them." In other words, if you push three items onto the stack, be sure you pull those three items back off again. Don't ever branch away and leave the stack littered. The stack is in memory at page 1. The stack pointer (SP) is one of the items displayed in the register. To look for the information on the stack, you must add $0100 to the value to get the next available stack position. As an example, if the SP shows a value of $F8, the next item to go on the stack will go into address $01F8; the moment we put an item onto the stack, the pointer will move down so that it becomes $F7. As the stack is filled, the stack pointer goes down. As the items are brought back out of the stack, the stack pointer goes up. A low value in the stack pointer means a full stack: a value below $40 signals trouble. The 650x chip itself doesn't give the stack any special treatment. If a machine language program--probably because of a coding error--wanted to push one thousand items onto the stack, that would be OK as far as the micro- processor was concerned. The stack would never leave page 1: as the stack pointer went down beyond zero, it would wrap around to $FF and keep going. You'd never get those thousand distinct items back, of course, Similarly, if a program wanted to pull a thousand items from the stack--whether or not they had been put there before--the processor would happily move the stack pointer round and round page 1, delivering bytes. There would only be 256 different values delivered, of course, but the processor doesn't care. :113: SP +----+ +------+ | F8 |-------. | USED | 01FF +----+ | +------+ | | USED | 01FE | +------+ | | USED | 01FD | +------+ | | USED | 01FC | +------+ | | USED | 01FB | +------+ | | USED | 01FA | +------+ | | USED | 01F9 | +------+ `----->| FREE | 01F8 +------+ NEXT ITEM | | PUSHED WILL GO : : TO ADDRESS $01F8 . . NEXT ITEM PULLED WILL COME FROM ADDRESS $01F9 Figure 7.1 Within the BASIC environment, the stack pointer starts around $FA (the first item will go into the stack at address $01FA), and goes down from there. When the stack pointer goes below about $40, BASIC will signal OUT OF MEMORY. That's over 160 available locations on the stack, plenty of room for most applications. PHA (push A) and PLA (pull A) ----------------------------- How may we use the stack? Suppose we have a value in the A register and in a moment we will want to use it. First we need to print something, and the character to be printed must be loaded into the A register. How can we put away the value in A and bring it back later? We could slip it into another register with a transfer function (TAX or TAY) and bring it back from there; or, we could store it into memory and load it back. Alternatively, we could PUSH the A register (PHA) to the stack and PULL (PLA) the value back later. :114: Again, let's do an example. Suppose the A register contains 5, and the stack pointer is at $F3. If the program says PHA, the value 5 is stored at address $01F3, and the stack pointer changes to $F2. Later in the program, we encounter the instruction PLA: the stack pointer moves back to $F3 and the value 5 is read from address $01F3 and placed into the A register. It's a handy way to put away a value in A for a moment. PHP (push processor status) and PLP Sometimes when we are writing a program, we want to test for a condition now but act on the result of that test later. We can arrange to do this by putting the flags away for the time being, and then bringing them back when we want to test the flags. We use the instruction PHP (push the processor status word) to place all the flags on the stack, and PLP (pull the processor status word) to restore the flags to the status register (SR). Why would we need to do this? Perhaps an example will illustrate. Suppose we are reading a file of customer purchases, and as we input a data item, we discover that this is the last one--it's the end of the file. That means that we want to close the file and summarize the customer's activity--though not just yet. First, we must handle the item of information that we have input. So we can "stack" our end-of-file information, handle the last record in the same way as the previous records, then bring back the status to wee whether it's time to close the file and print the totals. We'll be using the PHPH and PLP for exactly this kind of task in the next chapter. PHA and PHP both put exactly one item onto the stack; PLA and PLP pull one item. There are other commands that handle more than one stack location. JSR and RTS ----------- We know these commands. What are they doing here? When a JSR command is executed, the return address is placed onto the stack. When an RTS command is executed, the return address is picked from the stack, and that's where the program returns to. More precisely, when a JSR occurs, the processor places onto the stack the return address minus one as two bytes; the high-order part of the address goes to the stack first. When an RTS is encountered, the processor takes the two bytes from the stack, adds one, and then proceeds from the address so formed. :115: Example: If address $0352 contains the command JSR $033C, the following events occur. The return address would be $0355, the instruction directly behind the JSR; but an address of $0354 is calculated--the 03 goes to the stack first, and the 54 below it. The subroutine at $033C now starts to run. Eventually, it encounters an RTS. The values 54 and 03 are pulled from the stack and formed into address $0354; one is added, and the processor resumes execution at address $0355. You hardly need to know this. We have been using subroutines for some time without knowing that all this happened. But sometimes, it's useful to be able to examine the stack, asking, "Who called this subroutine?" The answer is there. Interrupts and RTI ------------------ There are three types of interrupt: IRQ, NMI, and the BRK instruction. IRQ (interrupt request) and NMI (non-maskable interrupt) are pins on the 650x. A suitable signal is applied to the appropriate pin will cause the processor to stop what it's doing and run an interrupt routine. The BRK instruction might be thought of as a fake interrupt--it behaves in a similar manner to IRQ. When an interrupt signal occurs, the processor completes the instruction it is currently working on. Then it takes the PC (the program counter, which contains the address of the next instruction) and pushes it onto the stack, high byte first. Finally, it pushes the status register to the stack. That's a total of three bytes that go to the stack. The processor then takes its execution address from one of the following locations: IRQ or BRK - from $FFFE and $FFFF NMI - from $FFFA and $FFFB Whatever value is found in these pointers becomes the interrupt execution address: the processor starts to run at that address. Eventually, the processor encounters an RTI instruction. The status register and the PC address are taken from the stack, and the interrupted program resumes where it left off. Note that the address on the stack is the return address. This differs from JSR/RTS, where the return address minus one is stored. On all Commodore machines, the IRQ strikes about sixty times a second. The NMI is unused (but available) on PET/CBM; it isn't available in the 264 series; and on the VIC-20 and Commodore 64, it is used for the RESTORE key and for RS-232 communications. :116: The BRK command can be distinguished from the IRQ signal by means of a bit in the status register. Bit 4 is the B, or break flag; if it's set, the last interrupt was caused by a BRK and not by an IRQ. Later, we will discuss using the interrupt routines for our own programming. By the time we can "catch" the interrupt, several more things will have been pushed to the stack: the A, X, and Y registers. This is done by a ROM program, not the processor; but it will prove handy since we can use these registers, safe in the knowledge that they will be restored at the end of the interrupt. Mixing and Matching ------------------- The processor uses the stack mechanically. If we know how to manipulate the stack, we can use it for surprising things. For example, an RTS can be given even though there was no subroutine call; all we have to do is prepare the stack with the proper address. Try to figure out what the following code will do: LDA #$24 PHA LDA #$68 PHA RTS This coding is identical to JMP $2469. We have placed a "false return address" onto the stack, and RTS has removed it and used it. This may not seem very useful, since we could easily have coded the JMP $2469 directly. But look at the following code: LDA TABLE1,X PHA LDA TABLE2,X PHA RTS The principle of coding is the same, but now we can "fan out" to any of several different addresses, depending on the value contained in X. USR: A Brother to SYS ---------------------- We have used SYS a number of times. It means, "Go to the address supplied and execute machine code there as a subroutine." USR is similar in may respects: it means, "Go to a fixed address and execute machine code there as a subroutine." The fixed address may be POKEd into the USR vector. On most Commodore machines this is at addresses 1 and 2; on the Commodore 64, it's at addresses 7875 and 786 (hex 0311 and 0312). :117: There's another difference that seems important at first. SYS is a command; USR is a function. You cannot type the command USR(0)--all you'll get is SYNTAX ERROR. You must say something like PRINT USR(0) or X=USR(0), where USR is used as a function. It seems as if SYS was meant to connect to action programs, and USR was meant to link to evaluation programs. In reality, the difference in usage is not that great. Whatever value is within the parentheses--the argument of the USR function-- is computed and placed into the floating accumulator before the USR function is called. The floating accumulator is located at $5E to $63 in most PET/CBM computers, and at $61 to $66 in VIC-20, Commodore 64, and PLUS/4. Floating- point representation is complex, as we have hinted in Chapter 6. Most beginning programmers prefer to leave this area alone and pass values through memory POKEs or integer values. When the USR function returns control to BASIC, the function value will be whatever is in the floating accumulator. If we have not modified it, this will be the same as the argument, so that in many cases PRINT USR(5) would print a value of 5. Interrupts: NMI, IRQ, and BRK ------------------------------ We have mentioned the mechanical aspects of interrupt. Now let's look at how to use the interrupt for simple jobs. The IRQ connects through a vector in RAM; if we change the address within the vector, we will change the address to which the interrupt goes. The interrupt vector is located as follows: Most PET/CBM: 0090-0091 (decimal 144-145) VIC/Commodore 64: 0314-0315 (decimal 788-789) Before we change this vector, we should realize something quite important: the interrupt does a lot of work sixty times a second. It updates the clock, checks the RUN/STOP key, gives service to the cassette motors, flashes the cursor, and handles keyboard input. If you thoughtlessly change the IRQ vector, it will stop doing these things, and it's hard to handle a computer when it has a dead keyboard. You could try to program all these functions yourself; but there's an easier way. :118: Suppose we use the vector to temporarily divert to our own program, and at the end of our program we allow the interrupt to continue with whatever it was going to do anyway. That way, our program would get service sixty times a second, and the usually interrupted jobs would still get done. It's not hard to do, and we can achieve many interesting effects by diverting the interrupt. Remember that the interrupt runs all the time, even when no BASIC program is running. By playing with the interrupt, we can make a permanent computer system change that is in effect even when no programs are in place. Care must be taken in changing an interrupt vector. Suppose we are beginning to change the two-byte address; we have changed the first byte, and suddenly, the interrupt strikes. It will use an address that's neither fish nor fowl: half is the old address, and half is the new. In such a case, it's likely that the interrupt will become confused; and f the interrupt is confused, the whole computer is in trouble. We must find a way to prevent interrupt from striking when we change the vector. We could do this in machine language: before a routine to change the IRQ vector, we could give the instruction SEI (set interrupt disable). After this instruction is given, the IRQ cannot interrupt us. We may set the vector and then re-enable the interrupt with the instruction CLI (clear interrupt disable). Be sure that you do this, since the interrupt routine performs many vital functions. We may say that we have masked off the interrupt in the time period between execution of SEI and CLI. The NMI interrupt, however, is non-maskable, and SEI will have no effect on it. There's a second way of turning off the interrupt--that is, by shutting off the interrupt source. Something makes an interrupt happen--it might be a timer, it might be an external signal, or it might even be a screen event. Whatever it is, we can get to the source of the interrupt and disconnect it. Almost all interrupt signals are delivered through an IA (interface adapter) chip; and these chips invariably allow the path of the interrupt signals to be blocked temporarily. We'll discuss the IA chips later; for the moment, the normal interrupt signals can be blocked with the following actions: Commodore 64: Store $7F into address $DC0C (POKE 56333,127) to disable; store $81 into the same address (POKE 56333,129) to re-enable. :119: VIC-20: Store $7F into address $912E (POKE 37166,127) to disable; store $C0 into the same address (POKE 37166,192) to re-enable. PET/CBM: Store $3C into address $E813 (POKE 59411,60) to disable; store $3D into the same address (POKE 59411,61) to re-enable. It goes without saying that the above POKEs should not normally be given as direct commands; the first POKE in each case will disable the keyboard (among other things), and you won't be able to type the restoring POKE. A warning about interrupt programs: changing the IRQ vector is likely to make it difficult to load and save programs. You may need to put the vector back to its original state before you attempt any of these activities. An Interrupt Project -------------------- The following project is written for the Commodore 64 only. The equivalent coding for PET/CBM may be found in Appendix E. Let's write the coding for the interrupt itself. Sixty times a second, we'd like to copy the contents of address $91 to the top of the screen. Here goes: .A 033C LDA $91 .A 033E STA $0400 .A 0341 JMP ($03A0) Why the indirect jump? We want to "pick up" the regular interrupt routine, but we don't know where it is yet. When we find the address, we'll but it into locations $03A0/$03A1 so that the indirect jump will link things up for us. Now let's write the routine to enable the above interrupt coding. First, let's copy the interrupt address from $0314 into the indirect address at $03A0: .A 0344 LDA $0314 .A 0347 STA $03A0 .A 034A LDA $0315 .A 034D STA $03A1 Now we are ready to put the address of our own interrupt routine (at $033C) into the IRQ vector: .A 0350 SEI .A 0351 LDA #$3C .A 0353 STA $0314 .A 0356 LDA #$03 .A 0358 STA $0315 .A 035B CLI .A 035C RTS We will enable the new interrupt procedure by a SYS to $0344, above (SYS 836). Before we give that command, let's write the coding to put everything back: .A 035D SEI .A 035E LDA $03A0 .A 0361 STA $0314 .A 0364 LDA $03A1 .A 0367 STA $0315 .A 036A CLI .A 036B RTS As you can see, we put the original address back, copying it from the indirect address area where it was saved. Once this code is in place, disassembled, and checked, you may return to BASIC. SYS 836 will invoke the new interrupt code; SYS 861 will turn it off. Note that the character (a copy of the contents of address $91) appears at the top left of the screen. The character seems to be affected by pressing some keys; can you established how may keys are involved? Some models of Commodore 64 may print blue-on-blue when screen memory is POKEd, as we are doing now. If so, the character may not always appear in the left-hand corner. Project for enthusiasts: Fix this problem by storing a value into the color nybble table at address $D800. The IA Chips: PIA, VIA, and CIA -------------------------------- The interface adapter (IA) chips are richly detailed. To understand them fully, you'll need to read the specifications in some detail. Here, we'll give their main functions. PIA stands for peripheral interface adapter, VIA for versatile interface adapter, and CIA for complex interface adapter. There is speculation among Commodore owners that the next interface chip will be called "FBI." The functions performed by an interface adapter are: 1. Event latching and interrupt control. We have noted that these chips can be manipulated to block the interrupt signal. In fact, they do more than "gating" the signal--allowing it through to the processor's IRQ trigger or alternatively blocking it. They also often latch a signal into an event flag, sometimes called an interrupt flag. :121: Latching is important. A triggering event may be brief; so short, in fact, that the original signal causing interrupt might go away before the processor can look at it. An IA event flag locks in the signal and holds it until the program turns it off. NEAR "THE IA CHIPS..." ON +-------+ OFF INTERRUPTING----------->| LATCH |<-------------. EVENT +-------+ | | | v COMPUTER EVENT ACKNOWLEDGEMENT FLAG Figure 7.2 If an event has time importance--that is, if the event's timing must be accurately measured, or if the event flag must be cleared quickly so as to allow for the detection of a new event--we may link the event flag to the interrupt line. If we do so, the occurrence of the event will cause the processor to be interrupted. We must write coding linked to the interrupt routines to detect this event, clear the flag, and do whatever processing is needed. We set up this link to the interrupt line by means of a register usually called the interrupt enable register. On the other hand, the event might not be particularly time critical. In this case, you can simply check the appropriate event flag from time to time. When the event occurs, you may then clear the flag and handle it. No interrupt is needed. Even when an event flag is not connected to the interrupt, it may be called an interrupt flag; don't let the terminology confuse you. Whether or not you handle these events through interrupt sequences, it's important to know that it's your job to turn the event flag off. The flag will hold the signal until it's turned off--and it usually won't turn off unless your program takes some action to do this. The various flags are triggered by timers or external signals. You can read a flag's state by checking the interrupt flag register. Several flags will be packed together in this register; as always, you will use the logical operators--AND, ORA, and EOR--to extract or modify the particular flags in which you are interested. You may also use the IFR (interrupt flag register) to clear the flags. 2. Timing. Certain addresses within the IA chip are often assigned as "timers." These timers count down; in other words, if we place a value of $97 into a timer and look at the value immediately, we might find that it has gone down to $93. Timers come in many shapes and sizes-- again, check the chip reference for details--but most of them toggle an interrupt flag when they have counted down to zero. As discussed, you may choose whether or not this flag will really cause an interrupt signal. :122: 3. Input/output. Certain addresses within the IA chip are connected to "ports," which extend outside the computer. Thus, the computer can detect external events or control external devices. Output signals are usually latching in nature: in other words, a store command might be taken to mean, "turn on port 5 and leave it on." Tips on IA Chips ---------------- Many addresses within an IA chip have a different meaning, depending on whether they are being written to (stored) or read (loaded). Watch for this when you are reading the chip specifications. Often, the action required to turn an interrupt flag off is odd. It looks like the kind of thing you should do to turn the flag on. Keep in mind that a flag may be turned on only by the external activity to which it is linked. So, although it may seem odd to turn the flag in bit zero off by storing a value of 1 (which would seem to want to turn bit zero on), don't worry. You'll get used to it. The IER (interrupt enable register) is often a source of problems. In many cases, the high bit of a value we are storing has a special meaning: if it's set, the other bits will cause the appropriate interrupt connections to turn on; if it's clear, the other bits will cause the appropriate interrupt connections to be turned off. You may recall that we shut off the Commodore 64 interrupt by storing $7F into address $DC0D. This may seem odd: we're storing a binary value of $01111111, which might seem to be turning bits on. In fact, the high bit of zero signals that all the remaining bits are "turn off" signals; so the value causes all interrupts to be blocked. Infiltrating BASIC: The Wedge ------------------------------ In zero-page, there's a subroutine that the BASIC interpreter uses frequently to obtain information from your BASIC program. It's used to get a character from your BASIC program, and to check it for type (numeric, end-of-command, or other). The routine is normally entered at either of two points: CHRGET, to get the next character from your BASIC program; and CHRGOT, to recheck the last character. The subroutine is located at $0070 to $0087 in most PET/CBM computers, and at $0073 to $008A in VIC-20 or Commodore 64. You may disassemble it there if you wish. The coding is described below. :123: Since CHRGET is in different locations, depending on the machine, the following code is shown with symbolic addresses. That is, instead of showing the hex address value, the address is given a name, or symbol. Thus, CHRGOT might represent address $0079, CHRGOT+1 would represent $007A, and so on. CHRGET INC CHRGOT+1 BNE CHRGOT INC CHRGOT+2 CHRGOT LDA $xxxx This subroutine is self-modifying, that is, it changes part of itself as it runs. That's not always a good programming technique, but it works well here. The first part of the subroutine adds one to the address used by instruction CHRGOT. This is a standard way of coding an address increment: add one to the low byte of the address; if that makes it zero, the low byte must have gone from $FF to $00, in which case , add one to the high byte. The address loaded by CHRGOT is within your BASIC program, or within the input buffer if you have just typed a direct command. Before we follow the next piece of code, let's look at our objectives: 1. If we find a space, go back and get the next character. 2. If we find a zero (BASIC end of line) or a colon (hex $3A, BASIC end- of-statement), we wish to set the Z flag and exit. 3. If we find a numeric, we wish the C flag to be clear; if we do not find a numeric, we wish the C flag to be set. CHRGOT LDA $xxxx CMP #$3A BCS EXIT If the character is a colon ($3A), we'll leave the subroutine with the Z flag set. That's one of our objectives. Here's part of another one: if the character is $3A or higher, it can't possibly be an ASCII numeric--numerics are in the range of $30 to $39. CMP #$20 BEQ CHRGET If the character is a space, we go back and get another character. :124: The following code looks rather strange, but it's correct. After the two subtractions, the A register will be back where it started: SEC SBC #$30 SEC SBC #$D0 After this, the A register is not changed; but the C flag will be set if the number is less than $30, which means that it is not an ASCII numeric. Additionally, the Z flag will be set if A contains a binary zero. We have met all of our objectives and may now return: EXIT RTS Breaking Into BASIC ------------------- Since BASIC comes to this subroutine often, we can infiltrate BASIC by changing this subroutine. Extra coding in this area is often called a "wedge" program. We must be very careful: o We must leave A, X, and Y unchanged; either we must not use them or we must save them away and bring them back. o We must not interfere with the flags. o We must be careful not to slow BASIC down too much. This is a tall order. The last requirement is often helped by two techniques: use the wedge to implement extra commands in direct mode only; and make use of a special character to identify our special commands. In PET/CBM, we may choose to modify this subroutine in either of two places: near the beginning, in CHRGET; or after the LDA, in CHRGOT. Each location has its advantages. In the CHRGET area, we don't need to preserve the A register or status flags, since CHRGOT will fix them up for us. In the area following CHRGOT, we have the character we wish to examine in the A register. But in either case, it's an exacting job. VIC-20 and Commodore 64 have made the job much more easy by providing a vector at address $0308/$0309 that will give us control of the computer, if we wish, immediately before each BASIC command is executed. We still need to use due care, but we have much more latitude. :125: The address of the instruction at CHRGOT is often referred to as TXTPTR, the text pointer. This address always points to the BASIC command being executed at the moment. If we want to participate in reading BASIC, we must learn to use TXTPTR to get information--usually by means of indirect, indexed addressing--and to leave this address pointing at a suitable place when we return control back to the normal BASIC handling programs. Project: Adding a Command -------------------------- Let's add a simple command to the VIC and Commodore 64 by using the $0308 vector. The ampersand (&) character isn't used in most BASIC programs, so we'll make it mean this: whenever you see the code "&", print ten asterisk (*) characters to the computer screen, followed by a carriage return. As with our interrupt program, we'll copy the old address from $0308/$0309 into an indirect address location, so that we can link up with the normal computer routines as necessary. An important point: the vector will give us control, if we want it, with TXTPTR positioned immediately before the next instruction. When we return control to BASIC, we must be sure that TXTPTR is similarly positioned. Here's our instruction "intercept": .A 033C LDY #$01 We're going to use indirect, indexed addressing to "look ahead" at the instruction. Let's look, using TXTPTR as an indirect address: .A 033E LDA ($7A),Y Since Y equals one, we'll look just beyond the address to which TXTPTR is pointing: .A 0340 CMP #$26 .A 0342 BEQ $0347 .A 0344 JMP ($03A0) If the character is an ampersand, we'll branch ahead to $0347. If not, we'll connect through the indirect vector to the regular BASIC interpreter code: .A 0347 JSR $0073 We may call CHRGET to move the pointer along. Now TXTPTR points squarely at the ampersand character. We are ready to print ten asterisks: :126: .A 034A LDY #$00 .A 034C LDA #$2A .A 034E JSR $FFD2 .A 0351 INY .A 0352 CPY #$0A .A 0354 BCC $034E .A 0356 LDA #$0D .A 0358 JSR $FFD2 .A 035B JMP $0344 The above code prints an asterisk ($2A) ten times and then prints a RETURN ($0D). It then goes to the regular BASIC interpreter, which will look behind the ampersand character for a new BASIC command. Now we need to set up the link to our program. We'll write the code to do this starting at $035E, so that SYS 862 will put the new command (ampersand) into effect: .A 035E LDA $0308 .A 0361 STA $03A0 .A 0364 LDA $0309 .A 0367 STA $03A1 .A 036A LDA #$3C .A 036C STA $0308 .A 036F LDA #$03 .A 0371 STA $0309 .A 0374 RTS When you have completed and checked the code (remember this is for VIC and Commodore 64 only), return to BASIC. Type NEW and write the following program: 100 PRINT 34:&:PRINT 5+6 110 & 120 PRINT "THAT'S ALL" If you type RUN, you will get a SYNTAX ERROR in line 100. We have not yet implemented our "ampersand" command. Type the command SYS 862. Now type RUN again. The ampersand command obediently prints ten asterisks each time it is invoked. Infiltrating BASIC isn't an easy job. But it can be done. Things You Have Learned ----------------------- - The stack is located in page 1, from $01FF moving down to $0100. It is used for holding temporary information. A program may push information to the stack, and the pull it back later. The last item that has been pushed onto the stack will be the first item to be pulled back off. :127: - Great care must be taken to ensure that your program pulls exactly the same number of items back from the stack as it pushed. In particular, be sure that a branch or jump does not inadvertently omit a needed stack activity. A badly handled stack is often fatal to the program run. - PHA pushes the contents of A to the stack; PLA pulls from the stack into the A register. These two commands are often used to temporarily save A. PHP pushes the status register (SR); PLP pulls it back. These two commands are often used for "deferred decisions." - JSR pushes a return address (minus 1) to the stack; RTS recalls this address. We may use JSR and RTS without needing to know the role the stack plays, since the two commands take care of the details for us. - Interrupts, including the BRK instruction, push three items to the stack; RTI brings them back so that the interrupted program may resume. - USR is a function, as opposed to SYS, which is a command. USR goes to a preset address, takes a numeric argument, and can return a value. In practice, USR and SYS are used in quite similar ways. - Commodore ROM systems contain coding for the interrupt sequences that cause the data registers--A, X, and Y--to be pushed to the stack, and a branch to be taken through an indirect address that the user can modify. Since interrupt is active virtually all the time, it may be used to create activities that are active even when no BASIC program is running. - The various IA chips--PIA, VIA, and CIA--perform many different functions, including: recording events in latching flags and controlling interrupts; timing; and connecting input/output ports. The detailed specification sheets must be studied for these rather complex details. - A subroutine called CHRGET is used frequently by the BASIC interpreter when a BASIC program is running. We may modify or add to this subroutine in order to add to or modify the BASIC language itself. Questions and Projects ---------------------- If you redirect the interrupt vector to your own machine language program, you can copy all of zero page to the screen. Use indexing; start X at zero; and walk through the whole of zero page, loading the memory contents and storing (indexed again, of course) to the screen. Don't forget to connect up your code to the regular interrupt entry address. You'll get a fascinating screen. There will be timers going, and as you type on the keyboard you'll see various inner values changing around. Enjoy the view. :128: It's sometimes suggested that a good way to pass information to a subroutine is to push the information onto the stack and call the subroutine. The subroutine can pull the information from the stack. What's wrong with this suggestion? The above suggestion can be implemented, but it takes a lot of careful stack work. You might like to work through the logic needed to do this. There are some utility programs which, when placed in the computer, allow a listing to be "scrolled." In other words, if the screen shows BASIC lines 250 to 460, the user can take the cursor to the bottom of the screen and continue to press the cursor-down key. New BASIC lines (following 460) will then appear. This is not an easy thing to code, but here's the question: do you think that this feature is done with a SYS command, a wedge, or an interrupt technique? Why? A SYS command from BASIC is like a subroutine call; so it must place an address on the stack to allow RTS to return to BASIC. Take a look at the stack and see if you can determine what address is used to return to BASIC on your machine. :129: :130: :131: Chapter 8 Timing, Input/Output, and Conclusion This chapter discusses: o How to estimate the speed of your program o Input and output from tape, disk, and printer o Review of instructions o Debugging o Symbolic assemblers o Where to go from here :132: Timing ------ For many applications, machine language programs seem to run instantaneously. The speed of the 650x is much greater than that of other devices, including the human user. The machine language program usually ends up waiting for something: waiting for the keyboard, waiting for the printer, waiting for the disk, or waiting for the human to read and react to the information presented on the screen. Occasionally, it may be important to get fairly precise timing for a machine language program. If so, the following rules of thumb may be kept in mind: - All timing estimates are crude if the interrupt routines are still active. The effect of interrupt on timing can be crudely estimated by adding 10 percent to the running time. - Remember to allow for loops. If an instruction within a loop is repeated ten times, its timing will need to be counted ten times. - The "clock speed," or memory cycle speed, of most Commodore machines is roughly 1 microsecond--one millionth of a second. The precise number varies from one machine to another, and also varies between North America and other regions. - Most instructions run at the fastest imaginable speed. Count the memory cycles, and that's how fast the instruction will execute. For example, LDA #$0D will need two memory cycles just to get the instruction--and that's how fast it runs. LDA $0500,X will usually take four memory cycles: three to get the instruction, and one to fetch the data from page 5. Exceptions: no instruction runs in less than two cycles; and shift/ rotate instructions, INC/DEC, and JSR/RTS take longer than you might expect by this rule. - Branches time differently, depending on whether the branch is taken (three cycles) or not taken (two cycles). - When a page boundary is crossed, the computer needs an extra cycle to do the arithmetic. If the program branches from $0FE4 to $1023, there will be an extra cycle; if we LDA $24E7,Y, there will be an extra cycle if Y contains a value of $19 or greater. Detailed timing values can be obtained from most tables of instructions. Let's take a simple routine and estimate its timing. The following program logically ANDs the contents of 100 locations from $17E0 to $1844: 033C LDX #$00 033E LDA #$00 0340 AND $17E0,X 0343 INX 0345 CPX #$64 0347 BCC $0340 0349 RTS :133: We may work out timing as follows: LDX #$00--executed once: 2 LDA #$00--executed once: 2 AND $17E0,X: 32 times at 4 cycles: 128 68 times at 5 cycles (page cross): 340 INX--100 times at 2 cycles: 200 CPX #$64--100 times at 2 cycles: 200 BCC--99 times at 3 cycles: 297 1 time at 2 cycles (no branch): 2 RTS--6 cycles: 6 Total time: 1171 cycles, or slightly over one thousandth of a second. We might add 10 percent to allow for the effects of interrupt; and since this is a subroutine, we could also add the extra six cycles needed to perform the JSR. Where timing is critical, the interrupt could be locked out with SEI. Be careful; it's seldom necessary, and is potentially dangerous. Input and Output ---------------- We know that calling the kernal routine CHROUT at $FFD2 will send an ASCII character to the screen. We may also redirect output to any logical file. We have seen that we may obtain input from the keyboard buffer into the A register by calling kernal routine GETIN at $FFE4. We may also redirect the input so that we draw information from any logical file. The same commands--$FFD2 and $FFE4--still perform the input and output. But we "switch" either of them to connect to a chosen device--or more accurately, a chosen logical file. The file must be open; we may switch to the file, and then switch back to normal I/O as we wish. Switching Output ---------------- We use subroutine CHKOUT at address $FFC9 to switch output to a logical file. When we want to restore output to the screen, we call subroutine CLRCHN at $FFCC. This is not the same as an OPEN and CLOSE--we simply connect to the file and disconnect, and we can do this as many times as we want. :134: KEYBOARD +---------+ ,------. +----+ INPUT | | OUTPUT | | |::::|-o<--O-----------| PROGRAM |------------O-->o-|SCREEN| +----+ | | | | o +---------+ o `------' / o o \ | / CHKIN ($FFC6) CHKOUT ($FFC9) \ | | | SETS THE INPUT SETS THE OUTPUT | | INPUT SWITCH SWITCH OUTPUT DEVICES DEVICES CLRCHN ($FFCC) RESTORES BOTH SWITCHES TO "NORMAL" Figure 8.1 *---------------------------------------------------------------------------* | | | Subroutine: CHKOUT | | Address: $FFC9 | | Action: Switches the output path (used by CHROUT, $FFD2) so that | | output is directed to the logical file specified in the X | | register. The logical file must previously have been | | opened. | | | | The character subsequently sent by $FFD2 is usually ASCII (or PET ASCII). | | When sent to the printer, special characters--text/graphics, width--will | | be honored in the usual way. Similarly, disk commands can be transmitted | | over secondary address 15 if desired; a logical "command channel" file | | must be previously opened. | | | | Registers: Registers A and X will be changed during the CHKOUT call. Be | | sure to save any sensitive data in these registers before calling CHKOUT. | | | | Status: Status flags may be changed. In VIC and Commodore 64, the C | | (carry) flag indicates some type of problem with connecting to the output | | channel. | | | *---------------------------------------------------------------------------* :135: To switch output to logical file 1, we would need to follow these steps: 1. Load a value of 1 into X (LDX #$01). 2. JSR to address $FFC9. Once the output is switched, we may send as many characters as we wish using subroutine $FFD2. Eventually, we must disconnect from the logical file and return to our default output, the screen. We do this by calling subroutine CLRCHN at address $FFCC. *---------------------------------------------------------------------------* | | | Subroutine: CLRCHN | | Address: $FFCC | | Action: Disconnects input and output from any logical files and | | restores them to the "default" input and output channels, | | keyboard and screen. The logical files are not closed, and | | may be reconnected at a later time. | | | | Registers: Registers A and X will be changed during the CLRCHN call. Be | | sure to save any sensitive data in these registers. | | | | Status: Status flags may be changed. In VIC and Commodore 64, the C | | (carry) flag indicates some type of problem with output | | | *---------------------------------------------------------------------------* The logical file concept is important. I may send to any destination-- cassette, printer, disk, or screen--without knowing which device is involved. I send the characters on their way and the operating system sees that they are delivered wherever they need to go. This simplifies the machine language programmer's job. It's a simple task to send the characters to some logical channel; the programmer does not need to take special coding action depending on which device is involved. Output Example -------------- If we wanted to print the message HI on the printer, we might code as follows. First, we'll open the printer channel in BASIC. Let's use logical file number 1: 100 OPEN 1,4 110 SYS 828 120 CLOSE 1 If you don't have a printer, you may open the file to cassette (OPEN 1,1,2) or to disk (OPEN 1,8,3,"0:DEMO,S,W"). The machine language program won't care: it will sent to logical file number 1 no matter what it is; it might even be the screen (OPEN 1,3). Let's write the coding: :136: .A 033C LDX #$01 .A 033E JSR $FFC9 Now the output is connected to logical file 1. Let's say HI: .A 0341 LDA #$48 .A 0343 JSR $FFD2 .A 0346 LDA #$49 .A 0348 JSR $FFD2 .A 034B LDA #$0D .A 034D JSR $FFD2 .A 0350 JSR $FFCC .A 0353 RTS Don't forget to send the RETURN--the printer needs it. After the machine language program says HI, the program will return to BASIC and close the file. Notice that the machine language program doesn't care what it's saying HI to...it sends the data to logical file 1. Switching Input --------------- We use subroutine CHKIN at address $FFC6 to switch input so as to draw data from a logical file. When we want to restore input from the keyboard, we call subroutine CLRCHN at $FFCC. Again, this is not the same as OPEN and CLOSE--we simply connect the file and disconnect, and we can do this as many times as we want. *---------------------------------------------------------------------------* | | | Subroutine: CHKIN | | Address: $FFC6 | | Action: Switches the input path (used by GETIN, $FFE4) so that | | input is taken from the logical file specified in the X | | register. The logical file must previously have been | | opened. | | | | The character subsequently obtained by $FFE4 into the A register is | | usually ASCII (or PET ASCII). A binary zero received from a file usually | | represents exactly that: an input character whose value is CHR$(0); this | | is different from keyboard GETIN where a binary zero means "no key | | pressed." When accessing a file, ST (address $90 for VIC and Commodore | | 64, $96 for most PET/CBM) is used for its usual functions of signaling | | end-of-file or error. Similarly, disk status information can be received | | over secondary address 15 if desired; a logical "command channel" file | | must be previously opened. | | | | Registers: Registers A and X will be changed during the CHKIN call. Be | | sure to save any sensitive data in these registers before calling CHKIN. | | | | Status: Status flags may be changed. In VIC and Commodore 64, the C | | (carry) flag indicates some type of problem with connecting to the input | | channel. | | | *---------------------------------------------------------------------------* :137: To switch input to logical file 1, we would need to follow these steps: 1. Load a value of 1 into X (LDX #$01). 2. JSR to address $FFC6. Once the input is switched, we may obtain as many characters as we wish using subroutine $FFE4. Eventually, we must disconnect from the logical file and return to our default input--the keyboard. We do this by calling subroutine CLRCHN at address $FFCC. This is the same subroutine that disconnects output from a logical file. Input Example ------------- We can write a program to read an input file from disk or cassette. First, let's write the file. We open the file according to its type: Disk: OPEN 1,8,3,"0:DEMO,S,W" Cassette: OPEN 1,1,1 This may be done with a direct statement. Now let's write a few things to the file: PRINT #1,"HELLO THIS IS A TEST" PRINT #1,"THIS IS THE LAST LINE" CLOSE 1 If we have typed in the above statements correctly, we should have a completed sequential file written on cassette or disk. Before writing the machine language input program, let's examine how we might read the file back in BASIC: Disk: 100 OPEN 1,8,3,"DEMO" Cassette: 100 OPEN 1 110 INPUT #1,X$ 120 PRINT X$ 130 IF ST=0 GOTO 110 140 CLOSE 1 We might alternatively have written lines 110 and 120 as 120 GET #1,X$ 130 PRINT X$; This more closely approximates the logic flow of our machine language program, since it will get the characters one at a time. If you are unsure about the role of ST, read up on it. We will use the same variable (at its address of $90 or $96) to do exactly the same thing in machine language. Type NEW and enter the following program: :138: Disk: 100 OPEN 1,8,3,"DEMO" Cassette: 100 OPEN 1,1,1 110 SYS 828 120 CLOSE 1 We will read the file and copy it to the screen entirely in machine language. Let's start coding at $033C: .A 033C LDX #$01 .A 033E JSR $FFC6 Now the input is connected to logical file 1. Let's get information from it and put it on the screen: .A 0341 JSR $FFE4 .A 0344 JSR $FFD2 We must check ST as we would in BASIC. ST might be at either of two addresses, depending on the system: VIC, Commodore 64: .A 0347 LDA $90 PET/CBM: .A 0347 LDA $96 If ST is zero, there is more to come from the file; we may go back. If ST is nonzero, there could be an error or we may be at the end of the file. In either case, we don't want to read more from the file. .A 0349 BEQ $0341 .A 034B JSR $FFCC .A 034E RTS Check it and try it. The file is delivered to the screen quickly. A File Transfer Program ----------------------- Let's write a program to transfer a sequential file from any common device to any other. BASIC will sort out which files to handle; once the files are opened, machine language will take from and deliver to the appropriate logical devices as desired. It's not a good idea to switch input and output at the same time--in other words, to call both $FFC6 and $FFC9 without canceling either via $FFCC. The kernal doesn't mind, but it confuses the peripheral devices, which expect to have exclusive occupancy of the data bus to the computer.. So we'll follow the pattern: switching on, sending or receiving, switching off, and then going to the other device. :139: One more thing. ST tells us the status of the last device handled. Consider: if we input a character, then output a character, and then check the value of ST, we have a problem. ST will not tell us about the input, since the last device handled was output; thus, we won't know if we are at the end of the file or not. In machine language, as in BASIC, we must code carefully to solve this problem. Here comes BASIC: 100 PRINT "FILE TRANSFER" 110 INPUT "INPUT FROM (DISK,TAPE)";A$ 120 IF LEFT$(A$,1)="T" THEN OPEN 1:GOTO 160 130 IF LEFT$(A$,1)<>"D" GOTO 110 140 INPUT "DISK FILE NAME";N$ 150 OPEN 1,8,3,N$ 160 INPUT "TO (DISK,TAPE,SCREEN)";B$ 170 IF LEFT$(B$,1)="S" THEN OPEN 2,3:GOTO240 180 IF LEFT$(B$,1)="D" GOTO 210 190 IF LEFT$(B$,1)<>"T" GOTO 160 200 IF LEFT$(A$,1)="T" GOTO 160 210 INPUT "OUTPUT FILE NAME";F$ 220 IF LEFT$(B$,1)="D" THEN OPEN 2,8,4,"0:"+N$+",S,W" 230 IF LEFT$(B$,1)="T" THEN OPEN 2,1,1,N$ 240 SYS xxxx 250 CLOSE 2:CLOSE 1 We'll work this out for the Commodore 64 computer; you can adjust it for PET/CBM or VIC-20. The above BASIC program should not take up more than 511 bytes; on a standard Commodore 64, that means that we'll have clear space for our machine language program starting at $0A00 (decimal 2560). We'll move the start-of-variables along, of course, so that our machine language program won't be disturbed by them. When we first type line 240, we won't know what SYS address to use. After the program is typed in (with SYS xxxx at line 240), we can easily confirm that the machine language can start at $0A00 by checking the start-of- variables pointer. We go back and change 240 to SYS 2560; now we're ready to put in the machine language code: .A 0A00 LDX #$01 .A 0A02 JSR $FFC6 .A 0A05 JSR $FFE4 :140: By this time, we have a character in the A register from the input source. We also have a value in ST, telling us if this is the last character. Let's examine the ST problem: we must check its value now, since ST will be changed after we do the output. But we don't want to take any action based on ST yet; we must first send the character we have received. Let's check ST, and put the results of the check onto the stack: .A 0A08 LDX $90 .A 0A0A PHP If ST is zero, the Z flag will be set; we'll preserve this flag along with the others until we call it back from the stack. If you are adapting this program for the PET/CBM, don't forget that ST is at address $96 for your machine. The next thing we want to do is to disconnect the input by calling $FFCC; but this will destroy the A register. How can we preserve this value? By transferring to another register, or by pushing A to the stack. Let's do that. There will now be two things on the stack. .A 0A0B PHA We are now free to disconnect from the input channel and connect to the output. Here we go: .A 0A0C JSR $FFCC .A 0A0F LDX #$02 .A 0A11 JSR $FFC9 .A 0A14 PLA The A register gets back the last thing saved to the stack, and that, of course, is our input character. We're ready to send it to the output device: .A 0A15 JSR $FFD2 .A 0A18 JSR $FFCC Now we may pick up on the condition of ST that we stacked away earlier. Here come up the flags that we stored: .A 0A1B PLP If the Z flag is set, we want to go back and get another character. If not, we are finished and can return to BASIC, allowing BASIC to close the files for us: .A 0A1C BEQ $0A00 .A 0A1E RTS Important: Before running this program, be sure to move the start-of- variables pointer ($002D/$002E) so that it points at address $0A1F; otherwise, the BASIC variables will destroy this program. :141: Review: The Instruction Set ---------------------------- We started with the load, save, and compare for the three data registers: LDA LDX LDY STA STX STY CMP CPX CPY The instructions are almost identical in action, although only the A register has indirect, indexed addressing modes. We continued with the logical and arithmetic routines that apply only to A: AND ORA EOR ADC SBC Arithmetic also includes the shift and rotate instructions, which may be used on the A register or directly upon memory: ASL ROL LSR ROR Memory may also be directly modified by the increment and decrement instructions, which have related instructions that operate on X and Y: INC DEC INX DEX INY DEY We may transfer control by means of branch instructions, which are all conditional: BEQ BCS BMI BVS BNE BCC BPL BVC The branch instructions can make only short "hops"; the jump instruction is unconditional: JMP Subroutines are called with the jump-subroutine, and returned with return- from-subroutine; we may also return from interrupts: JSR RTS RTI We may modify any of several flags with the appropriate set or clear command. Some of the flags control internal processor operation: for example, the I (interrupt disable) flag locks out the interrupt; the D (decimal mode) affects the way the ADC and SBC work with numbers. :142: SEC SEI SED CLC CLV CLI CLD We may transfer information between the A register and X or Y; and for checking or setting the stack location, we may move the stack pointer to X, or X to the stack pointer. The latter is a powerful command, so use it with care. TAX TAY TSX TXA TYA TXS We may push or pull information from the stack: PHA PHP PLA PLP There's a special test, used mostly for checking IA chips: BIT The BIT test is used only for specific locations: no indexing is allowed. The high bit from the location being tested is transferred straight to the N flag. The next highest bit (bit 6) goes straight to the V flag. Finally, the Z flag is set according to whether the location has any bits set that match the bits set in the A register. Thus, we can check a location with BIT $.... followed by BMI to test the high bit, or BVS to test bit 6, or BNE to test any selected bit or group of bits. It's a rather specialized instruction, but useful for testing input/output ports. Finally, the instruction that does nothing, and the BRK instruction that causes a "false interrupt," usually taking us to the monitor: NOP BRK That's the whole set. With these instructions, you can write programs to make the computer do whatever you choose. Debugging --------- When a program has been written, the next step is to look for any possible errors, or bugs. The process of searching for and systematically eliminating these bugs is called debugging. Most programs are made up of sections, each of which has a clear task to perform. When a program misbehaves, it may be easy to go to the area of the bug, since you can see which parts of the program are working and where things start to go wrong. :143: In case of doubt, you may insert breakpoints into your program. Replace selected instructions with the instruction BRK; this may be done by replacing the instructions' op codes with the value 00. Run the program; when it reaches the first breakpoint, it will stop and the machine language monitor will become active. Examine the registers carefully to see whether they contain the values expected. Display memory locations that the program should have written; the contents will tell you whether the program has been doing its job correctly. When you have confirmed that the program is behaving correctly up to the breakpoint, replace the BRK command at that point with the original op code. Command .G to that address, and the program will continue to the next breakpoint. If it helps your investigation, you may even change memory or registers before continuing program execution. If you carried this procedure to the extreme, you might stop your program after every instruction. It would take time, but you would certainly track down everything the program did. The best debugging takes place at the time you write the program. Write sensibly, not "super cleverly." If you fear getting caught in an endless loop, insert a stop key test (JSR $FFE1) so that you'll still have control of the computer. Get to know your machine language monitor. The monitor uses a number of locations in memory; you'll have trouble debugging a program if it uses the same storage addresses as does your program. Every time you try to check the contents of a memory location to see what your program has done, you'll see the monitor working values instead--and that would be misleading and annoying. Symbolic Assemblers ------------------- Throughout these exercises, we have used small, "nonsymbolic" assemblers such as would be found within a machine language monitor. These are good for beginners; they parallel the machine code quite closely and allow you to keep the working machine clearly in focus. As you write bigger and better programs, these small assemblers will be less convenient. Forward branches and subroutines we have not yet written make it necessary for us to "guess" at the address and fix up our guess later. There is the possible danger that an address will be typed in wrongly ($0345 instead of $0354), causing the program to fail. :144: To help us write more ambitious programs, we may wish to turn to commercially available assembler systems that allow labels or symbolic addresses. If we wish to write code to call a subroutine to input numbers--we might not have written this subroutine yet--we can code JSR NUMIN. When we write the subroutine, we'll put the identifying label NUMIN at the start. As your program is assembled, the proper address of NUMIN is determined, and this address will be inserted as needed. It saves work and helps guard against errors. But symbolic assemblers allow a more powerful capability: they help documentation and allow program updating. Your assembly may be listed to the printer. This allows you to examine and annotate your program, and file the details away for later reference. The assembler allows you to include comments, which improve the readability of the listing but don't affect the machine language program. The source program you have written may be saved and used again later. If you find it is necessary to change the program, bring back the source code from cassette or disk, make the changes, and reassemble. In this way, programs can be easily corrected or updated. [An even worse case scenario is when you find that you need to insert a few instructions in the middle of your machine code. Within a monitor, you need to adjust all of your addresses forward by some number of bytes, retype the entire section of code which follows your change, as well as modify any instructions prior to your change which reference affected addresses. If you were using an assembler, you can merely insert those instructions into the source file and let the assembler figure out all of the addressing issues. -wf] Where To Go From Here --------------------- Almost anywhere. Up to this point, we've been building confidence: trying to give you a feel as to how the pieces work. Now, the real fun--the creative programming--is up to you. Users have varying objectives. You may want to do mathematical operations. You may want to interact upon BASIC programs--analyzing, searching, renumbering. Whatever suits you. Your interest area may be music, graphics, or animation. Machine language will open the door to all of these; its amazing speed makes spectacular effects possible. You may plan to go into hardware and interface new devices to your computer; an understanding of machine language, and IA chips in particular, will be useful. The possibilities are endless. Even if you have no immediate plans to write new programs in machine language, you will have gained an insight into the workings of your machine. Everything that the machine does--BASIC, kernal, everything--is either hardware or machine language. With the elementary concepts we have introduced here, you will be able to go deeper into more advanced texts. Many programming books deal with the abstract 650x chip. That's hard for the beginner; it's difficult to see how the instructions fit within the architecture of a real machine, or how the programs can actually be placed within the computer. By now, you should be able to take a piece of abstract coding and fit it into your system. :145: Many things start to happen at once when you take your first steps in machine language programming. You must learn to use the monitor. You must learn a good deal about how your machine is designed. And you must learn how to fit the pieces together. It takes a while to adapt to the "information shock"-- but things start to fit together. Eventually, you'll have a stronger and sounder view of the whole computer: hardware, software, languages, and usage. What You Have Learned --------------------- - Machine language programs can have run times estimated fairly accurately. In many cases, however, machine language is so fast that detailed speed calculations are not needed. - We can handle input from devices other than the keyboard by switching the identity of the designated input device. If an input channel has been opened as a file, we may connect to it with JSR $FFC6 and disconnect with JSR $FFCC. - We can handle output to devices other than the screen by switching the identity of the designated output device. If an output channel has been opened as a file, we may connect to it with JSR $FFC9 and disconnect with JSR $FFCC. - Once input or output has been switched, we may receive in the usual way with the subroutine at $FFE4, or send in the usual way with the subroutine at $FFD2. - Be careful no to confuse connecting to a channel with opening a file. In a typical program, we open a file only once, but we may connect to it and disconnect from it hundreds of times as we read or write data. - You have met all the instructions of the 650x microprocessor. There are enough for versatility, but not so many that you can't keep track of them all. You have made a worthwhile start in the art and science of machine language programming. Questions and Projects ---------------------- Write a program to read a sequential file and count the number of times the letter "S" (hex 41) appears in the file. Use a BASIC PEEK to print the value. You may assume that "A" will not appear more than 255 times. :146: Rewrite the above to count the number of occurrences of the RETURN character ($0D) in a sequential file. Allow for up to 65535 appearances. Can you attach a meaning to this count? Write a program to print HAPPY NEW YEAR to the printer ten times. If you own a disk system, you know that you can scratch a program named JUNK by using the sequence: OPEN 15,8,15:PRINT#15,"S0:JUNK" Convert the PRINT# statement to machine language a write a program to scratch JUNK. Careful: don't scratch a program that you will need. Write a "typewriter" program to read a line of text from the keyboard and then transfer it to the printer. It will be a more useful program if you show what is being typed on the screen and if you write extra code to honor the DELETE key. :147: Appendix A The 6502/ 6510/6509/ 7501 Instruction Set The four chips differ only in their use of addresses 0 and 1: On the 6502, the addresses are normal memory. On the 6510 and 7501, address 0 is a directional register and address 1 is an input/output register, used for such things as cassette tape and memory control. On the 6509, address 0 is used to switch program execution to a new memory bank; address 1 is used to switch the memory bank accessed by the two instructions LDA (..),Y and STA (..),Y. :148: Addressing Modes ---------------- Accumulator Addressing--This form of addressing is represented with a one byte instruction, implying an operation on the accumulator. Immediate Addressing--In immediate addressing, the operand is contained in the second byte of the instruction, with no further memory addressing required. Absolute Addressing--In absolute addressing, the second byte of the instruction specifies the eight low order bits of the effective address while the third byte specifies the eight high order bits. Thus, the absolute addressing mode allows access to the entire 64K bytes of addressable memory. Zero Page Addressing--The zero page instructions allow for shorter code and execution times by only fetching the second byte of the instruction and assuming a zero high address byte. Careful use of the zero page can result in significant increase in code efficiency. Indexed Zero Page Addressing--(X, Y indexing)--This form of addressing is used in conjunction with the index register and is referred to as "Zero Page, X" or "Zero Page, Y". The effective address is calculated by adding the second byte to the contents of the index register. Since this is a form of "Zero Page" addressing, the content of the second byte references a location in page zero. Additionally, due to the "Zero Page" addressing nature of this mode, no carry is added to the high order eight bits of memory and crossing of page boundaries does not occur. Indexed Absolute Addressing--(X, Y indexing)--This form of addressing is used in conjunction with X and Y index register and is referred to as "Absolute, X", and "Absolute, Y". The effective address is formed by adding the contents of X and Y to the address contained in the second and third bytes of the instruction. This mode allows the index register to contain the index or count value and the instruction to contain the base address. This type of indexing allows any location referencing and the index to modify multiple fields resulting in reduced coding and execution time. Implied Addressing--In the implied addressing mode, the address containing the operand is implicitly stated in the operation code of the instruction. Relative Addressing--Relative addressing is used only with branch instructions and establishes a destination for the conditional branch. The second byte of the instruction becomes the operand which is an "Offset" added to the contents of the lower eight bits of the program counter when the counters is set at the next instruction. The range of the offset is -128 to +127 bytes from the next instruction. :149: Indexed Indirect Addressing--In indexed indirect addressing (referred to as "Indirect, X"), the second byte of the instruction is added to the contents of the X index register, discarding the carry. The result of this addition points to a memory location on page zero whose contents are the low order eight bits of the effective address. The next memory location in page zero contains the high order eight bits of the effective address. Both memory locations specifying the high and low order bytes of the effective address must be in page zero. Indirect Indexed Addressing--In indirect indexed addressing (referred to as "Indirect, Y"), the second byte of the instruction points to a memory location in page zero. The contents of this memory location are added to the contents of the Y index register, the result being the low order eight bits of the effective address. The carry from this addition is added to the contents of the next page zero memory location, the result being the high order eight bits of the effective address. Absolute Indirect--The second byte of the instruction contains the low order eight bits of a memory location. The high order eight bits of that memory location is contained in the third byte of the instruction. The contents of the fully specified memory location are the low order byte of the effective address. The next memory location contains the high order byte of the effective address which is loaded into the sixteen bits of the program counter. Instruction Set--Alphabetic Sequence ------------------------------------ ADC Add Memory to Accumulator with Carry AND "AND" Memory with Accumulator ASL Shift Left One Bit (Memory or Accumulator) BCC Branch on Carry Clear BCS Branch on Carry Set BEQ Branch on Result Zero BIT Test Bits in Memory with Accumulator BMI Branch on Result Minus BNE Branch on Result not Zero BPL Branch on Result Plus BRK Force Break BVC Branch on Overflow Clear BVS Branch on Overflow Set :150: CLC Clear Carry Flag CLD Clear Decimal Mode CLI Clear Interrupt Disable Bit CLV Clear Overflow Flag CMP Compare Memory and Accumulator CPX Compare Memory and Index X CPY Compare Memory and Index Y DEC Decrement Memory by One DEX Decrement Index X by One DEY Decrement Index Y by One EOR "Exclusive-OR" Memory with Accumulator INC Increment Memory by One INX Increment Index X by One INY Increment Index Y by One JMP Jump to New Location JSR Jump to New Location Saving Return Address LDA Load Accumulator with Memory LDX Load Index X with Memory LDY Load Index Y with Memory LSR Shift One Bit Right (Memory or Accumulator) NOP No Operation ORA "OR" Memory with Accumulator PHA Push Accumulator on Stack PHP Push Processor Status on Stack PLA Pull Accumulator from Stack PLP Pull Processor Status from Stack ROL Rotate One Bit Left (Memory or Accumulator) ROR Rotate One Bit Right (Memory or Accumulator) RTI Return from Interrupt RTS Return from Subroutine :151: SBC Subtract Memory from Accumulator with Borrow SEC Set Carry Flag SED Set Decimal Mode SEI Set Interrupt Disable Bit STA Store Accumulator in Memory STX Store Index X in Memory STY Store Index Y in Memory TAX Transfer Accumulator to Index X TAY Transfer Accumulator to Index Y TSX Transfer Stack Pointer to Index X TXA Transfer Index X to Accumulator TXS Transfer Index X to Stack Pointer TYA Transfer Index Y to Accumulator Programming Model ----------------- 7 0 +---------------+ | A | ACCUMULATOR "A" +---------------+ 7 0 +---------------+ | X | INDEX REGISTER "X" +---------------+ 7 0 +---------------+ | Y | INDEX REGISTER "Y" +---------------+ 15 8 7 0 +---------------+---------------+ | PCH | PCL | PROGRAM COUNTER "PC" +---------------+---------------+ 8 7 0 +-+---------------+ |1| S | STACK POINTER "S" +-+---------------+ 7 6 5 4 3 2 1 0 +---------------+ |N V B D I Z C| PROCESSOR STATUS REGISTER "P" +---------------+ | | | | | | | | | | | | | | | | | | | | `---> CARRY 1 = TRUE | | | | | | | | | | | `-----> ZERO 1 = RESULT ZERO | | | | | | | | | `-------> IRQ DISABLE 1 = DISABLE | | | | | | | `---------> DECIMAL MODE 1 = TRUE | | | | | `-----------> BRK COMMAND | | | `---------------> OVERFLOW 1 = TRUE | `-----------------> NEGATIVE 1 = NEGATIVE Figure A.1 :152: *------------------------------------------------------------* | IMM ZP ZP,X (IND,X) (IND),Y ABS ABS,X ABS,Y | | 2 2 2 2 2 3 3 3 | | +------------------------------------------------------+ | ORA | 09 05 15 01 11 0D 1D 19 | | AND | 29 25 35 21 31 2D 3D 39 | | EOR | 49 45 55 41 51 4D 5D 59 | | ADC | 69 65 75 61 71 6D 7D 79 | | STA | 85 95 81 91 8D 9D 99 | | LDA | A9 A5 B5 A1 B1 AD AD A9 | | CMP | C9 C5 D5 C1 D1 CD CD C9 | | SBC | E9 E5 F5 E1 F1 ED ED E9 | *-----+------------------------------------------------------* Op Code ends in -1, -5, -9, or -D *----------------------------------------------------* | IMM ZP ZP,X ZP,Y ABS ABS,X ABS,Y | | 2 2 2 2 3 3 3 | | +----------------------------------------------+ | ASL | 06 16 0E 1E | | ROL | 26 36 2E 3E | | LSR | 46 56 4E 5E | | ROR | 66 76 6E 7E | | STX | 86 96 8E | | LDX | A2 A6 B6 AE BE | | DEC | C6 D6 CE DE | | INC | E6 F6 EE FE | *-----+----------------------------------------------* Op Code ends in -2, -6, or -E *--------+--------* *------------------* *------------------------------* | BPL 10 | BMI 30 | | ABS (IND) | | IMM ZP ZP,X ABS ABS,X | | BVC 50 | BVS 70 | | +------------+ | 2 2 2 3 3 | | BCC 90 | BCS B0 | | JSR | 20 | | +------------------------+ | BNE D0 | BEQ F0 | | JMP | 4C 6C | | BIT | 24 2C | *--------+--------* *-----+------------* | STY | 84 94 8C | Branches -0 Jumps | LDY | A0 A4 B4 AC BC | | CPY | C0 C4 CC | | CPX | E0 E4 EC | *-----+------------------------* Misc. -0, -4, -C *------------------------------------------------------------* | 0- 1- 2- 3- 4- 5- 6- 7- | | +-------------------------------------------------------+ | -0 | BRK RTI RTS | | -8 | PHP CLC PLP SEC PHA CLI PLA SEI | | -A | ASL-A ROL-A LSR-A ROR-A | | +-------------------------------------------------------+ | 8- 9- A- B- C- D- E- F- | | +-------------------------------------------------------+ | -0 | | | -8 | DEY TYA TAY CLV INY CLD INX SED | | -A | TXA TXS TAX TSX DEX NOP | *----+-------------------------------------------------------* Single-byte Op Codes -0, -8, -A Figure A.2 :153: INSTRUCTIONS Imm :Abs :ZP :Acc :Imp :(IX):(I)Y:ZP,X:AbsX:AbsY:Rel :Ind :ZP,Y: 2 :3 :2 :1 :1 :2 :2 :2 :3 :3 :2 :3 :2 : OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:N ZCIDV --------+----+----+----+----+----+----+----+----+----+----+----+----+------- ADC!69 2:6D 4:65 3:.. .:.. .:61 6:71 5:75 4:7D 4:79 4:.. .:.. .:.. .:x$xx--x AND!29 2:2D 4:25 3:.. .:.. .:21 6:31 5:35 4:3D 4:39 4:.. .:.. .:.. .:x x---- ASL .. .:0E 6:06 5:0A 2:.. .:.. .:.. .:16 6:1E 7:.. .:.. .:.. .:.. .:x xx--- BCC .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:90 @:.. .:.. .:- ----- BCS .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:B0 @:.. .:.. .:- ----- BEQ .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:F0 @:.. .:.. .:- ----- BIT .. .:2C 4:24 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:M7x---M6 BMI .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:30 @:.. .:.. .:- ----- BNE .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:D0 @:.. .:.. .:- ----- BPL .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:10 @:.. .:.. .:- ----- BRK .. .:.. .:.. .:.. .:00 7:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- --1-- BVC .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:50 @:.. .:.. .:- ----- BVS .. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:70 @:.. .:.. .:- ----- CLC .. .:.. .:.. .:.. .:18 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- -0--- CLD .. .:.. .:.. .:.. .:D8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ---0- CLI .. .:.. .:.. .:.. .:58 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- --0-- CLV .. .:.. .:.. .:.. .:B8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----0 CMP!C9 2:CD 4:C5 3:.. .:.. .:C1 6:D1 5:D5 4:DD 4:D9 4:.. .:.. .:.. .:x xx--- CPX E0 2:EC 4:E4 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x xx--- CPY C0 2:CC 4:C4 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x xx--- DEC .. .:CE 6:C6 5:.. .:.. .:.. .:.. .:D6 6:DE 7:.. .:.. .:.. .:.. .:x x---- DEX .. .:.. .:.. .:.. .:CA 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- DEY .. .:.. .:.. .:.. .:B8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- EOR!49 2:4D 4:45 3:.. .:.. .:41 6:51 5:55 4:5D 4:59 4:.. .:.. .:.. .:x x---- INC .. .:EE 6:E6 5:.. .:.. .:.. .:.. .:F6 6:FE 7:.. .:.. .:.. .:.. .:x x---- INX .. .:.. .:.. .:.. .:E8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- INY .. .:.. .:.. .:.. .:C8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- JMP .. .:4C 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:6C 5:.. .:- ----- JSR .. .:20 6:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- :154: Imm :Abs :ZP :Acc :Imp :(IX):(I)Y:ZP,X:AbsX:AbsY:Rel :Ind :ZP,Y: 2 :3 :2 :1 :1 :2 :2 :2 :3 :3 :2 :3 :2 : OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:OP N:N ZCIDV --------+----+----+----+----+----+----+----+----+----+----+----+----+------- LDA!A9 2:AD 4:A5 3:.. .:.. .:A1 6:B1 5:B5 4:BD 4:B9 4:.. .:.. .:.. .:x x---- LDX!A2 2:AE 4:A6 3:.. .:.. .:.. .:.. .:.. .:.. .:BE 4:.. .:.. .:B6 4:x x---- LDY!A0 2:AC 4:A4 3:.. .:.. .:.. .:.. .:B4 4:BC 4:.. .:.. .:.. .:.. .:x x---- LSR .. .:4E 6:46 5:4A 2:.. .:.. .:.. .:56 6:5E 7:.. .:.. .:.. .:.. .:0 xx--- NOP .. .:.. .:.. .:.. .:EA 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- ORA 09 2:0D 4:05 3:.. .:.. .:01 6:11 5:15 4:1D 4:19 4:.. .:.. .:.. .:x x---- PHA .. .:.. .:.. .:.. .:48 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- PHP .. .:.. .:.. .:.. .:08 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- PLA .. .:.. .:.. .:.. .:68 4:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- PLP .. .:.. .:.. .:.. .:28 4:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:Restored ROL .. .:2E 6:26 5:2A 2:.. .:.. .:.. .:36 6:3E 7:.. .:.. .:.. .:.. .:x xx--- ROR .. .:6E 6:66 5:6A 2:.. .:.. .:.. .:76 6:7E 7:.. .:.. .:.. .:.. .:x xx--- RTI .. .:.. .:.. .:.. .:40 6:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:Restored RTS .. .:.. .:.. .:.. .:60 6:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- SBC!E9 2:ED 4:E5 3:.. .:.. .:E1 6:F1 5:F5 4:FD 4:F9 4:.. .:.. .:.. .:x x#--x SEC .. .:.. .:.. .:.. .:38 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- -1--- SED .. .:.. .:.. .:.. .:F8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ---1- SEI .. .:.. .:.. .:.. .:78 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- --1-- STA .. .:8D 4:85 3:.. .:.. .:81 6:91 6:95 4:9D 5:99 5:.. .:.. .:.. .:- ----- STX .. .:8E 4:86 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:96 4:- ----- STY .. .:8C 4:84 3:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- TAX .. .:.. .:.. .:.. .:AA 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- TAY .. .:.. .:.. .:.. .:A8 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- TSX .. .:.. .:.. .:.. .:BA 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- TXA .. .:.. .:.. .:.. .:8A 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- TXS .. .:.. .:.. .:.. .:9A 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:- ----- TYA .. .:.. .:.. .:.. .:98 2:.. .:.. .:.. .:.. .:.. .:.. .:.. .:.. .:x x---- Notes: ! = Add 1 to "N" if page boundary is crossed x modified @ = 2 cycles if branch does not occur - not modified 3 cycles if branch occurs to same page M6 memory bit 6 4 cycles if branch occurs to different page M7 memory bit 7 # = Borrow = Not Carry N no. cycles $ = If in decimal mode, Z flag is invalid Accumulator must be checked for zero result Figure A.4 :155: Appendix B Some Characteristics of Commodore Machines :156: PET--Original ROM ----------------- The first PET. It can be recognized by the message seen at power up: *** COMMODORE BASIC *** using asterisks but with no identifying number after the word BASIC. The original machine may be upgraded to Upgrade ROM by fitting a new set of ROM chips. This is a good idea, since the original logic cannot handle disk, does a poor job on cassette data files, has no built-in machine language monitor, and has a zero page architecture that differs significantly from all later PET/CBM's. The BASIC language on this unit is also limited; arrays may not contain over 256 elements, for example. This early machine is becoming rare. PET/CBM--Upgrade ROM -------------------- The first PET that can handle disk. It can be recognized by the message seen at power up: ### COMMODORE BASIC ### using the numbers sign (or octothorpe, if you like). This is much cleaner logic than the previous machine. Its internal structure is similar to that of later PET/CBM units (the 4.0 machines), so that is has much in common with them. It does not have specialized disk commands such as CATALOG, SCRATCH, or DLOAD (the 4.0 disk commands); but these are "convenience" commands and the Upgrade ROM unit can do everything that the later units do. Upgrade ROM machines have a BASIC annoyance: under some circumstances, string variables need to be tidied up using a technique called "garbage collection." This takes place automatically when needed; but when it does, the machine will freeze and seemingly will be dead for a period that may last from a few seconds to as long as a half hour or more. PET/CBM--4.0 ROM and 80 Characters ---------------------------------- This class of machine has been a mainstay of the Commodore line for years. It may be recognized by the message seen at power up: *** COMMODORE BASIC 4.0 *** For the first time, a number appears in the message. :157: These machines are characterized by new BASIC disk commands (CATALOG, etc.) and elimination of garbage-collection delays. Their internal architecture, especially zero page, is quite similar to the previous Upgrade ROM computers. Some time after the initial production of 40-column machines, 80-column machines were introduced, as well as a new 40-column version called the "fat 40." The later machines are distinguished by new screen/keyboard features, most noticeable of which is that the cursor movement keys repeat automatically. Subsequently, two memory-expanded machines became available. The 8096 came fitted with 96K of RAM; the extra 64K was "bank switched" into memory as needed in blocks of 16K. The SuperPET, too, had an extra 64K of RAM that was bank switched in 4K blocks; it also came with an additional microprocessor (the 6809) used primarily for implementing high-level languages. Both the 8096 and the SuperPET may be used as conventional CBM 8032 computers; the extra memory may be ignored. VIC-20 ------ The VIC-20 was a new design concept for Commodore. Color, graphics, and sound were built into the computer. The memory architecture changed radically. Zero-page locations were shifted significantly as compared to previous PET/CBM computers. BASIC reverted to Upgrade ROM style--no special disk commands and potentially slow garbage collection. Other than that, BASIC was not trimmed. All the functions and features remained, and some attractive new screen editing features were added, such as automatic repeating keys. The VIC comes with no machine language monitor; it's necessary to load one. The SYS command has a new attractive feature that allows registers A, X, and Y to be "preloaded" by POKEing values into address 780, 781, and 782. Location 783 could also be used to set the status register, but that's dangerous; unless it's done carefully, the decimal mode or interrupt disable flags might be set inadvertently. The VIC-20 is somewhat vexing for machine language programming work. Depending on the amount of extra memory fitted (none, 3K, or 8K and over), the location of start-of-BASIC and of screen memory will vary. Commodore 64 ------------ The Commodore 64 has much in common with the VIC-20. In particular, its zero page organization is almost identical to that of VIC. The Commodore 64 comes with a 6510 microprocessor; addresses 0 and 1 are reserved for "bank switching" of memory. :158: BASIC is identical to that of the VIC--no special disk commands and potentially slow garbage collection. There's no built-in machine language monitor, so one must be loaded. The SYS command, as with the VIC, allows preloading of registers A, X, and Y if desired. The Commodore 64 has a more stable architecture than the VIC. BASIC starts in a consistent place, and the screen is always at hex 0400 unless you move it. There's a bank of memory at $C000 to $CFFF that is not used by the computer system; it's useful for staging machine language coding. The Commodore 64 is the first Commodore machine in which it is sometimes desirable to write totally in machine language, with no BASIC at all. BASIC can be swapped out to release extra RAM, and large applications (word processors, spread sheets, and so on) are likely to do this. Commodore PLUS/4 ---------------- Similar to the Commodore 64 in many ways. The processor is a 7501, which has the same instruction set as the 6502. Screen memory and BASIC RAM have been moved a little higher. BASIC itself is greatly expanded. Color and sound are implemented differently to the Commodore 64. There's a built-in machine language monitor with expanded features, such as assemble and disassemble. This one is convenient for machine language programmers. The memory arrangement is more sophisticated than on previous machines; large implementations may require insight into the machine's detailed architecture. B Series -------- The B-128, B-256, CBM-128, and CBM-256 were designed as successors to the 80-column PET/CBM units. Architecture has been radically changed: the processor is a 6509, memory is bank switched, and zero page is significantly different from that of other models. The cassette buffer is no longer at $0330, so that the examples given in this book will need to be moved to a new part of RAM (addresses $0400 to $07FF are available). Bank switching is more complex than on other models. Beginners will find that there are more things to be kept track of in this machine. If possible, beginners should try to find a simpler computer on which to take their first steps. :159: Implementation of large-scale programs require setting up a "transfer sequence" program to link the program's memory bank to that of the kernal. Usually, a bootstrap program will be needed to set everything up. A machine language monitor is built into this line of machines. A few new commands have been made available: .V to switch banks, .@ to test disk status. :160: :161: Appendix C Memory Maps A word about memory maps: they are always too big or too small for the use you have in mind. The beginner may feel swamped by the wealth of detail. There's no threat, however. The information is there when you're ready for it. Browse through the information; it may be though-provoking. Try reading or changing locations to see what happens. The advanced programmer may want more: lengthy details on how each location is used, which parts of the system use these locations, and so on. Time and space don't permit such detail. The maps are intended to be fairly complete. Those who want more detail may find them cryptic; but at least each location will be associated with a type of activity. Different machines may be compared by checking their respective maps. In some cases, programs may be converted with their use, since they will help to find the corresponding memory location in the target machine. When you see a reference to a POKE or PEEK location--in this book or from other sources--check it in these maps. They will help add perspective. :162: "Original ROM" PET ================== The Great Zero-Page Hunt ------------------------ Most users help themselves to the high part of the input buffer ($0040 to $0059), which is not used except when long lines of data are inputted. Most zero-page locations may be copied to another part of memory so that their original contents can be restored after use. However, the programmer should take great care in modifying the following locations, which are critical within the operating system or BASIC: $03, $05, $64 to $67, $7A to $87, $89, $A2 to $A3, $B7, $C2 to $D9, $E0 to $E2, $F5. Memory Map ---------- Hex Decimal Description ------------------------------------------------------------------- 0000-0002 0-2 USR jump 0003 3 Current I/O-prompt suppress 0005 5 Cursor control position 0008-0009 8-9 Integer value (for SYS, GOTO, and so on) 000A-0059 10-89 Input buffer 005A 90 Search character 005B 91 Scan-between-quotes flag 005C 92 Input buffer pointer; number of subscripts 005D 93 Default DIM flag 005E 94 Type: FF = string; 00 = numeric 005F 95 Type: 80 = integer; 00 = floating point 0060 96 Flag: DATA scan; LIST quote; memory 0061 97 Subscript flag; FNx flag 0062 98 0 = INPUT; $40 = GET; $98 = READ 0063 99 ATN sign; Comparison evaluation flag 0064 100 Input flag (suppress output) 0065-0067 101-103 Pointers for descriptor stack :163: 0068-0070 104-112 Descriptor stack (temporary strings) 0071-0074 113-116 Utility pointer area 0075-0078 117-120 Product area for multiplication 0079 121 [unused? not listed in original text. -wf] 007A-007B 122-123 Pointer: start-of-BASIC 007C-007D 124-125 Pointer: start-of-variables 007E-007F 126-127 Pointer: start-of-arrays 0080-0081 128-129 Pointer: end-of-arrays 0082-0083 130-131 Pointer: string-storage (moving down) 0084-0085 132-133 Utility string pointer 0086-0087 134-135 Pointer: limit-of-memory 0088-0089 136-137 Current BASIC line number 008A-008B 138-139 Previous BASIC line number 008C-008D 140-141 Pointer: BASIC statement for CONT 008E-008F 142-143 Current DATA line number 0090-0091 144-145 Current DATA address 0092-0093 146-147 Input vector 0094-0095 148-149 Current variable name 0096-0097 150-151 Current variable address 0098-0099 152-153 Variable pointer for FOR/NEXT 009A-009B 154-155 Y-save; op-save; BASIC pointer save 009C 156 Comparison symbol accumulator 009D-00A2 157-162 Miscellaneous work area, pointers, and so on 00A3-00A5 163-165 Jump vector for functions 00A6-00AF 166-175 Miscellaneous numeric work area 00B0 176 Accumulator #1: exponent 00B1-00B4 177-180 Accumulator #1: mantissa 00B5 181 Accumulator #1: sign 00B6 182 Series evaluation constant pointer 00B7 183 Accumulator #1: high-order (overflow) 00B8 184 Accumulator #2: exponent 00B9-00BC 185-188 Accumulator #2: mantissa 00BD 189 Accumulator #2: sign 00BE 190 Sign comparison: Accumulator #1 versus #2 00BF 191 Accumulator #1: low-order (rounding) :164: 00C0-00C1 192-193 Cassette buffer length; series pointer 00C2-00D9 194-217 CHRGET subroutine; get BASIC character 00C9-00CA 201-202 BASIC pointer (within above subroutine) 00DA-00DE 218-222 Random number seed 00DF 223 [unused? not listed in original text. -wf] 00E0-00E1 224-225 Pointer to screen line 00E2 226 Position of cursor on above line 00E3-00E4 227-228 Utility pointer: tape, scroll 00E5-00E6 229-230 Tape end address/end of current program 00E7-00E8 231-232 Tape timing constants 00E9 233 Tape buffer character 00EA 234 Direct/programmed cursor: 0 = direct 00EB 235 Tape read timer 1 enabled 00EC 236 EOT received from tape 00ED 237 Read character error 00EE 238 Number of characters in file name 00EF 239 Current file logical address 00F0 240 Current file secondary address 00F1 241 Current file device number 00F2 242 Line margin 00F3-00F4 243-244 Pointer: start of tape buffer 00F5 245 Line where cursor lives 00F6 246 Last key; checksum; miscellaneous 00F7-00F8 247-248 Tape start address 00F9-00FA 249-250 File name pointer 00FB 251 Number of INSERTs outstanding 00FC 252 Write shift word; read character in 00FD 253 Tape blocks remaining to write/read 00FE 254 Serial word buffer 00FF 255 [unused? not listed in original text. -wf] 0100-010A 256-266 STR$ work area 0100-013E 256-318 Tape read error log 0100-01FF 256-511 Processor stack :165: 0200-0202 512-513 Jiffy clock for TI and TI$ 0203 515 Which key down: 255 = no key 0204 516 Shift key: 1 = pressed 0205-0206 517-518 Correction clock 0207-0208 519-520 Cassette statuses, #1 and #2 0209 521 Keyswitch PIA: STOP and RVS flags 020A 522 Timing constant for tape 020B 523 0 = load; 1 = verify 020C 524 Status word ST 020D 525 Number of characters in keyboard buffer 020E 526 Screen reverse flag 020F-0218 527-536 Keyboard input buffer 0219-021A 537-538 IRQ vector 021B-021C 539-540 BRK interrupt vector 021D 541 IEEE output: 255 = character pending 021E 542 End-of-line-for-input pointer 021F 543 [unused? not listed in original text. -wf] 0220-0221 544-545 Cursor log, row and column 0222 546 IEEE output buffer 0223 547 Key image 0224 548 0 = flash cursor 0225 549 Cursor timing countdown 0226 550 Character under cursor 0227 551 Cursor in blink phase 0228 552 EOT received from tape 0229-0241 553-577 Screen line wrap table 0242-024B 578-587 File logical address table 024C-0255 588-597 File device number table 0256-025F 598-607 File secondary address table 0260 608 Input from screen/keyboard 0261 609 X save 0262 610 How many open files 0263 611 Input device, normally 0 0264 612 Output CMD device, normally 3 0265 613 Tape character parity 0266 614 Byte-received flag 0267 615 [unused? not listed in original text. -wf] 0268-0269 616-617 File name pointer; counter :166: 026A-026B 618-619 [unused? not listed in original text. -wf] 026C 620 Serial bit count 026D-026E 621-622 [unused? not listed in original text. -wf] 026F 623 Cycle counter 0270 624 Tape writer countdown 0271-0272 625-626 Tape buffer pointers, #1 and #2 0273 627 Write leader count; read pass 1/2 0274 628 Write new byte; read error flag 0275 629 Write start bit; read bit error sequence 0276-0277 630-631 Error log pointers, pass 1/2 0278 632 0 = scan; 1-15 = count; $40 = load; $80 = end 0279 633 Write leader length; read checksum 027A-0339 634-825 Tape #1 input buffer 033A-03F9 826-1017 Tape #2 input buffer 03FA-03FB 1018-1019 Monitor extension vector 03FC-03FF 1020-1023 [unused? not listed in original text. -wf] 0400-7FFF 1024-32767 Available RAM including expansion 8000-83E7 32768-33767 Screen RAM C000-E7F8 49152-59384 BASIC ROM; part of kernal ROM E810-E813 59408-59411 PIA 1 (6520): keyboard interface E820-E823 59424-59427 PIA 2 (6520): IEEE interface E840-E84F 59456-59471 VIA (6522): miscellaneous interface, timers F000-FFFF 61440-65535 Kernal ROM routines PIA and VIA charts are the same as shown for Upgrade/4.0 units. Upgrade ROM and BASIC 4.0 Systems ================================= The Great Zero-Page Hunt ------------------------ Zero-page locations are tough to find in these areas. Locations $1F to $27, $4B to $50, and $54 to $5D are work areas available for temporary use. If tape is not being read or written, addresses $B1 to $C3 are available. :167: Most zero-page locations may be copied to another part of memory so that their original contents can be restored after use. The programmer should take great care, however, in modifying the following locations, which are critical within the operating system or BASIC: $10, $13 to $15, $28 to $35, $37, $50 to $51, $65, $70 to $87, $8D to $B0, $C4 to $FA. Memory Map ---------- Where Upgrade ROM differs from 4.0, an asterisk (*) is shown and the 4.0 value is given. There are some differences in usage between the 40- and 80-column machines. Hex Decimal Description ------------------------------------------------------------------- 0000-0002 0-2 USR jump 0003 3 Search character 0004 4 Scan-between-quotes flag 0005 5 Input buffer pointer; number of subscripts 0006 6 Default DIM flag 0007 7 Type: FF = string; 00 = numeric 0008 8 Type: 80 = integer; 00 = floating point 0009 9 Flag: DATA scan; LIST quote; memory 000A 10 Subscript flag; FNx flag 000B 11 0 = INPUT; $40 = GET; $98 = READ 000C 12 ATN sign; Comparison evaluation flag 000D-000F 13-15 * Disk status DS$ descriptor 0010 16 * Current I/O device for prompt-suppress 0011-0012 17-18 Integer value (for SYS, GOTO, and so on) 0013-0015 19-21 Pointers for descriptor stack 0016-001E 22-30 Descriptor stack (temporary strings) 001F-0022 31-34 Utility pointer area 0023-0027 35-39 Product area for multiplication 0028-0029 40-41 Pointer: start-of-BASIC 002A-002B 42-43 Pointer: start-of-variables 002C-002D 44-45 Pointer: start-of-arrays :168: 002E-002F 46-47 Pointer: end-of-arrays 0030-0031 48-49 Pointer: string-storage (moving down) 0032-0033 50-51 Utility string pointer 0034-0035 52-53 Pointer: limit-of-memory 0036-0037 54-55 Current BASIC line number 0038-0039 56-57 Previous BASIC line number 003A-003B 58-59 Pointer: BASIC statement for CONT 003C-003D 60-61 Current DATA line number 003E-003F 62-63 Current DATA address 0040-0041 64-65 Input vector 0042-0043 66-67 Current variable name 0044-0045 68-69 [unused? not listed in original text. -wf] 0046-0047 70-71 Variable pointer for FOR/NEXT 0048-0049 72-73 Y-save; op-save; BASIC pointer save 004A 74 Comparison symbol accumulator 004B-0050 75-80 Miscellaneous work area, pointers, and so on 0051-0053 81-83 Jump vector for functions 0054-005D 84-93 Miscellaneous numeric work area 005E 94 Accumulator #1: exponent 005F-0062 95-98 Accumulator #1: mantissa 0063 99 Accumulator #1: sign 0064 100 Series evaluation constant pointer 0065 101 Accumulator #1: high-order (overflow) 0066 102 Accumulator #2: exponent 0067-006A 103-106 Accumulator #2: mantissa 006B 107 Accumulator #2: sign 006C 108 Sign comparison: Accumulator #1 versus #2 006D 109 Accumulator #1: low-order (rounding) 006E-006F 110-111 Cassette buffer length; series pointer 0070-0087 112-135 CHRGET subroutine; get BASIC character 0077-0078 119-120 BASIC pointer (within above subroutine) 0088-008C 136-140 Random number seed 008D-008F 141-143 Jiffy clock for TI and TI$ :169: 0090-0091 144-145 IRQ vector 0092-0093 146-147 BRK interrupt vector 0094-0095 148-149 NMI interrupt vector 0096 150 Status word ST 0097 151 Which key down: 255 = no key 0098 152 Shift key: 1 = pressed 0099-009A 153-154 Correction clock 009B 155 Keyswitch PIA: STOP and RVS flags 009C 156 Timing constant for tape 009D 157 0 = load; 1 = verify 009E 158 Number of characters in keyboard buffer 009F 159 Screen reverse flag 00A0 160 IEEE output: 255 = character pending 00A1 161 End-of-line-for-input pointer 00A2 162 [unused? not listed in original text. -wf] 00A3-00A4 163-164 Cursor log, row and column 00A5 165 IEEE output buffer 00A6 166 Key image 00A7 167 0 = flash cursor 00A8 168 Cursor timing countdown 00A9 169 Character under cursor 00AA 170 Cursor in blink phase 00AB 171 EOT received from tape 00AC 172 Input from screen/keyboard 00AD 173 X save 00AE 174 How many files open 00AF 175 Input device, normally 0 00B0 176 Output CMD device, normally 3 00B1 177 Tape character parity 00B2 178 Byte-received flag 00B3 179 Logical address temporary save 00B4 180 Tape buffer character; MLM command 00B5 181 File name pointer; MLM flag; counter 00B6 182 [unused? not listed in original text. -wf] 00B7 183 Serial bit count 00B8 184 [unused? not listed in original text. -wf] 00B9 185 Cycle counter 00BA 186 Tape writer countdown :170: 00BB-00BC 187-188 Tape buffer pointers, #1 and #2 00BD 189 Write leader count; read pass 1/2 00BE 190 Write new byte; read error flag 00BF 191 Write start bit; read bit sequence error 00C0-00C1 192-193 Error log pointers, pass 1/2 00C2 194 0 = scan; 1-25 = count; $40 = load; $80 = end 00C3 195 Write leader length; read checksum 00C4-00C5 196-197 Pointer to screen line 00C6 198 Position of cursor on above line 00C7-00C8 199-200 Utility pointer: tape, scroll 00C9-00CA 201-202 Tape end address; end of current program 00CB-00CC 203-204 Tape timing constants 00CD 205 0 = direct cursor; else programmed 00CE 206 Tape read timer 1 enabled 00CF 207 EOT received from tape 00D0 208 Read character error 00D1 209 Number of characters in file name 00D2 210 Current file logical address 00D3 211 Current file secondary address 00D4 212 Current file device number 00D5 213 Right-hand window or line margin 00D6-00D7 214-215 Pointer: start of tape buffer 00D8 216 Line where cursor lives 00D9 217 Last key; checksum; miscellaneous 00DA-00DB 218-219 File name pointer 00DC 220 Number of INSERTs outstanding 00DD 221 Write shift word; read character in 00DE 222 Tape blocks remaining to write/read :171: 00DF 223 Serial word buffer 00E0-00F8 224-248 (40-column) Screen line wrap table 00E0-00E1 224-225 * (80-column) Top, bottom of window 00E2 226 * (80-column) Left window margin 00E3 227 * (80-column) Limit of keyboard buffer 00E4 228 * (80-column) Key repeat flag 00E5 229 * (80-column) Repeat countdown 00E6 230 * (80-column) New key marker 00E7 231 * (80-column) Chime time 00E8 232 * (80-column) HOME count 00E9-00EA 233-234 * (80-column) Input vector 00EB-00EC 235-236 * (80-column) Output vector 00ED-00F8 237-248 * (80-column) [unused? -wf] 00F9-00FA 249-250 Cassette statuses, #1 and #2 00FB-00FC 251-252 MLM pointer; tape start address 00FD-00FE 253-254 MLM; DOS pointer; miscellaneous 00FF 255 [unused? not listed in original text. -wf] 0100-010A 256-266 STR$ work area; MLM work area 0100-013E 256-318 Tape read error log 0100-01FF 256-511 Processor stack 0200-0250 512-592 MLM work area; input buffer 0251-025A 593-602 File logical address table 025B-0264 603-612 File device number table 0265-026E 613-622 File secondary address table 026F-0278 623-632 Keyboard input buffer 027A-0339 634-825 Tape #1 input buffer 033A-03F9 826-1017 Tape #2 input buffer 033A-0380 826-896 * DOS work area 03E9 1001 (Fat 40) New key marker 03EA 1002 (Fat 40) Key repeat countdown 03EB 1003 (Fat 40) Keyboard buffer limit 03EC 1004 (Fat 40) Chime time 03ED 1005 (Fat 40) Decisecond timer 03EE 1006 (Fat 40) Key repeat flag 03EE-03F7 1006-1015 (80-column) Tab stop table 03EF 1007 (Fat 40) Tab work value 03F0-03F9 1008-1017 (Fat 40) Tab stops 03FA-03FB 1018-1019 MLM extension vector :172: 03FC 1020 * IEEE timeout default 03FD-03FF 1021-1023 [unused? not listed in original text. -wf] 0400-7FFF 1024-32767 Available RAM including expansion 8000-83E7 32768-33767 (40-column) Video RAM 8000-87CF 32768-34767 * (80-column) Video RAM 9000-AFFF 36864-45055 Available ROM expansion area B000-E7FF 45056-59391 BASIC ROM, part of kernal E810-E813 59408-59411 PIA 1: keyboard I/O E820-E823 59424-59427 PIA 2: IEEE-488 I/O E840-E84F 59456-59471 VIA: I/O and timers E880-E881 59520-59521 (80-column and Fat 40) CRT controller F000-FFFF 61440-65535 Kernal ROM [as printed in the book, it's pretty difficult to see exactly which bits the fields take, so this may be off by a bit or two. :( -wf ] 6520 +-------+-------+-------+-------+-------+-------+-------+-------+ | Diag | | Tape Switch | | $E810 |Sense/ |EOI In | Sense | Keyboard Row Select | 59408 |Uncrash| | #1 #2 | | +-------+-------+-------+-------+-------+-------+-------+-------+ |Tape #1| | (Screen Blank-- | DDRA | Tape #1 Input | $E811 | In | | Original ROM) |Access | L Control | 59409 | Latch | | EOI Out | | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | $E812 | Keyboard Input for Selected Row | 59410 | | +-------+-------+-------+-------+-------+-------+-------+-------+ |Retrace| | Cassette #1 Motor | DDRB | Retrace | $E813 | Latch | | Output |Access | Interrupt | 59411 | | | | | Control | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.1 PIA 1 Chart 6520 +-------+-------+-------+-------+-------+-------+-------+-------+ | | $E820 | IEEE-488 Input | 59424 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | /ATN | | | DDRA | /ATN Int | $E821 | Int | | /NDAC Out |Access | Control | 59425 | | | | | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | $E822 | IEEE-488 Output | 59426 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | /SRQ | | Cassette #1 Motor | DDRB | /SRQ Int | $E823 | Int | | Output |Access | Control | 59427 | | | | | | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.2 PIA 2 Chart :173: 6522 +-------+-------+-------+-------+-------+-------+-------+-------+ $E840 | /DAV | /NRFD |Retrace|Tape #2| Tape | /ATN | NRFD | /NDAC | 59456 | In | In | In | Motor |Output | Out | Out | In | +-------+-------+-------+-------+-------+-------+-------+-------+ $E841 | Unused (See E84F) | 59457 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E842 | Data Direction Register B (for E840) | 59458 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E843 | Data Direction Register A (for E84F) | 59459 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E844 | | 59460 | | +- - - - - - - - - - - - - - Timer 1 - - - - - - - - - - - - - -+ $E845 | | 59461 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E846 | | 59462 | | +- - - - - - - - - - - - Timer 1 Latch - - - - - - - - - - - -+ $E847 | | 59463 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E848 | | 59464 | | +- - - - - - - - - - - - - - Timer 2 - - - - - - - - - - - - - -+ $E849 | | 59465 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E84A | Shift Register (unused) | 59466 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $E84B | T1 Control | T2 | Shift Register |Latch Controls | 59467 | |Control| Control | PB PA | +-------+-------+-------+-------+-------+-------+-------+-------+ $E84C | CB2 (PUP) Control |CB1 Ctl| CA2 Control |CA1 PUP| 59468 | |Tape #2| Graphics/Text Mode |Control| +-------+-------+-------+-------+-------+-------+-------+-------+ $E84D | IRQ |Timer 1|Timer 2|CB1 Int|CB2 Int| SR |CA1 Int|CA2 G/T| 59469 |Status:| Int | Int |Tape #2| (PUP) |Unused | (PUP) |Unused | +-------+-------+-------+-------+-------+-------+-------+-------+ $E84E | IRQ |Timer 1|Timer 2|CB1 Int|CB2 Int| SR |CA1 Int|CA2 G/T| 59470 |Enable:| Int | Int |Tape #2| (PUP) |Unused | (PUP) |Unused | +-------+-------+-------+-------+-------+-------+-------+-------+ $E84F | Parallel User Port Data Register PA | 59471 | | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.3 VIA Chart CBM 8032 and FAT-40 6545 CRT Controller --------------------------------------- Typical Values (decimal) $E880 $E881 Text Graphics +------+-------------------------------+ | 0 | Horizontal Total | 49 49 +------+-------------------------------+ | 1 |Horizontal Characters Displayed| 40 40 +------+-------------------------------+ | 2 | Horizontal Sync Position | 41 41 +------+---+---+---+---+---+---+---+---+ | 3 | V Sync Width | H Sync Width | 15 15 +------+---+---+---+---+---+---+---+---+ | 4 |///| Vertical Total | 32 40 +------+---+---+---+---+---+---+---+---+ | 5 |///////////|V Total Adjustment | 3 5 +------+---+---+---+---+---+---+---+---+ | 6 |///| Vertical Displayed | 25 25 +------+---+---+---+---+---+---+---+---+ | 7 |///| Vertical Sync Position | 29 33 +------+---+---+---+---+---+---+---+---+ | 8 |///////| Mode | 0 0 +------+---+---+---+---+---+---+---+---+ | 9 | Scan Lines | 9 7 +------+-------------------------------+ | 10 | | 0 0 +------+- - Cursor Start (unused) - -+ | 11 | | 0 0 +------+---+---+---+---+---+---+---+---+ | 12 |///////| C | R | Display | 16 16 +------+---+---+---+---+- - - -+ | 13 | Address | 0 0 +------+-------------------------------+ NOTES: 1. Registers are write-only. 2. Avoid extreme changes in Register 0. CRT damage could result. 3. Register 0 will adjust scan to allow interfacing to an external monitor. 4. Register 12, Bit 4, will "invert" the video signal. 5. Register 12, Bit 5, switches to an alternate character set. The character set is not implemented on most machines except SuperPET. Figure C.4 :175: VIC-20 ====== The Great Zero-Page Hunt ------------------------ Locations $FC to $FF are available. Locations $22 to $2A, $4E to $53, and $57 to $60 are work areas available for temporary use. Most zero-page locations may be copied to another part of memory so that their original contents can be restored after use. However, the programmer should take great care in modifying the following locations, which are critical within the operating system or BASIC: $13, $16 to $18, $2B to $38, $3A, $53 to $54, $68, $73 to $8A, $90 to $9A, $A0 to $A2, $B8 to $BA, $C5 to $F4. :176: Memory Map ---------- Hex Decimal Description ------------------------------------------------------------------- 0000-0002 0-2 USR jump 0003-0004 3-4 Float-fixed vector 0005-0006 5-6 Fixed-float vector 0007 7 Search character 0008 8 Scan-quotes flag 0009 9 TAB column save 000A 10 0 = LOAD; 1 = VERIFY 000B 11 Input buffer pointer; number of subscripts 000C 12 Default DIM flag 000D 13 Type: FF = string; 00 = numeric 000E 14 Type: 80 = integer; 00 = floating point 000F 15 DATA scan; LIST quote; memory flag 0010 16 Subscript/FNx flag 0011 17 0 = INPUT; $40 = GET; $98 = READ 0012 18 ATN sign; Comparison evaluation flag 0013 19 Current I/O prompt flag 0014-0015 20-21 Integer value 0016 22 Pointer: temporary string stack 0017-0018 23-24 Last temporary string vector 0019-0021 25-33 Stack for temporary strings 0022-0025 34-37 Utility pointer area 0026-002A 38-42 Product area for multiplication 002B-002C 43-44 Pointer: start-of-BASIC 002D-002E 45-46 Pointer: start-of-variables 002F-0030 47-48 Pointer: start-of-arrays 0031-0032 49-50 Pointer: end-of-arrays 0033-0034 51-52 Pointer: string-storage (moving down) 0035-0036 53-54 Utility string pointer 0037-0038 55-56 Pointer: limit-of-memory 0039-003A 57-58 Current BASIC line number 003B-003C 59-60 Previous BASIC line number 003D-003E 61-62 Pointer: BASIC statement for CONT :177: 003F-0040 63-64 Current DATA line number 0041-0042 65-66 Current DATA address 0043-0044 67-68 Input vector 0045-0046 69-70 Current variable name 0047-0048 71-72 Current variable address 0049-004A 73-74 Variable pointer for FOR/NEXT 004B-004C 75-76 Y-save; op-save; BASIC pointer save 004D 77 Comparison symbol accumulator 004E-0053 78-83 Miscellaneous work area, pointers, and so on 0054-0056 84-86 Jump vector for functions 0057-0060 87-96 Miscellaneous work area 0061 97 Accumulator #1: exponent 0062-0065 98-101 Accumulator #1: mantissa 0066 102 Accumulator #1: sign 0067 103 Series evaluation constant pointer 0068 104 Accumulator #1: high-order (overflow) 0069 105 Accumulator #2: exponent 006A-006D 106-109 Accumulator #2: mantissa 006E 110 Accumulator #2: sign 006F 111 Sign comparison: Accumulator #1 vs #2 0070 112 Accumulator #1: low-order (rounding) 0071-0072 113-114 Cassette buffer length; series pointer 0073-008A 115-138 CHRGET subroutine; get BASIC character 007A-007B 122-123 BASIC pointer (within above subroutine) 008B-008F 139-143 RND seed value 0090 144 Status word ST 0091 145 Keyswitch PIA: STOP and RVS flags 0092 146 Timing constant for tape 0093 147 Load = 0; verify = 1 0094 148 Serial output: deferred character flag 0095 149 Serial deferred character 0096 150 Tape EOT received :178: 0097 151 Register save 0098 152 How many open files 0099 153 Input device, normally 0 009A 154 Output CMD device, normally 3 009B 155 Tape character parity 009C 156 Byte-received flag 009D 157 Direct = $80; RUN = 0 output control 009E 158 Tape pass 1 error log/character buffer 009F 159 Tape pass 2 error log corrected 00A0-00A2 160-162 Jiffy clock HML 00A3 163 Serial bit count; EOI flag 00A4 164 Cycle count 00A5 165 Countdown, tape write/bit count 00A6 166 Tape buffer pointer 00A7 167 Tape write leader count; read pass; inbit 00A8 168 Tape write new byte; read error; inbit count 00A9 169 Write start bit; read bit error; stbit 00AA 170 Tape scan; Cnt; Load; End/byte assembly 00AB 171 Write lead length; read checksum; parity 00AC-00AD 172-173 Pointer: tape buffer, scrolling 00AE-00AF 174-175 Tape end address; end of program 00B0-00B1 176-177 Tape timing constants 00B2-00B3 178-179 Pointer: start of tape buffer 00B4 180 1 = tape timer enabled; bit count 00B5 181 Tape EOT; RS232 next bit to send 00B6 182 Read character error; outbyte buffer 00B7 183 Number of characters in filename 00B8 184 Current logical file 00B9 185 Current secondary address 00BA 186 Current device 00BB-00BC 187-188 Pointer to filename :179: 00BD 189 Write shift word; read input char 00BE 190 Number of blocks remaining to write/read 00BF 191 Serial word buffer 00C0 192 Tape motor interlock 00C1-00C2 193-194 I/O start address 00C3-00C4 195-196 Kernal setup pointer 00C5 197 Last key pressed 00C6 198 Number of characters in keyboard buffer 00C7 199 Screen reverse flag 00C8 200 End-of-line for input pointer 00C9-00CA 201-202 Input cursor log, row and column 00CB 203 Which key pressed: 64 if no key 00CC 204 0 = flash cursor 00CD 205 Cursor timing countdown 00CE 206 Character under cursor 00CF 207 Cursor in blink phase 00D0 208 Input from screen/keyboard 00D1-00D2 209-210 Pointer to screen line 00D3 211 Position of cursor on above line 00D4 212 0 = direct cursor; else programmed 00D5 213 Current screen line length 00D6 214 Row where cursor lives 00D7 215 Last inkey; checksum; buffer 00D8 216 Number of INSERTs outstanding 00D9-00F0 217-240 Screen line link table 00F1 241 Dummy screen link 00F2 242 Screen row marker 00F3-00F4 243-244 Screen color pointer 00F5-00F6 245-246 Keyboard pointer 00F7-00F8 247-248 RS-232 Rcv pointer 00F9-00FA 249-250 RS-232 Tx pointer 00FB-00FE 251-254 [unused; not listed in original text. -wf] 00FF-010A 255-266 Floating to ASCII work area 0100-013E 256-318 Tape error log 0100-01FF 256-511 Processor stack area 0200-0258 512-600 BASIC input buffer :180: 0259-0262 601-610 Logical file table 0263-026C 611-620 Device number table 026D-0276 621-630 Secondary address table 0277-0280 631-640 Keyboard buffer 0281-0282 641-642 Start of BASIC memory 0283-0284 643-644 Top of BASIC memory 0285 645 Serial bus timeout flag 0286 646 Current color code 0287 647 Color under cursor 0288 648 Screen memory page 0289 649 Maximum size of keyboard buffer 028A 650 Repeat all keys 028B 651 Repeat speed counter 028C 652 Repeat delay counter 028D 653 Keyboard shift/control flag 028E 654 Last shift pattern 028F-0290 655-656 Keyboard table setup pointer 0291 657 Keymode (Katakana) 0292 658 0 = scroll enable 0293 659 RS-232 control register 0294 660 RS-232 command register 0295-0296 661-662 Bit timing 0297 663 RS-232 status 0298 664 Number of bits to send 0299-029A 665-666 RS-232 speed code 029B 667 RS-232 receive pointer 029C 668 RS-232 input pointer 029D 669 RS-232 transmit pointer 029E 670 RS-232 output pointer 029F-02A0 671-672 IRQ save during tape I/O 02A1-02FF 673-767 [unused? not listed in original text. -wf] 0300-0301 768-769 Error message link 0302-0303 770-771 BASIC warm start link 0304-0305 772-773 Crunch BASIC tokens link 0306-0307 774-775 Print tokens link 0308-0309 776-777 Start new BASIC code link 030A-030B 778-779 Get arithmetic element link 030C 780 SYS A-reg save 030D 781 SYS X-reg save 030E 782 SYS Y-reg save 030F 783 SYS status reg save :181: 0310-0313 784-787 [unused? not listed in original text. -wf] 0314-0315 788-789 IRQ vector (default EABF) 0316-0317 790-791 BRK vector (default FED2) 0318-0319 792-793 NMI vector (default FEAD) 031A-031B 794-795 OPEN vector (default F40A) 031C-031D 796-797 CLOSE vector (default F34A) 031E-031F 798-799 Set-input vector (default F2C7) 0320-0321 800-801 Set-output vector (default F309) 0322-0323 802-803 Restore I/O vector (default F3F3) 0324-0325 804-805 Input vector (default F20E) 0326-0327 806-807 Output vector (default F27A) 0328-0329 808-809 Test STOP vector (default F770) 032A-032B 810-811 GET vector (default F1F5) 032C-032D 812-813 Abort I/O vector (default F3EF) 032E-032F 814-815 USR vector (FED2) 0330-0331 816-817 LOAD link 0332-0333 818-819 SAVE link 0334-033B 820-827 [unused? not listed in original text. -wf] 033C-03FB 828-1019 Cassette buffer 03FC-03FF 1020-1023 [unused? not listed in original text. -wf] 0400-0FFF 1024-4095 3K RAM expansion area 1000-1FFF 4096-8191 Normal BASIC memory 2000-7FFF 8192-32767 Memory expansion area 8000-8FFF 32768-36863 Character bitmaps (ROM) 9000-900F 36864-36879 Video interface chip (6560) 9110-911F 37136-37151 VIA (6522) interface-NMI 9120-912F 37152-37167 VIA (6522) interface-IRQ 9400-95FF 37888-38399 Alternate color nybble area 9600-97FF 38400-38911 Main color nybble area A000-BFFF 40960-49151 Plug-in ROM area C000-FFFF 49152-65535 ROM: BASIC and operating system :182: VIC 6560 Chip ------------- +-------+-------+-------+-------+-------+-------+-------+-------+ $9000 |Inter- | Left Margin (=5) | 36864 | lace | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9001 | Top Margin (=25) | 36865 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9002 |Scrn Ad| # Columns (=22) | 36866 | Bit 9 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9003 | | # Rows (=23) |Double | 36867 | Bit 0 | | Char | +- - - -+-------+-------+-------+-------+-------+-------+-------+ $9004 | Input Raster Value | 36868 | Bits 8-1 | +-------+-------+-------+-------+-------+-------+-------+-------+ $9005 | Screen Address | Character Address | 36869 | Bits 13-10 | Bits 13-10 | +-------+-------+-------+-------+-------+-------+-------+-------+ $9006 | Light Pen | 36870 | Horizontal | +-------+-------+-------+-------+-------+-------+-------+-------+ $9007 | Light Pen | 36871 | Vertical | +-------+-------+-------+-------+-------+-------+-------+-------+ $9008 | Paddle Input X | 36872 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9009 | Paddle Input Y | 36873 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $900A |ON/OFF | Voice 1 Frequency | 36874 | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $900B |ON/OFF | Voice 2 Frequency | 36875 | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $900C |ON/OFF | Voice 3 Frequency | 36876 | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $900D |ON/OFF | Noise Frequency | 36877 | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $900E | Multicolor | Sound | 36878 | Mode (=0) | Amplitude | +-------+-------+-------+-------+-------+-------+-------+-------+ $900F | Screen Background |Foregd/| Frame | 36879 | Color |Backgd | Color | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.6 :183: VIC-20 6522 Usage ----------------- +-------+-------+-------+-------+-------+-------+-------+-------+ $9110 |DSR In |CTS In | |DCD* In|RI* In |DTR Out|RTS Out|Data In| 37136 | RS-232 Interface, or Parallel User Port | +-------+-------+-------+-------+-------+-------+-------+-------+ $9111 | Unused (See $911F) | 37137 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9112 | Data Direction Register B (for $9110) | 37138 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9113 | Data Direction Register A (for $911F) | 37139 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9114 | | 37140 | Timer 1: | +- - - - - - - - - - - -+ $9115 | RS-232 Send Speed, or Tape Write Timing | 37141 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9116 | | 37142 | | +- - - - - - - - - - - - Timer 1 Latch - - - - - - - - - - - -+ $9117 | | 37143 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9118 | | 37144 | Timer 2: | +- - - - - - - - - - - - - - - - - - - - - -+ $9119 | RS-232 Input Timing | 37145 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $911A | Shift Register (*unused) | 37146 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $911B | T1 Control | T2 | Shift Register |Latch Controls | 37147 | |Control| Control | PB PA | +-------+-------+-------+-------+-------+-------+-------+-------+ $911C | CB2: RS-232 Send |CB1 Ctl| CA2 Control |CA1 Ctl| 37148 | |RS232In| Tape Motor Control |RESTORE| +-------+-------+-------+-------+-------+-------+-------+-------+ $911D | NMI |Timer 1|Timer 2|CB1 Int| | |CA1 Int| | 37149 |Status:| Int | Int |RS232In| | |RESTORE| | +-------+-------+-------+-------+-------+-------+-------+-------+ $911E | NMI |Timer 1|Timer 2|CB1 Int| | SR |CA1 Int| | 37150 |Enable:| Int | Int |RS232In| |Unused |RESTORE| | +-------+-------+-------+-------+-------+-------+-------+-------+ $911F | ATN | Tape | Joysticks |Serial |Serial | 37151 | Out | Sense |Button | Left | Down | Up |Data In|Clk In | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.7 :184: VIC-20 6522 Usage ----------------- +-------+-------+-------+-------+-------+-------+-------+-------+ $9120 |Joy Sel| | | |TapeOut| | | | 37152 | Keyboard Row Select | +-------+-------+-------+-------+-------+-------+-------+-------+ $9121 | Keyboard Column Input | 37153 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9122 | Data Direction Register B (for $9120) | 37154 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9123 | Data Direction Register A (for $9121) | 37155 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9124 | | 37156 | Timer 1: | +- - - - - - - -+ $9125 | Cassette Tape Read or Keyboard & Clock Interrupt | 37157 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9126 | | 37158 | | +- - - - - - - - - - - - Timer 1 Latch - - - - - - - - - - - -+ $9127 | | 37159 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $9128 | | 37160 | Timer 2: | +- - - - - - - - - - - - - - - - - -+ $9129 | Serial Bus or Tape R/W Timing | 37161 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $912A | Shift Register (*unused) | 37162 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $912B | T1 Control | T2 | Shift Register |Latch Controls | 37163 | |Control| Control | PB PA | +-------+-------+-------+-------+-------+-------+-------+-------+ $912C | CB2: Serial Bus |CB1 Ctl| CA2 Control |CA1 Ctl| 37164 | Data Out |SRQ In | Serial Clock Out |Tape In| +-------+-------+-------+-------+-------+-------+-------+-------+ $912D | IRQ |Timer 1|Timer 2|CB1 Int| | |CA1 Int| | 37165 |Status:| Int | Int |SRQ In | | |Tape In| | +-------+-------+-------+-------+-------+-------+-------+-------+ $912E | IRQ |Timer 1|Timer 2|CB1 Int| | |CA1 Int| | 37166 |Enable:| Int | Int |SRQ In | | |Tape In| | +-------+-------+-------+-------+-------+-------+-------+-------+ $912F | *Unused: see $9121 | 37167 | | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.8 :185: Commodore 64 ============ The Great Zero-Page Hunt ------------------------ Locations $FC to $FF are available. Locations $22 to $2A, $4E to $53, and $57 to $60 are work areas available for temporary use. Most zero-page locations may be copied to another part of memory so that their original contents can be restored after use. The programmer should take great care, however, in modifying the following locations, which are critical within the operating system or BASIC: $13, $16 to $18, $2B to $38, $3A, $53 to $54, $68, $73 to $8A, $90 to $9A, $A0 to $A2, $B8 to $BA, $C5 to $F4. Memory Map ---------- Hex Decimal Description ------------------------------------------------------------------- 0000 0 Chip directional register 0001 1 Chip I/O; memory and tape control 0002 2 [unused; not listed in original text. -wf] 0003-0004 3-4 Float-fixed vector 0005-0006 5-6 Fixed-float vector 0007 7 Search character 0008 8 Scan-quotes flag 0009 9 TAB column save 000A 10 0 = LOAD; 1 = VERIFY 000B 11 Input buffer pointer; number of subscripts 000C 12 Default DIM flag 000D 13 Type: FF = string; 00 = numeric 000E 14 Type: 80 = integer; 00 = floating point 000F 15 DATA scan; LIST quote; memory flag 0010 16 Subscript/FNx flag 0011 17 0 = INPUT; $40 = GET; $98 = READ 0012 18 ATN sign; Comparison evaluation flag 0013 19 Current I/O prompt flag 0014-0015 20-21 Integer value :186: 0016 22 Pointer: temporary string stack 0017-0018 23-24 Last temporary string vector 0019-0021 25-33 Stack for temporary strings 0022-0025 34-37 Utility pointer area 0026-002A 38-42 Product area for multiplication 002B-002C 43-44 Pointer: start-of-BASIC 002D-002E 45-46 Pointer: start-of-variables 002F-0030 47-48 Pointer: start-of-arrays 0031-0032 49-50 Pointer: end-of-arrays 0033-0034 51-52 Pointer: string-storage (moving down) 0035-0036 53-54 Utility string pointer 0037-0038 55-56 Pointer: limit-of-memory 0039-003A 57-58 Current BASIC line number 003B-003C 59-60 Previous BASIC line number 003D-003E 61-62 Pointer: BASIC statement for CONT 003F-0040 63-64 Current DATA line number 0041-0042 65-66 Current DATA address 0043-0044 67-68 Input vector 0045-0046 69-70 Current variable name 0047-0048 71-72 Current variable address 0049-004A 73-74 Variable pointer for FOR/NEXT 004B-004C 75-76 Y-save; op-save; BASIC pointer save 004D 77 Comparison symbol accumulator 004E-0053 78-83 Miscellaneous work area, pointers, and so on 0054-0056 84-86 Jump vector for functions 0057-0060 87-96 Miscellaneous work area 0061 97 Accumulator #1: exponent 0062-0065 98-101 Accumulator #1: mantissa 0066 102 Accumulator #1: sign 0067 103 Series evaluation constant pointer 0068 104 Accumulator #1: high-order (overflow) 0069 105 Accumulator #2: exponent 006A-006D 106-109 Accumulator #2: mantissa 006E 110 Accumulator #2: sign 006F 111 Sign comparison: Accumulator #1 vs #2 0070 112 Accumulator #1: low-order (rounding) :187: 0071-0072 113-114 Cassette buffer length; series pointer 0073-008A 115-138 CHRGET subroutine; get BASIC character 007A-007B 122-123 BASIC pointer (within above subroutine) 008B-008F 139-143 RND seed value 0090 144 Status word ST 0091 145 Keyswitch PIA: STOP and RVS flags 0092 146 Timing constant for tape 0093 147 Load = 0; verify = 1 0094 148 Serial output: deferred character flag 0095 149 Serial deferred character 0096 150 Tape EOT received 0097 151 Register save 0098 152 How many open files 0099 153 Input device, normally 0 009A 154 Output CMD device, normally 3 009B 155 Tape character parity 009C 156 Byte-received flag 009D 157 Direct = $80; RUN = 0 output control 009E 158 Tape pass 1 error log/character buffer 009F 159 Tape pass 2 error log corrected 00A0-00A2 160-162 Jiffy clock HML 00A3 163 Serial bit count; EOI flag 00A4 164 Cycle count 00A5 165 Countdown, tape write/bit count 00A6 166 Tape buffer pointer 00A7 167 Tape write leader count; read pass; inbit 00A8 168 Tape write new byte; read error; inbit count 00A9 169 Write start bit; read bit error; stbit 00AA 170 Tape scan; Cnt; Load; End/byte assembly 00AB 171 Write lead length; read checksum; parity 00AC-00AD 172-173 Pointer: tape buffer, scrolling :188: 00AE-00AF 174-175 Tape end address; end of program 00B0-00B1 176-177 Tape timing constants 00B2-00B3 178-179 Pointer: start of tape buffer 00B4 180 1 = tape timer enabled; bit count 00B5 181 Tape EOT; RS232 next bit to send 00B6 182 Read character error; outbyte buffer 00B7 183 Number of characters in filename 00B8 184 Current logical file 00B9 185 Current secondary address 00BA 186 Current device 00BB-00BC 187-188 Pointer to filename 00BD 189 Write shift word; read input char 00BE 190 Number of blocks remaining to write/read 00BF 191 Serial word buffer 00C0 192 Tape motor interlock 00C1-00C2 193-194 I/O start address 00C3-00C4 195-196 Kernal setup pointer 00C5 197 Last key pressed 00C6 198 Number of characters in keyboard buffer 00C7 199 Screen reverse flag 00C8 200 End-of-line for input pointer 00C9-00CA 201-202 Input cursor log, row and column 00CB 203 Which key pressed: 64 if no key 00CC 204 0 = flash cursor 00CD 205 Cursor timing countdown 00CE 206 Character under cursor 00CF 207 Cursor in blink phase 00D0 208 Input from screen/keyboard 00D1-00D2 209-210 Pointer to screen line 00D3 211 Position of cursor on above line 00D4 212 0 = direct cursor; else programmed 00D5 213 Current screen line length :189: 00D6 214 Row where cursor lives 00D7 215 Last inkey; checksum; buffer 00D8 216 Number of INSERTs outstanding 00D9-00F2 217-242 Screen line link table 00F3-00F4 243-244 Screen color pointer 00F5-00F6 245-246 Keyboard pointer 00F7-00F8 247-248 RS-232 Rcv pointer 00F9-00FA 249-250 RS-232 Tx pointer 00FB-00FE 251-254 [unused; not listed in original text. -wf] 00FF-010A 255-266 Floating to ASCII work area 0100-013E 256-318 Tape error log 0100-01FF 256-511 Processor stack area 0200-0258 512-600 BASIC input buffer 0259-0262 601-610 Logical file table 0263-026C 611-620 Device number table 026D-0276 621-630 Secondary address table 0277-0280 631-640 Keyboard buffer 0281-0282 641-642 Start of BASIC memory 0283-0284 643-644 Top of BASIC memory 0285 645 Serial bus timeout flag 0286 646 Current color code 0287 647 Color under cursor 0288 648 Screen memory page 0289 649 Maximum size of keyboard buffer 028A 650 Repeat all keys 028B 651 Repeat speed counter 028C 652 Repeat delay counter 028D 653 Keyboard shift/control flag 028E 654 Last shift pattern 028F-0290 655-656 Keyboard table setup pointer 0291 657 Keymode shift mode 0292 658 0 = scroll enable 0293 659 RS-232 control register 0294 660 RS-232 command register 0295-0296 661-662 Bit timing 0297 663 RS-232 status 0298 664 Number of bits to send 0299-029A 665-666 RS-232 speed code 029B 667 RS-232 receive pointer 029C 668 RS-232 input pointer :190: 029D 669 RS-232 transmit pointer 029E 670 RS-232 output pointer 029F-02A0 671-672 IRQ save during tape I/O 02A1 673 CIA 2 (NMI) interrupt control 02A2 674 CIA 1 timer A control log 02A3 675 CIA 1 interrupt log 02A4 676 CIA 1 timer A enabled flag 02A5 677 Screen row marker 02A6 678 [0 = NTSC; 1 = PAL -wf] 02A7-02BF 679-703 [unused -wf] 02C0-02FE 704-766 (Sprite 7 data area) 02FF 767 [unused -wf] 0300-0301 768-769 Error message link 0302-0303 770-771 BASIC warm start link 0304-0305 772-773 Crunch BASIC tokens link 0306-0307 774-775 Print tokens link 0308-0309 776-777 Start new BASIC code link 030A-030B 778-779 Get arithmetic element link 030C 780 SYS A-reg save 030D 781 SYS X-reg save 030E 782 SYS Y-reg save 030F 783 SYS status reg save 0310-0312 784-786 USR function jump (default B248) 0313 787 [unused -wf] 0314-0315 788-789 IRQ vector (default EA31) 0316-0317 790-791 BRK vector (default FE66) 0318-0319 792-793 NMI vector (default FE47) 031A-031B 794-795 OPEN vector (default F34A) 031C-031D 796-797 CLOSE vector (default F291) 031E-031F 798-799 Set-input vector (default F20E) 0320-0321 800-801 Set-output vector (default F250) 0322-0323 802-803 Restore I/O vector (default F333) 0324-0325 804-805 Input vector (default F157) 0326-0327 806-807 Output vector (default F1CA) 0328-0329 808-809 Test STOP vector (default F6ED) 032A-032B 810-811 GET vector (default F13E) 032C-032D 812-813 Abort I/O vector (default F32F) 032E-032F 814-815 USR vector (default FE66) 0330-0331 816-817 LOAD link (default F4A5) 0332-0333 818-819 SAVE link (default F5ED) 0334-033B 820-827 [unused -wf] 033C-03FB 828-1019 Cassette buffer 03FC-03FF 1020-1023 [unused -wf] 0400-07E7 1024-2023 Screen memory 07E8-07F7 2024-2039 [unused -wf] 07F8-07FF 2040-2047 [Sprite bitmap pointers -wf] 0800-9FFF 2048-40959 BASIC RAM memory :191: 8000-9FFF 32768-40959 Alternative: ROM plug-in area A000-BFFF 40960-49151 ROM: BASIC A000-BFFF 40960-49151 Alternative: RAM C000-CFFF 49152-53247 RAM memory D000-D02E 53248-53294 Video chip (6566) D400-D41C 54272-56319 Color nybble memory DC00-DC0F 56320-56335 Interface chip 1, IRQ (6526) DD00-DD0F 56576-56591 Interface chip 2, NMI (6526) D000-DFFF 53248-53294 Alternative: ROM character set E000-FFFF 57344-65535 ROM: operating system E000-FFFF 57344-65535 Alternative: RAM Commodore 64 6526 Usage (CIA 1) ------------------------------- +-------+-------+-------+-------+-------+-------+-------+-------+ $DC00 |Paddle |Paddle | | Joystick 0 | 56320 |SelectA|SelectB| | Right | Left | Down | Up | | Keyboard Row Select (inverted) | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC01 | | Joystick 1 | 56321 | Keyboard Column Read | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC02 | Data Direction Register A (for $DC00) | 56322 | Default $FF = All Output | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC03 | Data Direction Register B (for $DC01) | 56323 | Default $00 = All Input | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC04 | | 56324 | Timer A: | +- - - - - - - -+ $DC05 | Cassette Tape I/O or Keyboard & Clock Interrupt | 56325 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC06 | | 56326 | Timer B: | +- - - - - - - - - - - - - - - - - - - -+ $DC07 | Cassette Tape I/O Timing | 56327 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC08 | | Time-of-Day Clock | 56328 | | 1/10th Seconds | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC09 | | Time-of-Day Clock | Time-of-Day Clock | 56329 | |Seconds, BCD High Digit| Seconds, BCD Low Digit | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC0A | | Time-of-Day Clock | Time-of-Day Clock | 56330 | |Minutes, BCD High Digit| Minutes, BCD Low Digit | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC0B | | Time-of-Day Clock | Time-of-Day Clock | 56331 | | Hours, BCD High Digit | Hours, BCD Low Digit | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC0C | Shift Register (*unused) | 56332 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC0D | IRQ | | | Tape | | TOD |Timer B|Timer A| 56333 |Control| | | Input | | Alarm | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC0E | | | | |Timer A|PB6 Out|Timer A|Timer A| 56334 | | | | |OneShot| Mode |PB6 Out| Start | +-------+-------+-------+-------+-------+-------+-------+-------+ $DC0F | | | | |Timer B|PB7 Out|Timer B|Serial | 56335 | | | | |OneShot| Mode |PB7 Out|Clk In | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.9 :192: Commodore 64 6526 Usage (CIA 2) ------------------------------- +-------+-------+-------+-------+-------+-------+-------+-------+ $DD00 |Serial | Clock |Serial | Clock | ATN |RS-232 | Video Bank | 56320 | In | In | Out | Out | Out | Out | Select | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD01 | DSR | CTS | | DCD* | RI* | DTR | RTS |RS-232 | 56321 | In | In | | In | In | Out | Out | In | | Parallel User Port | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD02 | In | In | Out | Out | Out | Out | Out | Out | 56322 | Data Direction Register A (for $DC00), Default $3F | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD03 | In | In | In | In | In | Out | Out | In | 56323 | Data Direction Register B (for $DC01), $06 for RS-232 | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD04 | | 56324 | Timer A: | +- - - - - - - - - - - - - - - - - - - - - - - - - + $DD05 | RS-232 Timing | 56325 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD06 | | 56326 | Timer B: | +- - - - - - - - - - - - - - - - - - + $DD07 | RS-232 or Serial Bus Timing | 56327 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD08 | TOD clock registers (unused) | 56328 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD09 | TOD clock registers (unused) | 56329 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD0A | TOD clock registers (unused) | 56330 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD0B | TOD clock registers (unused) | 56331 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD0C | Shift Register (unused) | 56332 | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD0D | NMI | | |RS-232 | | TOD |Timer B|Timer A| 56333 |Control| | | In | | Alarm | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD0E | | | | |Timer A|PB6 Out|Timer A|Timer A| 56334 | | | | |OneShot| Mode |PB6 Out| Start | +-------+-------+-------+-------+-------+-------+-------+-------+ $DD0F | | | | |Timer B|PB7 Out|Timer B|Serial | 56335 | | | | |OneShot| Mode |PB7 Out|Clk In | +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.10 :193: Commodore 64 6566 Usage (Sprite Registers) ------------------------------------------ Sprite (1 byte each) 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ $D000 - $D00E | X Position (even addresses) | 53248 - 53262 +-------------------------------+ $D001 - $D00F | Y Position (odd addresses) | 53249 - 53263 +-------------------------------+ +-------------------------------+ $D027 - $D02E | Color | 53287 - 53297 +-------------------------------+ Sprite (1 bit each) 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+ $D010 | X-Position High Bit | 53264 +-------------------------------+ +-------------------------------+ $D015 | Sprite Enable | 53269 +-------------------------------+ +-------------------------------+ $D017 | Y-Expand | 53271 +-------------------------------+ +-------------------------------+ $D01B | Background Priority | 53275 +-------------------------------+ $D01C | Multicolor Enable | 53276 +-------------------------------+ $D01D | X-Expand | 53277 +-------------------------------+ $D01E | Interrupt: Sprite Collision | 53278 +-------------------------------+ $D01F |Interrupt: Sprite/bg Collision| 53279 +-------------------------------+ Figure C.11 :194: Commodore 64 6581 Usage (SID) ----------------------------- Voices (write only) Voice1 Voice2 Voice3 Voice1 Voice2 Voice3 +---+---+---+---+---+---+---+---+ $D400 $D407 $D40E | Low Byte| 54272 54279 54286 +- Frequency - - - - - - - - - -+ $D401 $D408 $D40F | High Byte| 54273 54280 54287 +---+---+---+---+---+---+---+---+ $D402 $D409 $D410 | Pulse Width Low Byte| 54274 54281 54288 +---+---+---+---+- - - - - - - -+ $D403 $D40A $D411 | 0 | 0 | 0 | 0 | High Bits| 54275 54282 54289 +---+---+---+---+---+---+---+---+ $D404 $D40B $D412 | Waveform |Tst|Rng|Syn|On/| 54276 54283 54290 |Noi|Pul|Saw|Tri| |Mod| |Off| +---+---+---+---+---+---+---+---+ $D405 $D40C $D413 | Attack | Decay | 54277 54284 54291 | 2 ms - 8 sec | 6 ms - 24 sec | +---+---+---+---+---+---+---+---+ $D406 $D40D $D414 | Sustain | Release | 54278 54285 54292 | Level | 6 ms - 24 sec | +---+---+---+---+---+---+---+---+ Filter & Volume (write only) +---+---+---+---+---+---+---+---+ $D415 | 0 | 0 | 0 | 0 | Low Bits| 54293 +---+---+---+---+- - - - - - - -+ $D416 | Filter Frequency High Byte| 54294 +---+---+---+---+---+---+---+---+ $D417 | Resonance | Filter Voices | 54295 | |ext| 1 | 2 | 3 | +---+---+---+---+---+---+---+---+ $D418 |V3 |FilterShape| Master | 54296 |Off|Hi |BP |Low| Volume | +---+---+---+---+---+---+---+---+ Sense (read only) +---+---+---+---+---+---+---+---+ $D419 | Paddle X | 54297 +---+---+---+---+---+---+---+---+ $D41A | Paddle Y | 54298 +---+---+---+---+---+---+---+---+ $D41B | Voice 3 Output | 54299 +---+---+---+---+---+---+---+---+ $D41C | Envelope 3 | 54300 +---+---+---+---+---+---+---+---+ Figure C.12 :195: Commodore PLUS/4--Preliminary ============================= At time of publication the Commodore 264 (alternatively called Plus/4) and a related machine, the Commodore 16, are not commercially available. Design details could change before commercial release. On the prototype units, much of zero-page is the same as for VIC and Commodore 64; in particular, the BASIC pointers (SOB, SOV, etc) are the same. Memory Map, Preliminary ----------------------- Much of zero-page is the same as for the Commodore 64. Some differences, and other information: Hex Decimal Description ------------------------------------------------------------------- 0073-008A 115-138 (CHRGET not present) 0097 151 How many open files 0098 152 Input device, normally 0 0099 153 Output CMD device, normally 3 00AC 172 Current logical file 00AD 173 Current secondary address 00AE 174 Current device 00AF-00B0 175-176 Pointer to filename 00C8-00C9 200-201 Pointer to screen line 00CA 202 Position of cursor on above line 00CD 205 Row where cursor lives 00EF 239 Number of characters in keyboard buffer 0314-0315 788-789 IRQ vector (default CE0E) 0316-0317 790-791 BRK vector (default F44B) 0318-0319 792-793 OPEN vector (default EF53) (Most other vectors are similar to the C64, but are two locations lower) 0500-0502 1280-1282 USR program jump 0509-0512 1289-1298 Logical file table 0513-051C 1299-1308 Device number table 051D-0526 1309-1318 Secondary address table 0527-0530 1319-1328 Keyboard buffer 0800-0BE7 2048-3047 Color memory 0C00-0FE7 3072-4071 Screen memory 1000-FFFF 4096-65535 BASIC RAM memory 8000-FFFF 32768-65535 ROM: BASIC FF00-FF3F 65280-65343 TED I/O control registers :196: "TED" Chip, Preliminary ----------------------- [again, it's pretty difficult to see the exact bit boundaries between the fields, so this may be off by a bit or two. :( -wf ] +-------+-------+-------+-------+-------+-------+-------+-------+ $FF00 | Low Byte| 65280 +- - - - - - - - - - - - - - Timer 1 - - - - - - - - - - - - - -+ $FF01 | High Byte| 65281 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF02 | Low Byte| 65282 +- - - - - - - - - - - - - - Timer 2 - - - - - - - - - - - - - -+ $FF03 | High Byte| 65283 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF04 | Low Byte| 65284 +- - - - - - - - - - - - - - Timer 3 - - - - - - - - - - - - - -+ $FF05 | High Byte| 65285 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF06 | Test | ECM | BMM | Blank | Rows | X-Adjust | 65286 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF07 |Rus Off| PAL |Freeze | MCM |Columns| Y-Adjust | 65287 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF08 | Keyboard Latch | 65288 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF09 |IRQFlag|Timer 3|///////|Timer 2|Timer 1|LightPn|Raster |///////| 65289 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF0A |IER: |Timer 3|///////|Timer 2|Timer 1|LightPn|Raster | | 65290 +-------+-------+-------+-------+-------+-------+-------+- - - -+ $FF0B | RC | 65291 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF0C |///////////////////////////////////////////////| | 65292 +-------+-------+-------+-------+-------+-------+- - - - - - - -+ $FF0D | CUR | 65293 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF0E | Voice 1 Low Byte| 65294 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF0F | Voice 2 Low Byte| 65295 +-------+-------+-------+-------+-------+-------+- - - - - - - -+ $FF10 |///////////////////////////////////////////////| High Bits| 65296 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF11 | Sound Select | Volume | 65297 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF12 |///////////////| BMB | RBANK |Voice 1 High| 65298 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF13 | Character Base |SCLOCK |STATUS | 65299 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF14 | Video Matrix |///////////////////////| 65300 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF15 |///////| Luminance | Color 0| 65301 +-------+- - - - - - - - - - - - - - - - - - - - - - - - - - - -+ $FF16 |///////| | 1| 65302 +-------+- - - - - - - - - Background - - - - - - - - - - - - -+ $FF17 |///////| 2| 65303 +-------+- - - - - - - - - Colors - - - - - - - - - - - - -+ $FF18 |///////| | 3| 65304 +-------+- - - - - - - - - - - - - - - - - - - - - - - - - - - -+ $FF19 |///////| | 4| 65305 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF1A |///////////////////////////////////////////////| | 65306 +-------+-------+-------+-------+-------+-------+- - - - - - - -+ $FF1B | BRE | 65307 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF1C |///////////////////////////////////////////////////////| | 65308 +-------+-------+-------+-------+-------+-------+-------+- - - -+ $FF1D | VL | 65309 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF1E | H | 65310 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF1F |///////| BL | VSUB | 65311 +-------+-------+-------+-------+-------+-------+-------+-------+ +-------+-------+-------+-------+-------+-------+-------+-------+ $FF3E | ROM Select | 65342 +-------+-------+-------+-------+-------+-------+-------+-------+ $FF3F | RAM Select | 65343 +-------+-------+-------+-------+-------+-------+-------+-------+ Figure C.13 :197: B-Series (B-128, CBM-256, etc.) =============================== The Great Zero-Page Hunt ------------------------ Zero page has a different meaning on the B series. There are several zero pages. Usually, you'll want to use values from bank 15 (the ROM bank, where system variables are kept); but if you are writing programs that will reside in a different bank, you'll have all of zero page (except locations 0 and 1) completely at your disposal. If you need space in bank 15 zero page, you'll need to do some looking around. Addresses $E6 to $FF are not used by the system. Locations $20 to $2B and $64 to $6E are work areas available for temporary use. Most zero-page locations may be copied to another part of memory so that their original contents can be restored after use. The programmer should take great care, however, in modifying the following locations, which are critical within the operating system or BASIC: $1A, $1D to $1F, $2D to $41, $43, $5B, $78, $85 to $87, $9E to $AB, $C0 to $E5. Memory Map ---------- The following information applies to B systems released after April 1983, which contain a revised machine language monitor. (If POKE 6,0:SYS 6 doesn't bring in a monitor display complete with a "period" prompt, you have an incompatible version.) Notable features as compared to previous Commodore products include: - CHRGOT is no longer in RAM. Wedge-type coding must be inserted at links $029E and $02A0, which is likely to make the job easier. - BASIC vectors have "split." Now, for example, there are discrete "start of variables" and "end of variables," distinct from end-of-BASIC and start-of-arrays. Three-byte vectors (including bank number) are not uncommon. - The "jump table" at the top of memory is still accessible and reasonably consistent with previous Commodore products. - Simple machine language programs will fit into the spare 1K of RAM at $0400 to $07FF without trouble. Large programs must be implemented either by plug-in memory (RAM or ROM) in bank 15 or by being placed into another bank (preferably bank 3). Supplementary code will be needed to make all the coding components fit. :198: The following map contains BASIC addresses specific to the B256/80; references to banks 0 and 4 are also specific to that machine. Most of the map is of general usage, however. Hex Decimal Description ------------------------------------------------------------------- -All Banks: 0000 0 6509 execution register 0001 1 6509 indirection register -Bank 0: Unused -Bank 1: 0002-F000 2-61439 BASIC program (text) RAM FA5E-FB00 61440-64512 Input buffer area -B256 Bank 2: 0002-FFFF 2-65535 BASIC arrays in RAM -B128 Bank 2: 0002-FFFF 2-65535 BASIC variables, arrays and strings; KEY definitions -B256 Bank 3: 0002-7FFF 2-32767 Unused RAM 8000-FFFF 32768-65535 BASIC variables in RAM -B256 Bank 4: 0002-FBFF 2-64511 BASIC strings (top down) in RAM FC00-FCFF 64512-64767 Unused RAM (descriptors?) FD00-FFFF 64768-65535 Current KEY definitions -Banks 5 to 14: Unused. -Bank 15: 0002-0004 2-4 USR jump 0005-0008 5-8 TI$ output elements: Hours, Mins, Secs, Tenths 0009-000B 9-11 PRINT USING format pointer 000C 12 Search character 000D 13 Scan-between-quotes flag 000E 14 Input point; number of subscripts 000F 15 Catalog line counter 0010 16 Default DIM flag 0011 17 Type: $FF = string; $00 = integer :199: 0012 18 Type: $80 = integer; $00 = floating point 0013 19 Crunch flag 0014 20 Subscript index 0015 21 INPUT = 0; GET = 64; READ = 152 0016-0019 22-25 Disk status work values 001A 26 Current I/O device for prompt suppress 001B-001C 27-28 Integer value 001D-001F 29-31 Descriptor stack pointers 0020-002B 32-43 Miscellaneous work pointers 002C 44 [unused? -wf] 002D-002E 45-46 Start-of-BASIC pointer 002F-0030 47-48 End-of-BASIC pointer 0031-0032 49-50 Start-of-Variables pointer 0033-0034 51-52 End-of-Variables pointer 0035-0036 53-54 Start-of-Arrays pointer 0037-0038 55-56 End-of-Arrays pointer 0039-003A 57-58 Variable work pointer 003B-003C 59-60 Bottom-of-Strings pointer 003D-003E 61-62 Utility string pointer 003F-0041 63-65 Top of string memory pointer 0042-0043 66-67 Current BASIC line number 0044-0045 68-69 Previous BASIC line number 0046-0047 70-71 Previous BASIC text pointer 0048 72 [unused? -wf] 0049-004A 73-74 Data line pointer 004B-004C 75-76 Data text pointer 004D-004E 77-78 Input pointer 004F-0050 79-80 Variable name 0051-0053 81-83 Variable address 0054-0056 84-86 For-loop pointer 0057-0058 87-88 Text pointer save 0059 89 [unused? -wf] 005A 90 Comparison symbol accumulator 005B-005D 91-93 Function location 005E-0060 94-96 Working string vector 0061-0063 97-99 Function jump code 0064-006E 100-110 Work pointers, values 006F 111 Exponent sign 0070 112 Accumulator string prefix 0071 113 Accumulator #1: exponent 0072-0075 114-117 Accumulator #1: mantissa 0076 118 Accumulator #1: sign :200: 0077 119 Series evaluation constant pointer 0078 120 Accumulator #1: high-order (overflow) 0079 121 Accumulator #2: exponent 007A-007D 122-125 Accumulator #2: mantissa 007E 126 Accumulator #2: sign 007F 127 Sign comparison: Accumulator #1 vs #2 0080 128 Accumulator #1: low-order (rounding) 0081-0084 129-132 Series; work pointers 0085-0087 133-135 BASIC text pointer 0088-0089 136-137 Input pointer 008B-008E 139-142 DOS parser work values 008F 143 Error type number 0090-0092 144-146 Pointer to filename 0093-0095 147-149 Pointer: tape buffer; scrolling 0096-0098 150-152 Load end address; end of program 0099-009B 153-155 I/O start address 009C 156 Status word ST 009D 157 Filename length 009E 158 Current logical file 009F 159 Current device 00A0 160 Current secondary address 00A1 161 Input device, normally 0 00A2 162 Output CMD device, normally 3 00A3-00A5 163-165 [unused? -wf] 00A6-00A8 166-168 INBUF 00A9 169 Keyswitch PIA: STOP key, etc. 00AA 170 IEEE deferred flag 00AB 171 IEEE deferred character 00AC-00AD 172-173 Segment transfer routine vector 00AE-00B3 174-179 Monitor register save 00B4 180 Monitor stack pointer save 00B5 181 Monitor bank number save 00B6 182 [unused? -wf] 00B7-00B8 183-184 Monitor IRQ save; pointer 00B9-00BA 185-186 Monitor memory pointer 00BB-00BC 187-188 Monitor secondary pointer 00BD 189 Monitor counter 00BE 190 Monitor miscellaneous byte 00BF 191 Monitor device number 00C0-00C1 192-193 Programmable key table address 00C2-00C3 194-195 Programmable key address 00C4-00C7 196-199 Pointers to change programmable key table :201: 00C8-00C9 200-201 Pointer to screen line 00CA 202 Screen line number 00CB 203 Position of cursor on line 00CC 204 0 = text mode, else graphics mode 00CD 205 Keypress variable 00CE 206 Old cursor column 00CF 207 Old cursor row 00D0 208 New character flag 00D1 209 Number of keys in keyboard buffer 00D2 210 Quotes flag 00D3 211 Insert key counter 00D4 212 Cursor type flag 00D5 213 Screen line length 00D6 214 Number of keys in "key" buffer 00D7 215 Key repeat delay 00D8 216 Key repeat speed 00D9-00DA 217-218 Temporary variables 00DB 219 Current output character 00DC 220 Top line of current screen 00DD 221 Bottom line of screen 00DE 222 Left edge of current screen 00DF 223 Right edge of screen 00E0 224 Keys: 255 = none; 127 = key; 111 = shift 00E1 225 Key pressed: 255 = no key 00E2-00E5 226-229 Line wrap bits 00E6-00FF 230-255 [unused -wf] 0100 256 Hex to binary staging area 0100-010A 256-266 Numeric to ASCII work area 0100-01FE 256-510 Stack area 01FF 511 Stack pointer save location 0200-020F 512-527 Filename area 0210-0226 528-550 Disk command work area 0227-0254 551-596 [unused? -wf] 0255-0256 597-598 Miscellaneous work values for WAIT, etc. 0257 599 "Bank: value 0258 600 Output logical file (CMD) 0259 601 Sign of TAN 025A-025D 602-605 Pickup subroutine; miscellaneous work values 025E-0276 606-630 PRINT USING working variables :202: 0280-0281 640-641 Error routine link (default 8555) 0282-0283 642-643 Warm start link (default 85CD) 0284-0285 644-645 Crunch token link (default 88C2) 0286-0287 646-647 LIST link (default 89F4) 0288-0289 648-649 Command dispatch link (default 8754) 028A-028B 650-651 Token evaluate link (default 96B1) 028C-028D 652-653 Expression eval link (default 95C4) 028E-028F 654-655 CHRGOT link (default BA2C) 0290-0291 656-657 CHRGET link (default BA32) 0292-0293 658-659 Float-fixed vector (default BA1E) 0294-0295 660-661 Fixed-float vector (default 9D39) 0296-0297 662-663 Error trap vector 0298-0299 664-665 Error line number 029A-029B 666-667 Error exit pointer 029C 668 Stack pointer save 029D-029F 669-671 Temporary TRAP, DISPOSE bytes 02A0-02A5 672-677 Temporary INSTR$ bytes 02A6-02A7 678-679 Bank offset 02A8-02FF 680-767 [unused? -wf] 0300-0301 768-769 IRQ vector (default FBE9) 0302-0303 770-771 BRK vector (default EE21) 0304-0305 772-773 NMI vector (default FCAA) 0306-0307 774-775 OPEN vector (default F6BF) 0308-0309 776-777 CLOSE vector (default F5ED) 030A-030B 778-779 Connect-input vector (default F549) 030C-030D 780-781 Connect-output vector (default F5A3) 030E-030F 782-783 Restore default I/O vector (default F6A6) 0310-0311 784-785 Input vector (default F49C) 0312-0313 786-787 Output vector (default F4EE) 0314-0315 788-789 STOP key test vector (default F96B) 0316-0317 790-791 GET vector (default F43D) 0318-0319 792-793 Abort all files vector (default F67F) 031A-031B 794-795 LOAD vector (default F746) 031C-031D 796-797 SAVE vector (default F84C) 031E-031F 798-799 Monitor command vector (default EE77) 0320-0321 800-801 Keyboard control vector (default E01F) 0322-0323 802-803 Print control vector (default E01F) [??? -wf] 0324-0325 804-805 IEEE send LSA vector (default F274) 0326-0327 806-807 IEEE send TSA vector (default F280) :203: 0328-0329 808-809 IEEE receive byte vector (default F30A) 032A-032B 810-811 IEEE send character vector (default F297) 032C-032D 812-813 IEEE send untalk vector (default F2AB) 032E-032F 814-815 IEEE send unlisten vector (default F2AF) 0330-0331 816-817 IEEE send listen vector (default F234) 0332-0333 818-819 IEEE send talk vector (default F230) 0334-033D 820-829 File logical addresses table 033E-0347 830-839 File device table 0348-0351 840-849 File secondary address table 0352-0354 850-852 Bottom of system memory 0355-0357 853-855 Top of system memory 0358-035A 856-858 Bottom of user memory 035B-035D 859-861 Top of user memory 035E 862 IEEE timeout: 0 = enabled 035F 863 0 = load; 128 = verify 0360 864 Number of open files 0361 865 Message mode byte 0362 866 [unused? -wf] 0363-0366 867-870 Miscellaneous register save bytes 0369 873 Timer toggle 036A-036B 874-875 Cassette vector (dead end) 036C-036E 876-878 [unused? -wf] 036F-0371 879-881 Relocation start address 0372-0374 882-884 [unused? -wf] 0375 885 Cassette motor flag (unused) 0376-0377 886-887 RS-232 control, command 0378-0379 888-889 [unused? -wf] 037A 890 RS-232 status 037B 891 RS-232 handshake input 037C 892 RS-232 input buffer 037D 893 RS-232 arrival pointer 037E-037F 894-895 [unused? -wf] 0380-0381 896-897 Top of memory pointer 0382 898 Bank byte 0383 899 RVS flag 0384 900 Current line length 0385 901 Temporary output character save 0386 902 0 = normal; 255 = auto insert 0387 903 0 = scrolling; 255 = no scroll 0388 904 Miscellaneous work byte for screen :204: 0389 905 Index to programmed key 038A 906 Scroll mode flag 038B 907 Bell mode flag 038C 908 Indirect bank save 038D-03A0 909-928 Lengths of 'key' words 03A1-03AA 929-938 Bitmapped tab stops 03AB-03B4 939-948 Keyboard input buffer 03B5-03B6 949-950 'Key' word link (default E91B) 03B7-03F7 951-1015 [unused? -wf] 03F8-03F9 1016-1017 Restart vector 03FA-03FB 1018-1019 Restart test mask 03FC-03FF 1020-1023 [unused? -wf] 0400-07FF 1024-2047 Free RAM (reserved for DOS) 0800-0FFF 2048-4095 Reserved for plug-in RAM 1000-1FFF 4096-8191 Reserved for plug-in DOS ROM 2000-7FFF 8192-32767 Reserved for cartridges 8000-BFFF 32768-49151 BASIC ROM C000-CFFF 49152-53247 Unused D000-D7CF 53248-55247 Screen RAM D800-D801 55296-55297 Video controller 6545 DA00-DA1C 55808-55836 Sound interface device 6581 DB00-DB0F 56064-56079 Complex interface adapter 6526 DC00-DC0F 56320-56335 Complex interface adapter 6526 DD00-DD03 56576-56579 Asynchronous communications IA 6551 DE00-DE07 56832-56839 Tri Port Interface Adapter 6525 DF00-DF07 57088-57095 Tri Port Interface Adapter 6525 E000-FFFF 57344-65535 Kernal ROM The above table shows contents for the link and vector addresses at $0280 to $0295; these are taken from a recent B-128. :205: B-Series 6545 CRT Controller ---------------------------- +-------+---------------------------+-------------------+ | $D800 | $D801 | Typical Value | | 55296 | 55297 | (decimal) | +-------+---------------------------+-------------------+ | 0 | Horizontal Total | 108 or 126 or 127 | +-------+---------------------------+-------------------+ | 1 |Horizontal Chars Displayed | 80 | +-------+---------------------------+-------------------+ | 2 | Horizontal Sync Position | 83 or 98 or 96 | +-------+-------------+-------------+-------------------+ | 3 |V Sync Width |H Sync Width | 15 or 10 | +-------+-------------+-------------+-------------------+ | 4 | Vertical Total | 25 or 31 or 38 | +-------+---------------------------+-------------------+ | 5 | Vertical Total Adjustment | 3 or 6 or 1 | +-------+---------------------------+-------------------+ | 6 | Vertical Displayed | 25 | +-------+---------------------------+-------------------+ | 7 | Vertical Sync Position | 25 or 28 or 30 | +-------+---------------------------+-------------------+ | 8 | Mode | 0 | +-------+---------------------------+-------------------+ | 9 | Scan Lines | 13 or 7 | +-------+---------------------------+-------------------+ | 10 | Cursor Start | 96 (blink) or 0 | | | | or 6 (underline) | +-------+---------------------------+-------------------+ | 11 | Cursor End | 13 or 7 | +-------+---------------------------+-------------------+ | 12 | High| 0 | +-------+- - - Display Address - - -+-------------------+ | 13 | Low| 0 | +-------+---------------------------+-------------------+ | 14 | High| Varies | +-------+- - - Cursor Address - - -+-------------------+ | 15 | Low| Varies | +-------+---------------------------+-------------------+ | 16 | High| 0 | +-------+- - - Light Pen In - - - -+-------------------+ | 17 | Low| 0 | +-------+---------------------------+-------------------+ Most registers are write only: 14/15 are read/write 16/17 are read only Registers 10, 14, and 15 change as the cursor moves Figure C.14 :206: B-Series 6525 Tri Port 1 ------------------------ +-------+-------+-------+-------+-------+-------+-------+-------+ $DE00 | NRFD | NDAC | EOI | DAV | ATN | RFN | | | 56832 +-------+-------+-------+-------+-------+-------+-------+-------+ $DE01 | Cassette | Network | SRQ | IFC | 56833 | Sense | Motor | Out | ARB | Rx | Tx | | | +-------+-------+-------+-------+-------+-------+-------+-------+ $DE02 | | 56834 +-------+-------+-------+-------+-------+-------+-------+-------+ $DE03 | Data Direction Register for $DE00 | 56835 +-------+-------+-------+-------+-------+-------+-------+-------+ $DE04 | Data Direction Register for $DE01 | 56836 +-------+-------+-------+-------+-------+-------+-------+-------+ $DE05 | IRQ | | ACIA | IP | ALM | IEEE | PWR | 56837 +-------+-------+-------+-------+-------+-------+-------+-------+ $DE06 | CB | | CA | | IRQ | 56838 | | | Graphics | | Stack On | +-------+-------+-------+-------+-------+-------+-------+-------+ $DE07 | Active Interrupt Register | 56839 +-------+-------+-------+-------+-------+-------+-------+-------+ B-Series 6525 Tri Port 2 ------------------------ +-------+-------+-------+-------+-------+-------+-------+-------+ $DF00 | Keyboard | 57088 +-------+-------+-------+-------+-------+-------+-------+-------+ $DF01 | Select | 57089 +-------+-------+-------+-------+-------+-------+-------+-------+ $DF02 | CRT Mode | Keyboard Read | 57090 +-------+-------+-------+-------+-------+-------+-------+-------+ $DF03 | Data Direction Register for $DF00 (out) | 57091 +-------+-------+-------+-------+-------+-------+-------+-------+ $DF04 | Data Direction Register for $DF01 (out) | 57092 +-------+-------+-------+-------+-------+-------+-------+-------+ $DF05 | Data Direction Register for $DF02 (in) | 57093 +-------+-------+-------+-------+-------+-------+-------+-------+ $DF06 | Unused | 57094 +-------+-------+-------+-------+-------+-------+-------+-------+ Commodore 64: ROM Detail ------------------------- This type of ROM memory map is intended primarily for users who want to "browse" through the inner logic of the computer. It allows a user to disassemble an area of interest, to see why the computer behaves in a certain way. With the use of this map, the user will be able to identify subroutines that are called by the coding under study. I recommend against using the ROM subroutines as part of your own programs. They often don't do precisely what you want. They change locations when you move to a different machine. With rare exceptions, you can probably write better coding to do the job yourself. Stick with the kernal jump table: especially $FFD2 to output; $FFE4 to get input; $FFE1 to check the RUN/STOP key; $FFC6 and $FFC9 to switch input and output respectively; and $FFCC to restore normal input/output. They are the same on all Commodore products. [if you're looking for more in-depth information, you definitely should read "Mapping the Commodore 64", which should be available from where you got this e-text. -wf] :207: A000: ROM control vectors A00C: Keyword action vectors A052: Function vectors A080: Operator vectors A09E: Keywords A19E: Error messages A328: Error message vectors A365: Miscellaneous messages A38A: Scan stack for FOR/GOSUB A3B8: Move memory A3FB: Check stack depth A408: Check memory space A435: Print "OUT OF MEMORY" A437: Error routine A469: BREAK entry A474: Print "READY." A480: Ready for BASIC A49C: Handle new line A533: Re-chain lines A560: Receive input line A579: Crunch tokens A613: Find BASIC line A642: Perform NEW A65E: Perform CLR A68E: Back up text pointer A69C: Perform LIST A742: Perform FOR A7ED: Execute statement A81D: Perform RESTORE A82C: Break A82F: Perform STOP A831: Perform END A857: Perform CONT A871: Perform RUN A883: Perform GOSUB A8A0: Perform GOTO A8D2: Perform RETURN A8F8: Perform DATA A906: Scan for next statement A928: Perform IF A93B: Perform REM A94B: Perform ON A96B: Get fixed point number A9A5: Perform LET AA80: Perform PRINT# :208: AA86: Perform CMD AAA0: Perform PRINT AB1E: Print string from (Y.A) AB3B: Print format character AB4D: Bad input routine AB7B: Perform GET ABA5: Perform INPUT# ABBF: Perform INPUT ABF9: Prompt and input AC06: Perform READ ACFC: Input error messages AD1E: Perform NEXT AD78: Type match check AD9E: Evaluate expression AEA8: Constant-pi AEF1: Evaluate within brackets AEF7: Check for ")" AEFF: Check for comma AF08: Syntax error AF14: Check range AF28: Search for variable AFA7: Set up FN reference AFE6: Evaluate OR AFE9: Evaluate AND B016: Compare B081: Perform DIM B08B: Locate variable B113: Check alphabetic B11D: Create variable B194: Array pointer subroutine B1A5: Value 32768 B1B2: Float-fixed B1D1: Set up array B245: Print "BAD SUBSCRIPT" B248: Print "ILLEGAL QUANTITY" B34C: Compute array size B37D: Evaluate FRE B391: Fixed-float B39E: Evaluate POS B3A6: Check direct B3B3: Perform DEF B3E1: Check FN syntax B3F4: Evaluate FN :209: B465: Evaluate STR$ B475: Calculate string vector B487: Set up string B4F4: Make room for string B526: Garbage collection B5BD: Check salvageability B606: Collect string B63D: Concatenate B67A: Build string to memory B6A3: Discard unwanted string B6DB: Clean descriptor stack B6EC: Evaluate CHR$ B700: Evaluate LEFT$ B72C: Evaluate RIGHT$ B737: Evaluate MID$ B761: Pull string parameters B77C: Evaluate LEN B782: Exit string-mode B78B: Evaluate ASC B79B: Input byte parameter B7EB: Parameters for POKE/WAIT B7F7: Float-fixed B80D: Evaluate PEEK B824: Perform POKE B82D: Perform WAIT B849: Add 0.5 B850: Subtract-from B853: Evaluate - (subtraction) B86A: Evaluate + (addition) B947: Complement FAC (floating accumulator) #1 B97E: Print "OVERFLOW" B983: Multiply by zero byte B9EA: Evaluate LOG BA2B: Evaluate * (multiplication) BA59: Multiply a bit BA8C: Memory to FAC#2 BAB7: Adjust FAC#1 and FAC#2 BAD4: Underflow/overflow BAE2: Multiply by 10 BAF9: +10 in floating point BAFE: Divide by 10 BB12: Evaluate / (division) :210: BBA2: Memory to FAC#1 BBC7: FAC#1 to memory BBFC: FAC#2 to FAC#1 BC0C: FAC#1 to FAC#2 BC1B: Round FAC#1 BC2B: Get sign BC39: Evaluate SGN BC58: Evaluate ABS BC5B: Compare FAC#1 to memory BC9B: Float-fixed BCCC: Evaluate INT BCF3: String to FAC BD7E: Get ASCII digit BDC2: Print "IN.." BDCD: Print line number BDDD: Float to ASCII BF16: Decimal constants BF3A: TI constants BF71: Evaluate SQR BF7B: Evaluate ^ (raise to power) BFB4: Evaluate - (negative) BFED: Evaluate EXP E043: Series evaluation 1 E059: Series evaluation 2 E097: Evaluate RND E0F9: Kernal calls with error checking E12A: Perform SYS E156: Perform SAVE E165: Perform VERIFY E168: Perform LOAD E1BE: Perform OPEN E1C7: Perform CLOSE E1D4: Parameters for LOAD/SAVE E206: Check default parameters E20E: Check for comma E219: Parameters for open/close E264: Evaluate COS E26B: Evaluate SIN E2B4: Evaluate TAN E30E: Evaluate ATN E37B: Warm restart E394: Initialize E3A2: CHRGET for zero page :211: E3BF: Initialize BASIC E447: Vectors for $0300 E453: Initialize vectors E45F: Power-up message E500: Get I/O messages E505: Get screen size E50A: Put/get row/column E518: Initialize I/O E544: Clear screen E566: Home cursor E56C: Set screen pointers E5A0: Set I/O defaults E5B4: Input from keyboard E632: Input from screen E684: Quote test E691: Set up screen print E6B6: Advance cursor E6ED: Retreat cursor E701: Back into previous line E716: Output to screen E87C: Go to next line E891: Perform (return) E8A1: Check line decrement E8B3: Check line increment E8CB: Set color code E8EA: Scroll screen E965: Open space on screen E9C8: Move a screen line E9E0: Synchronize color transfer E9F0: Set start-of-line E9FF: Clear screen line EA13: Print to screen EA24: Synchronize color pointer EA31: Interrupt-clock, etc. EA87: Read keyboard EB79: Keyboard select vectors EB81: Keyboard 1-unshifted EBC2: Keyboard 2-shifted EC03: Keyboard 3-"Commodore" shifted EC44: Graphics/text control EC4F: Set graphics/text mode EC78: Keyboard 4 :212: ECB9: Video chip setup ECE7: Shift/run equivalent ECF0: Screen line address low ED09: Send "talk" to serial bus ED0C: Send "listen" to serial bus ED40: Send to serial bus EDB2: Serial timeout EDB9: Send listen SA EDBE: Clear ATN EDC7: Send talk SA EDCC: Wait for clock EDDD: Send serial deferred EDEF: Send "untalk" to serial bus EDFE: Send "unlisten" to serial bus EE13: Receive from serial bus EE85: Serial clock on EE8E: Serial clock off EE97: Serial output "1" EEA0: Serial output "0" EEA9: Get serial in and clock signals EEB3: Delay 1 millisecond EEBB: RS-232 send EF06: Send new RS-232 byte EF2E: No-DSR error EF31: No-CTS error EF3B: Disable timer EF4A: Compute bit count EF59: RS-232 receive EF7E: Set up to receive EFC5: Receive parity error EFCA: Receive overflow EFCD: Receive break EFD0: Framing error EFE1: Submit to RS-232 F00D: No-DSR error F017: Send to RS-232 buffer F04D: Input from RS-232 F086: Get from RS-232 F0A4: Check serial bus idle F0BD: Messages F12B: Print if direct F13E: Get... F14E: ...from RS-232 :213: F157: Input F199: Get: tape/serial/RS-232 F1CA: Output... F1DD: ...to tape F20E: Set input device F250: Set output device F291: Close file F30F: Find file F31F: Set file values F32F: Abort all files F333: Restore default I/O F34A: Do file open F3D5: Send SA F409: Open RS-232 F49E: Load program F5AF: Print "SEARCHING" F5C1: Print filename F5D2: Print "LOADING"/"VERIFYING" F5DD: Save program F68F: Print "SAVING" F69B: Bump clock F6BC: Log PIA key reading F6DD: Get time F6E4: Set time F6ED: Check STOP key F6FB: Output error messages F72D: Find any tape header F76A: Write tape header F7D0: Get buffer address F7D7: Set buffer start/end pointers F7EA: Find specific header F80D: Bump tape pointer F817: Print "PRESS PLAY..." F82E: Check tape status F838: Print "PRESS RECORD..." F841: Initiate tape read F864: Initiate tape write F875: Common tape code F8D0: Check tape stop F8E2: Set read timing F92C: Read tape bits FA60: Store tape characters FB8E: Reset pointer :214: FB97: New character setup FBA6: Send transition to tape FBC7: Write data to tape FBCD: IRQ entry point FC57: Write tape header FC93: Restore normal IRQ FCB8: Set IRQ vector FCCA: Kill tape motor FCD1: Check R/W pointer FCDB: Bump R/W pointer FCE2: Power reset entry FD02: Check 8-ROM FD10: 8-ROM mask FD15: Kernal reset FD1A: Kernal move FD30: Vectors FD50: Initialize system constants FD9B: IRQ vectors FDA3: Initialize I/O FDDD: Enable timer FDF9: Save filename data FE00: Save file details FE07: Get status FE18: Flag status FE1C: Set status FE21: Set timeout FE25: Read/set top of memory FE27: Read top of memory FE2D: Set top of memory FE34: Read/set bottom of memory FE43: NMI entry FE66: Warm start FEB6: Reset IRQ and exit FEBC: Interrupt exit FEC2: RS-232 timing table FED6: NMI RS-232 in FF07: NMI RS-232 out FF43: Fake IRQ FF48: IRQ entry FF81: Jumbo jump table FFFA: Hardware vectors :215: Appendix D Character Sets :216: Superchart ---------- The "superchart" shows the PET character sets. A byte may have any of several meanings, depending on how it is used. The chart is constructed to reflect this. "ASCII" is PET ASCII; these are the characters as they would be input or printed. "Screen" is the Commodore screen codes, as they would be used in screen memory--POKEing to or PEEKing from the screen would yield these codes. Notice that the numeric character set is the same for both screen and PET ASCII. Within a program, the code changes again. "BASIC" shows these codes; they are similar to ASCII in the range $20 to $5F. Machine language op codes are included for the sake of convenience and completeness. [any PETSCII character that can't be represented here will be replaced by a tilde (~). For ASCII and SCREEN columns, the first character displayed is for the standard uppercase/graphics character set. The second character, if present, is that character in the lowercase character set. Characters that are prepended with "r" means they appear in reverse video. A complete graphical list of the PETSCII and screen codes are listed at my web page, http://fly.to/theflame -wf] DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL 0 00 @ end-line BRK 0 1 01 A,a ORA(zp,X) 1 2 02 B,b 2 3 03 C,c 3 4 04 D,d 4 5 05 white E,e ORA zp 5 6 06 F,f ASL zp 6 7 07 bell G,g 7 8 08 lock H,h PHP 8 9 09 unlock I,i ORA #imm 9 10 0A J,j ASL A 10 11 0B K,k 11 12 0C L,l 12 13 0D car ret M,m ORA abs 13 14 0E text N,n ASL abs 14 15 0F top O,o 15 16 10 P,p BPL rel 16 17 11 cur down Q,q ORA(zp),Y 17 18 12 reverse R,r 18 19 13 cur home S,s 19 20 14 delete T,t 20 21 15 del line U,u ORA zp,X 21 22 16 ers begin V,v ASL zp,X 22 23 17 W,w 23 24 18 X,x CLC 24 25 19 scr up Y,y ORA abs,Y 25 26 1A Z,z 26 27 1B [ 27 28 1C red sterling 28 29 1D cur right ] ORA abs,X 29 :217: DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL 30 1E green up arrow ASL abs,X 30 31 1F blue left arrow 31 32 20 space space space JSR abs 32 33 21 ! ! ! AND (zp,X) 33 34 22 " " " 34 35 23 # # # 35 36 24 $ $ $ BIT zp 36 37 25 % % % AND zp 37 38 26 & & & ROL zp 38 39 27 ' ' ' 39 40 28 ( ( ( PLP 40 41 29 ) ) ) AND #imm 41 42 2A * * * ROL A 42 43 2B + + + 43 44 2C , , , BIT abs 44 45 2D - - - AND abs 45 46 2E . . . ROL abs 46 47 2F / / / 47 48 30 0 0 0 BMI rel 48 49 31 1 1 1 AND(zp),Y 49 50 32 2 2 2 50 51 33 3 3 3 51 52 34 4 4 4 52 53 35 5 5 5 AND zp,X 53 54 36 6 6 6 ROL zp,X 54 55 37 7 7 7 55 56 38 8 8 8 SEC 56 57 39 9 9 9 AND abs,Y 57 58 3A : : : CLI 58 59 3B ; ; ; 59 60 3C < < < 60 61 3D = = = AND abs,X 61 62 3E > > > ROL abs,X 62 63 3F ? ? ? 63 64 40 @ ~ @ RTI 64 65 41 A,a ~,A A EOR(zp,X) 65 66 42 B,b ~,B B 66 67 43 C,c ~,C C 67 68 44 D,d ~,D D 68 69 45 E,e ~,E E EOR zp 69 70 46 F,f ~,F F LSR zp 70 71 47 G,g ~,G G 71 72 48 H,h ~,H H PHA 72 73 49 I,i ~,I I EOR #imm 73 74 4A J,j ~,J J LSR A 74 75 4B K,k ~,K K 75 76 4C L,l ~,L L JMP abs 76 77 4D M,m ~,M M EOR abs 77 78 4E N,n ~,N N LSR abs 78 :218: DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL 79 4F O,o ~,O O 79 80 50 P,p ~,P P BVC rel 80 81 51 Q,q ~,Q Q EOR(zp),Y 81 82 52 R,r ~,R R 82 83 53 S,s ~,S S 83 84 54 T,t ~,T T 84 85 55 U,u ~,U U EOR zp,X 85 86 56 V,v ~,V V LSR zp,X 86 87 57 W,w ~,W W 87 88 58 X,x ~,X X CLI 88 89 59 Y,y ~,Y Y EOR abs,Y 89 90 5A Z,z ~,Z Z 90 91 5B [ ~ [ 91 92 5C sterling ~ sterling 92 93 5D ] ~ ] EOR abs,X 93 94 5E up arrow pi,~ up arrow LSR abs,X 94 95 5F left arrow ~,~ left arrow 95 96 60 ~ space RTS 96 97 61 ~,A ~ ADC(zp,X) 97 98 62 ~,B ~ 98 99 63 ~,C ~ 99 100 64 ~,D ~ 100 101 65 ~,E ~ ADC zp 101 102 66 ~,F ~ ROR zp 102 103 67 ~,G ~ 103 104 68 ~,H ~ PLA 104 105 69 ~,I ~,~ ADC #imm 105 106 6A ~,J ~ ROR A 106 107 6B ~,K ~ 107 108 6C ~,L ~ JMP(ind) 108 109 6D ~,M ~ ADC abs 109 110 6E ~,N ~ ROR abs 110 111 6F ~,O ~ 111 112 70 ~,P ~ BVS rel 112 113 71 ~,Q ~ ADC(zp),Y 113 114 72 ~,R ~ 114 115 73 ~,S ~ 115 116 74 ~,T ~ 116 117 75 ~,U ~ ADC zp,X 117 118 76 ~,V ~ ROR zp,X 118 119 77 ~,W ~ 119 120 78 ~,X ~ SEI 120 121 79 ~,Y ~ ADC abs,Y 121 122 7A ~,Z ~,~ 122 123 7B ~ ~ 123 124 7C ~ ~ 124 125 7D ~ ~ ADC abs,X 125 126 7E pi,~ ~ ROR abs,X 126 127 7F ~,~ ~ 127 :219: DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL 128 80 r@ END 128 129 81 orange rA,a FOR STA(zp,X) 129 130 82 rB,b NEXT 130 131 83 rC,c DATA 131 132 84 rD,d INPUT# STY zp 132 133 85 F1 rE,e INPUT STA zp 133 134 86 F3 rF,f DIM STX zp 134 135 87 F5 rG,g READ 135 136 88 F7 rH,h LET DEY 136 137 89 F2 rI,i GOTO 137 138 8A F4 rJ,j RUN TXA 138 139 8B F6 rK,k IF 139 140 8C F8 rL,l RESTORE STY abs 140 141 8D car ret rM,m GOSUB STA abs 141 142 8E graphic rN,n RETURN STX abs 142 143 8F bottom rO,o REM 143 144 90 black rP,p STOP BCC rel 144 145 91 cur up rQ,q ON STA(zp),Y 145 146 92 rvx off rR,r WAIT 146 147 93 clear rS,s LOAD 147 148 94 insert rT,t SAVE STY zp,X 148 149 95 ins line/br rU,u VERIFY STA zp,X 149 150 96 ers end/p rV,v DEF STX zp,Y 150 151 97 gray 1 rW,w POKE 151 152 98 gray 2 rX,x PRINT# TYA 152 153 99 scr down rY,y PRINT STA abs,Y 153 154 9A lt blue rZ,z CONT TXS 154 155 9B gray 3 r[ LIST 155 156 9C purple rSterling CLR 156 157 9D cur left r] CMD STA abs,X 157 158 9E yellow rUpArrow SYS 158 159 9F cyan rLeftArrow OPEN 159 160 A0 space rSpace CLOSE LDY #imm 160 161 A1 ~ r! GET LDA(zp,X) 161 162 A2 ~ r" NEW LDX #imm 162 163 A3 ~ r# TAB( 163 164 A4 ~ r$ TO LDY zp 164 165 A5 ~ r% FN LDA zp 165 166 A6 ~ r& SPC( LDX zp 166 167 A7 ~ r' THEN 167 168 A8 ~ r( NOT TAY 168 169 A9 ~,~ r) STEP LDA #imm 169 170 AA ~ r* + TAX 170 171 AB ~ r+ - 171 172 AC ~ r, * LDY abs 172 173 AD ~ r- / LDA abs 173 174 AE ~ r. UpArrow LDX abs 174 175 AF ~ r/ AND 175 176 B0 ~ r0 OR BCS rel 176 :220: DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL 177 B1 ~ r1 > LDA(zp),Y 177 178 B2 ~ r2 = 178 179 B3 ~ r3 < 179 180 B4 ~ r4 SGN( LDY zp,X 180 181 B5 ~ r5 INT( LDA zp,X 181 182 B6 ~ r6 ABS( LDX zp,Y 182 183 B7 ~ r7 USR( 183 184 B8 ~ r8 FRE( CLV 184 185 B9 ~ r9 POS( LDA abs,Y 185 186 BA ~,~ r: SQR( TSX 186 187 BB ~ r; RND( 187 188 BC ~ r< LOG( LDY abs,X 188 189 BD ~ r= EXP( LDA abs,X 189 190 BE ~ r> COS( LDX abs,Y 190 191 BF ~ r? SIN( 191 192 C0 ~ r~ TAN( CPY #imm 192 193 C1 ~,A r~,rA ATN( CMP(zp,X) 193 194 C2 ~,B r~,rB PEEK( 194 195 C3 ~,C r~,rC LEN( 195 196 C4 ~,D r~,rD STR$( CPY zp 196 197 C5 ~,E r~,rE VAL( CMP zp 197 198 C6 ~,F r~,rF ASC( DEC zp 198 199 C7 ~,G r~,rG CHR$( 199 200 C8 ~,H r~,rH LEFT$( INY 200 201 C9 ~,I r~,rI RIGHT$( CMP #imm 201 202 CA ~,J r~,rJ MID$( DEX 202 203 CB ~,K r~,rK GO 203 204 CC ~,L r~,rL CONCAT CPY abs 204 205 CD ~,M r~,rM DOPEN CMP abs 205 206 CE ~,N r~,rN DCLOSE DEC abs 206 207 CF ~,O r~,rO RECORD 207 208 D0 ~,P r~,rP HEADER BNE rel 208 209 D1 ~,Q r~,rQ COLLECT CMP(zp),Y 209 210 D2 ~,R r~,rR BACKUP 210 211 D3 ~,S r~,rS COPY 211 212 D4 ~,T r~,rT APPEND 212 213 D5 ~,U r~,rU DSAVE CMP zp,X 213 214 D6 ~,V r~,rV DLOAD DEC zp,X 214 215 D7 ~,W r~,rW CATALOG 215 216 D8 ~,X r~,rX RENAME CLD 216 217 D9 ~,Y r~,rY SCRATCH CMP abs,Y 217 218 DA ~,Z r~,rZ DIRECTORY 218 219 DB ~ r~ 219 220 DC ~ r~ 220 221 DD ~ r~ CMP abs,X 221 222 DE pi,~ rPi,r~ DEC abs,X 222 223 DF ~,~ r~,r~ 223 224 E0 space rSpace CPX #imm 224 225 E1 ~ r~ SBC(zp,X) 225 :221: DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL 226 E2 ~ r~ 226 227 E3 ~ r~ 227 228 E4 ~ r~ CPX zp 228 229 E5 ~ r~ SBC zp 229 230 E6 ~ r~ INC zp 230 231 E7 ~ r~ 231 232 E8 ~ r~ INX 232 233 E9 ~,~ r~,r~ SBC #imm 233 234 EA ~ r~ NOP 234 235 EB ~ r~ 235 236 EC ~ r~ CPX abs 236 237 ED ~ r~ SBC abs 237 238 EE ~ r~ INC abs 238 239 EF ~ r~ 239 240 F0 ~ r~ BEQ rel 240 241 F1 ~ r~ SBC(zp),Y 241 242 F2 ~ r~ 242 243 F3 ~ r~ 243 244 F4 ~ r~ 244 245 F5 ~ r~ SBC zp,X 245 246 F6 ~ r~ INC zp,X 246 247 F7 ~ r~ 247 248 F8 ~ r~ SED 248 249 F9 ~ r~ SBC abs,Y 249 250 FA ~,~ r~,r~ 250 251 FB ~ r~ 251 252 FC ~ r~ 252 253 FD ~ r~ SBC abs,X 253 254 FE ~ r~ INC abs,X 254 255 FF pi,~ r~ pi 255 :222: ASCII ----- ASCII is the American Standard for Information Interchange. It is the standard for communications, and is often used with non-Commodore printers. When a Commodore machine is in its graphic mode, its character set corresponds closely to ASCII. Numeric, upper case alphabetic, and punctuation characters are the same. A few control characters, such as RETURN, also match. Commodore graphics have no counterpart in ASCII. When the Commodore machine is switched to text mode, the character set diverges noticeably from ASCII. Numeric characters and much of the punctuation corresponds, but ASCII upper case alphabetic codes match the Commodore computer's lower case codes. Commodore's upper case alphabetics are now completely out of the ASCII range, since ASCII is a seven-bit code. As a result, Commodore's PET ASCII codes require conversion before transmission to a true ASCII device or communications line. This may be done with either hardware interfacing or with a program. Briefly, the procedure is: 1. If the Commodore character is below $3F, it may be transmitted directly to the ASCII facility. 2. If the Commodore character is between $40 and $5F, it should be logically ORed with $20 (or add decimal 32) before transmission to ASCII. 3. If the Commodore character is between $C0 and $DF, it should be logically ANDed with $7F (or subtract decimal 128) before transmission to ASCII. Equivalent rules can be derived to allow a Commodore computer to receive from ASCII. For either direction of transmission, some control characters may require special treatment. :223: First Hexadecimal Digit +---+---+---+---+---+---+---+---+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +---+---+---+---+---+---+---+---+---+ | 0 |NUL|DLE|SP | 0 | @ | P | ` | p | +---+---+---+---+---+---+---+---+---+ S | 1 |SOH|DC1| ! | 1 | A | Q | a | q | e +---+---+---+---+---+---+---+---+---+ c | 2 |STX|DC2| " | 2 | B | R | b | r | o +---+---+---+---+---+---+---+---+---+ n | 3 |ETX|DC3| # | 3 | C | S | c | s | d +---+---+---+---+---+---+---+---+---+ | 4 |EOT|DC4| $ | 4 | D | T | d | t | H +---+---+---+---+---+---+---+---+---+ e | 5 |ENQ|NAK| % | 5 | E | U | e | u | x +---+---+---+---+---+---+---+---+---+ a | 6 |ACK|SYN| & | 6 | F | V | f | v | d +---+---+---+---+---+---+---+---+---+ e | 7 |BEL|ETB| ' | 7 | G | W | g | w | c +---+---+---+---+---+---+---+---+---+ i | 8 |BS |CAN| ( | 8 | H | X | h | x | m +---+---+---+---+---+---+---+---+---+ a | 9 |HT |EM | ) | 9 | I | Y | i | y | l +---+---+---+---+---+---+---+---+---+ | A |LF |SUB| * | : | J | Z | j | z | D +---+---+---+---+---+---+---+---+---+ i | B |VT |ESC| + | ; | K | [ | k | { | g +---+---+---+---+---+---+---+---+---+ i | C |FF |FS | , | < | L | \ | l | | | t +---+---+---+---+---+---+---+---+---+ | D |CR |GS | - | = | M | ] | m | } | +---+---+---+---+---+---+---+---+---+ | E |SO |RS | . | > | N | ^ | n | ~ | +---+---+---+---+---+---+---+---+---+ | F |SI |US | / | ? | O | _ | o |DEL| +---+---+---+---+---+---+---+---+---+ :224: Control Character Representations --------------------------------- NUL Null DLE Data Link Escape (CC) SOH Start of Heading (CC) DC1 Device Control 1 STX Start of Text (CC) DC2 Device Control 2 ETX End of Text (CC) DC3 Device Control 3 EOT End of Transmission (CC) DC4 Device Control 4 ENQ Enquiry (CC) NAK Negative Acknowledgement (CC) ACK Acknowledge (CC) SYN Synchronous Idle (CC) BEL Bell ETB End of Transmission Block (CC) BS Backspace (FE) CAN Cancel HT Horizontal Tabulation (FE) EM End of Medium LF Line Feed (FE) SUB Substitute VT Vertical Tabulation (FE) ESC Escape FF Form Feed (FE) FS File Separator (IS) -> CR Carriage Return (FE) GS Group Separator (IS) SO Shift Out RS Record Separator (IS) SI Shift In US Unit Separator (IS) DEL Delete (CC) = Communication Control (FE) = Format Effector (IS) = Information Separator Figure D.1 Special Graphic Characters -------------------------- -> SP Space -> < Less Than -> ! Exclamation Point -> = Equals -> " Quotation Marks -> > Greater Than -> # Number Sign -> ? Question Mark -> $ Dollar Sign -> @ Commercial At -> % Percent -> [ Opening Bracket -> & Ampersand \ Reverse Slant -> ' Apostrophe -> ] Closing Bracket -> ( Opening Parenthesis ^ Circumflex -> ) Closing Parenthesis _ Underline -> * Asterisk ` Grave Accent -> + Plus { Opening Brace -> , Comma | Vertical Line (This graphic is -> - Hyphen (Minus) sometimes stylized to -> . Period (Decimal Point) distinguish it from the -> / Slant unbroken Logical OR which is -> : Colon not an ASCII character) -> ; Semicolon } Closing Brace ~ Tilde NOTE: Characters marked "->" correspond to the PET ASCII character set. Figure D.2 :225: Appendix E Exercises for Alternative Commodore Machines :226: From Chapter 6 ============== VIC-20 (Unexpanded) Version --------------------------- We write the BASIC program as follows: 100 V%=0 110 FOR J=1 TO 5 120 INPUT "VALUE";V% 130 SYS ++++ 140 PRINT "TIMES TEN =";V% 150 NEXT J Plan to start the machine language program at around 4097+127, or 4224 (hexadecimal 1080). On that basis, we may change line 130 to SYS 4224. Do not try to run the program yet. .A 1080 LDY #$02 .A 1082 LDA ($2D),Y .A 1084 STA $033C .A 1087 STA $033E .A 108A LDY #$03 .A 108C LDA ($2D),Y .A 108E STA $033D .A 1091 STA $033F .A 1094 ASL $033D .A 1097 ROL $033C .A 109A ASL $033D .A 109D ROL $033C .A 10A0 CLC .A 10A1 LDA $033D .A 10A4 ADC $033F .A 10A7 STA $033D .A 10AA LDA $033C .A 10AD ADC $033E .A 10B0 STA $033C .A 10B3 ASL $033D .A 10B6 ROL $033C .A 10B9 LDY #$02 .A 10BB LDA $033C .A 10BE STA ($2D),Y .A 10C0 LDY #$03 .A 10C2 LDA $033D .A 10C5 STA ($2D),Y .A 10C7 RTS :227: To change the start-of-variables pointer to a location above the machine language program, display the SOV pointer with .M 002D 002E and change the pointer to .:002D C8 10 .. .. .. .. .. .. PET/CBM Version --------------- We write the BASIC program as follows: 100 V%=0 110 FOR J=1 TO 5 120 INPUT "VALUE";V% 130 SYS ++++ 140 PRINT "TIMES TEN =";V% 150 NEXT J Plan to start the machine language program at around 1025+127, or 1152 (hexadecimal 480). On that basis, we may change line 130 to SYS 1152. Do not try to run the program yet. .A 0480 LDY #$02 .A 0482 LDA ($2A),Y .A 0484 STA $033C .A 0487 STA $033E .A 048A LDY #$03 .A 048C LDA ($2A),Y .A 048E STA $033D .A 0491 STA $033F .A 0494 ASL $033D .A 0497 ROL $033C .A 049A ASL $033D .A 049D ROL $033C .A 04A0 CLC .A 04A1 LDA $033D .A 04A4 ADC $033F .A 04A7 STA $033D .A 04AA LDA $033C .A 04AD ADC $033E .A 04B0 STA $033C .A 04B3 ASL $033D .A 04B6 ROL $033C .A 04B9 LDY #$02 .A 04BB LDA $033C .A 04BE STA ($2A),Y .A 04C0 LDY #$03 .A 04C2 LDA $033D .A 04C5 STA ($2A),Y .A 04C7 RTS :228: To change the start-of-variables pointer to a location above the machine language program, display the SOV pointer with .M 002A 002B and change the pointer to .:002D C8 04 .. .. .. .. .. .. From Chapter 7: An Interrupt Project ===================================== VIC-20 (Unexpanded) Version --------------------------- The only difference with the VIC-20 is that the screen is located at $1E00: .A 033C LDA $91 .A 033E STA $1E00 .A 0341 JMP ($03A0) To place the link address into $03A0/1: .A 0344 LDA $0314 .A 0347 STA $03A0 .A 034A LDA $0315 .A 034D STA $03A1 To fire up the program: .A 0350 SEI .A 0351 LDA #$3C .A 0353 STA $0314 .A 0356 LDA #$03 .A 0358 STA $0315 .A 035B CLI .A 035C RTS To restore the original interrupt: .A 035D SEI .A 035E LDA $03A0 .A 0361 STA $0314 .A 0364 LDA $03A1 .A 0367 STA $0315 .A 036A CLI .A 036B RTS :229: SYS 836 will invoke the new interrupt code; SYS 861 will turn it off. As with the Commodore 64, there is a possibility of the character printing white-on-white, so that it cannot be seen. PET/CBM Version --------------- This version is not for original ROM machines, which have the IRQ vector located at address $0219/A: .A 033C LDA $9B .A 033E STA $8000 .A 0341 JMP ($03A0) To place the link address into $03A0/1: .A 0344 LDA $0090 .A 0347 STA $03A0 .A 034A LDA $0091 .A 034D STA $03A1 To fire up the program: .A 0350 SEI .A 0351 LDA #$3C .A 0353 STA $0090 .A 0356 LDA #$03 .A 0358 STA $0091 .A 035B CLI .A 035C RTS To restore the original interrupt: .A 035D SEI .A 035E LDA $03A0 .A 0361 STA $0090 .A 0364 LDA $03A1 .A 0367 STA $0091 .A 036A CLI .A 036B RTS SYS 836 will invoke the new interrupt code; SYS 861 will turn it off. Since the PET/CBM does not have colors, the characters will always show. :230: Project: Adding a Command ========================== PET/CBM Version --------------- It's not possible to write a comparable program to add a command to the PET/CBM. This machine doesn't have a "link" neatly waiting for us at address $0308/9. Equivalent code would need to be somewhat longer and less elegant. The equivalent program for PET/CBM won't be given here. It would involve writing over part of the CHRGET program (at $0070 to $0087), supplying replacement code for the part we have destroyed, and then adding the new features. :231: Appendix F Floating Point Representation :232: Packed: 5 bytes (as found in variable or array) +--------+ +--------+--------+--------+--------+ |eeeeeeee| |smmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm| +--------+ +--------+--------+--------+--------+ Zero Flag/ Mantissa (value) Exponent 4 bytes High bit represents sign of mantissa Unpacked: 6 bytes (as found in floating point accumulators) +--------+ +--------+--------+--------+--------+ +--------+ |eeeeeeee| |1mmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm| |sxxxxxxx| +--------+ +--------+--------+--------+--------+ +--------+ Zero Flag/ Mantissa (value) Sign (High Exponent 4 bytes Order Bit High bit represents sign of mantissa Only) - If exponent = 0, the whole number = 0 - If exponent > $80, the decimal point is to be set as many places to the right as the exponent exceeds $80. - Example: Exponent: $83, mantissa: 11000000... binary. Set the point three positions over: 110.000... to give a value of 6. - If exponent <= $80, the number is a fraction less than 1. Exercise: Represent +27 in Floating Point 27 decimal = 11011 binary; mantissa = 11011000... the point is to be positioned 5 places in (11011.000...) so we get: Exponent: $85 Mantissa: %11011000 00000000 00000000 00000000 $D8 00 00 00 To pack, we replace the first bit of the mantissa with a sign bit (0 for positive) and arrive at: 85 58 00 00 00 :233: Appendix G Uncrashing It's best to write a program that doesn't fail (or "crash"). Not all of us succeed in doing this. If a program gives us trouble, it should be tested using breakpoint techniques. The BRK (break) instruction is inserted at several strategic points within the program. The program stops (or "breaks") at these points, and the programmer has an opportunity to confirm correct behavior of the program at selected points. Using this technique, a fault can be pinned down quite closely. Occasionally, usually because of bad planning, a program crashes and the cause of the crash cannot be identified. Worse still, a lengthy program crashes and the user has forgotten to save a copy of it; the user is then faced with the task of putting it in all over again. In such cases, uncrashing techniques are sometimes available to bring the computer back from limbo. They are never entirely satisfactory, and should be thought of as a last resort. The technique differs from computer to computer. :234: PET/CBM ------- Original ROM PETs cannot be uncrashed. Subsequent models can be uncrashed, though hardware additions are necessary. The reader should find someone with computer hardware knowledge to assist in fitting the switches to the computer. A toggle switch is needed, to be connected to the "diagnostic sense" line of the parallel user port; that's pin 5 of the PUP. The other side of the toggle switch should connect to ground (pin 12). Additionally, a momentary pushbutton is required. This must connect the reset line of the computer to ground. Technically speaking, it's better to trigger the input of the computer's power-on reset chip (a 555 one-shot), using a resistor to guard against accidentally grounding a live circuit. To uncrash, set the toggle switch to "on" and press the pushbutton; the machine will come back to life in the machine language monitor. Set the toggle switch off. There's more work to do. The computer is still in an unstable state. To correct this, either of two actions may be taken. You may return to BASIC with .X and immediately give the command CLR; alternatively, you may type .; followed by the RETURN key. Whatever investigation or other action is needed should be performed quickly and the computer reset to its normal state. VIC/Commodore 64 ---------------- You might try holding down the RUN/STOP key and tapping the RESTORE key to see if that will bring the machine to its senses. Otherwise, you must do a more serious reset. You must depend on the fact that the computer does a nondestructive memory test during reset. There are various commercially available interfaces for the cartridge port--usually "mother boards" that are fitted with reset switches. When the reset switch is pressed, the computer starts from the beginning; but memory is not disturbed. If you have logged the entry location of the machine language monitor, you can bring it back with the appropriate SYS command. :235: Commodore PLUS/4 ---------------- There's a reset button next to the power switch. Before you press it, hold down the RUN/STOP and CTRL keys. Now press the reset button and you'll find yourself in the machine language monitor. :236: :237: Appendix H Supermon Instructions Program Supermon is not a monitor; it is a monitor generator that will make a machine language monitor for you. There's a reason for this. Supermon finds a likely spot in memory and then plunks the MLM there so as to fit it into the most suitable place. Load Supermon and say RUN. It will write an MLM for you, and call it up. Now, exit back to BASIC and command NEW. You do not want the MLM builder any more (it's done the job) and you do not want the danger of building two--or more--MLM's. Get rid of the generator program. Any time you need to use the MLM, give SYS4 or SYS8, as appropriate. Supermon contains the following "essential" commands: .R - to display (and change) registers .M - to display (and change) memory .S - to save memory to disk or tape .L - to load from disk or tape .G - go to a ML program .X - to exit to BASIC Supermon also contains the following extra commands: .A - to assemble .D - to disassemble :238: Most versions of Supermon (not the "do-it-yourself" below [which is omitted -wf]) contain the following commands. Though not used by this book, they are useful: .F - fills memory with fixed constants .F 1800 18FF 00 .H - hunts for a memory pattern: .H 0800 1800 20 D2 FF .T - transfers a block of memory to a new location .T 0800 0BFF 8000 A few versions of Supermon contain the command .I which causes machine language single stepping. A Do-It-Yourself Supermon ------------------------- [I am NOT about to type in 6 pages of raw numbers. Many monitors are available for download from the internet, and many options are available for transferring files from more internet-enabled platforms to the C64. -wf] :239: :240: :241: :242: :243: :244: :245: Appendix I IA Chip Information The following material has been adapted from manufacturer's specifications. The information is not essential to machine language programming, but can be a great help for further study. Some of these specifications are not widely published and contain "hard to get" information. 6520 PIA, peripheral interface adapter 6522 VIA, versatile interface adapter 6525 TPA, tri port adapter 6526 CIA, complex interface adapter 6545 CRTC, CRT controller 6560 VIC, video interface chip 6566 VIC-2, video interface chip 6581 SID, sound interface device [Essentially manufacturer's specs, less hardware details] :246: 6520 Peripheral interface Adapter (PIA) ======================================= The 6520 is an I/O device which acts as an interface between the microprocessor and peripherals such as printers, displays, keyboards, etc. The prime function of the 6520 is to respond to stimulus from each of the two worlds it is serving. On the one side, the 6520 is interfacing with peripherals via two eight-bit bi-directional peripheral data ports. On the other side, the device interfaces with the microprocessor through an eight-bit data bus. In addition to the lines described above, the 6520 provides four interrupt input/peripheral control lines and the logic necessary for simple, effective control of peripheral interrupts. | +---------+ ----------- | | |/ CONTROL \ | |\ / | | ----------- +---------+ ---------- | | ----------- | |/ 8 BIT \| |/ 8 BIT \ | |\ DATA BUS /| |\ DATA PORT / PERIPHERAL | MICRO | ---------- | | ----------- DEVICES-- |PROCESSOR| | 6520 | PRINTERS, | 650x | ---------- | | ----------- DISPLAYS, | |/ CONTROL \| |/ 8 BIT \ ETC. | |\ /| |\ DATA PORT / +---------+ ---------- | | ----------- | | ----------- | |/ CONTROL \ | | |\ / +---------+ ----------- | Figure I.1 The functional configuration of the 6520 is programmed by the microprocessor during systems initialization. Each of the peripheral data lines is programmed to act as an input or output and each of the four control/interrupt lines may be programmed for one of four possible control modes. This allows a high degree of flexibility in the overall operation of the interface. Data Input Register ------------------- When a microprocessor writes data into the 6520, the data which appears on the data bus is latched into the Data Input Register. It is then transferred into one of six internal register of the 6520. This assures that the data on the peripheral output lines will not "glitch," i.e., the output lines will make smooth transitions from high to low or from low to high and the voltage will remain stable except when it is going to the opposite polarity. :247: Control Registers (CRA and CRB) ------------------------------- Figure I.2 illustrates the bit designation and functions in the Control Registers. The Control Registers allow the microprocessor to control the operation of the interrupt lines (CA1, CA2, CB1, CB2), and peripheral control lines (CA2, CB2). A single bit in each register controls the addressing of the Data Direction Registers (DDRA, DDRB) and the Output Registers (ORA, ORB) discussed below. In addition, two bits (bit 6 and 7) are provided in each control register to indicate the status of the interrupt input lines (CA1, CA2, CB1, CB2). These interrupt status bits (/IRQA, /IRQB) are normally interrogated by the microprocessor during the interrupt service program to determine the source of an active interrupt. These are the interrupt lines which drive the interrupt input (/IRQ, /NMI) of the microprocessor. The other bits in CRA and CRB are as described in the discussion of the interface to the peripheral device 7 6 5 4 3 2 1 0 +------+------+------+------+------+------+------+------+ CRA | IRQ | IRQ | CA2 CONTROL | DDRA | CA1 CONTROL | | A1 | A2 | |ACCESS| | +------+------+------+------+------+------+------+------+ +------+------+------+------+------+------+------+------+ CRB | IRQ | IRQ | CA2 CONTROL | DDRB | CB1 CONTROL | | B1 | B2 | |ACCESS| | +------+------+------+------+------+------+------+------+ Figure I.2 The various bits in the control registers will be accessed many times during a program to allow the processor to enable or disable interrupts, change operating modes, etc. as required by the peripheral device being controlled. Data Direction Registers (DDRA, DDRB) ------------------------------------- The Data Direction Registers allow the processor to program each line in the 8-bit Peripheral I/O port to act as either an input or an output. Each bit in DDRA controls the corresponding line in the Peripheral A port and each bit in DDRB controls the corresponding line in the Peripheral B port. Placing a "0" in the Data Direction Register causes the corresponding Peripheral I/O line to act as an input. A "1" causes it to act as an output. The Data Direction Registers are normally programmed only during the system initialization routine which is performed in response to a Reset signal. However, the contents of these registers can be altered during system operation. This allows very convenient control of some peripheral devices such as keyboards. Peripheral Output Registers (ORA, ORB) -------------------------------------- The Peripheral Output Registers store the output data which appears on the Peripheral I/O port. Writing a "0" into a bit in ORA causes the corresponding line on the Peripheral A port to go low (< 0.4V) if that line is programmed to act as an output. A "1" causes the corresponding output to go high. The lines of the Peripheral B port are controlled by ORB in the same manner. :248: Interrupt Status Control ------------------------ The four interrupt/peripheral control lines (CA1, CA2, CB1, CB2) are controlled by the Interrupt Status Control (A,B). This logic interprets the contents of the corresponding Control Register, detects active transitions on the interrupt inputs and performs those operations necessary to assure proper operation of these four peripheral interface lines. Reset (/RES) ------------ The active low Reset line resets the contents of all 6520 registers to a logic zero. This line can be used as a power-on reset or as a master reset during system operation. Interrupt Request Line (/IRQA, /IRQB) ------------------------------------- The active low Interrupt Request lines (/IRQA and /IRQB) act to interrupt the microprocessor either directly or through external interrupt priority circuitry. Each Interrupt Request line has two interrupt flag bits which can cause the Interrupt Request line to go low. These flags are bits 6 and 7 in the two Control Registers. These flags act as the link between the peripheral interrupt signals and the microprocessor interrupt inputs. Each flag has a corresponding interrupt disable bit which allow the processor to enable or disable the interrupt from each of the four interrupt inputs (CA1, CA2, CB1, CB2). The four interrupt flags are set by active transitions of the signal on the interrupt input (CA1, CA2, CB1, CB2). Controlling this active transition is discussed in the next section. Control of /IRQA ---------------- Control Register A bit 7 is always set by an active transition of the CA1 interrupt input signal. Interrupting from this flag can be disabled by setting bit 0 in the Control Register A (CRA) to a logic 0. Likewise, Control Register A bit 6 can be set by an active transition of the CA2 interrupt input signal. Interrupting from this flag can be disabled by setting bit 3 in the Control Register to a logic 0. Both bit 6 and bit 7 in CRA are reset by a "Read Peripheral Output Register A" operation. This is defined as an operation in which the processor reads the Peripheral A I/O port. :249: Control of /IRQB ---------------- Control of /IRQB is performed in exactly the same manner as that described above for /IRQA. Bit 7 in CRB is set by an active transition on CB1; interrupting from this flag is controlled by CRB bit 0. Likewise, bit 6 in CRB is set by an active transition on CB2; interrupting from this flag is controlled by CRB bit 3. Also, both bit 6 and bit 7 are reset by a "Read Peripheral B Output Register" operation. Summary ------- /IRQA goes low when CRA-7=1 and CRA-0=1 or when CRA-6=1 and CRA-3=1. /IRQB goes low when CRB-7=1 and CRB-0=1 or when CRB-6=1 and CRB-3=1. It should be stressed at this point that the flags act as the link between the peripheral interrupt signal and the processor interrupt inputs. The interrupt disable bits allow the processor to control the interrupt function. Peripheral I/O Ports -------------------- Each of the Peripheral I/O lines can be programmed to act as an input or an output. This is accomplished by setting a "1" in the corresponding bit in the Data Direction Register for those lines which are to act as outputs. A "0" in a bit of the Data Direction Register causes the corresponding Peripheral I/O lines to act as an input. Interrupt Input/Peripheral Control Lines (CA1, CA2, CB1, CB2) ------------------------------------------------------------- The four interrupt input/peripheral control lines provide a number of special peripheral control functions. These lines greatly enhance the power of the two general purpose interface ports (PA0-PA7, PB0-PB7). Peripheral A Interrupt Input/Peripheral Control Lines (CA1, CA2) ---------------------------------------------------------------- CA1 is an interrupt input only. An active transition of the signal on this input will set bit 7 of the Control Register A to a logic 1. The active transition can be programmed by the microprocessor by setting a "0" in bit 1 of the CRA if the interrupt flag (bit 7 of CRA) is to be set on a negative transition of the CA1 signal or a "1" if it is to be set on a positive transition. :250: Setting the interrupt flag will interrupt the processor through /IRQA if bit 0 of CRA is a 1 as described previously. CA2 can act as a totally independent interrupt input or as a peripheral control output. As an input (CRA, bit 5=0) it acts to set the interrupt flag, bit 6 of CRA, to a logic 1 on the active transition selected by bit 4 of CRA. These control register bits and interrupt inputs serve the same basic function as that described above for CA1. The input signal sets the interrupt flag which serves as the link between the peripheral device and the processor interrupt structure. The interrupt disable bit allows the processor to exercise control over the system interrupts. In the Output mode (CRA, bit 51), CA2 can operate independently to generate a simple pulse each time the microprocessor reads the data on the Peripheral A I/O port. This mode is selected by setting CRA, bit 4 to a "0" and CRA, bit 3 to a "1". This pulse output can be used to control the counters, shift registers, etc. which make sequential data available on the Peripheral input lines. A second output mode allows CA2 to be used in conjunction with CA1 to "handshake" between the processor and the peripheral device. On the A side, this technique allows positive control of data transfers from the peripheral device into the microprocessor. The CA1 input signals the processor that data is available by interrupting the processor. The processor reads the data and sets CA2 low. This signals the peripheral device that it can make new data available. The final output mode can be selected by setting bit 4 of CRA to a 1. In this mode, CA2 is a simple peripheral control output which can be set high or low by setting bit 3 of CRA to a 1 or a 0 respectively. Peripheral B Interrupt Input/Peripheral Control Lines (CB1, CB2) ---------------------------------------------------------------- CB1 operates as an interrupt input only in the same manner as CA1. Bit 7 of CRB is set by the active transition selected by bit 0 of CRB. Likewise, the CB2 input mode operates exactly the same as the CA2 input modes. The CB2 output modes, CRB, bit 5=1, differ somewhat from those of CA2. The pulse output occurs when the processor writes data into the Peripheral B Output Register. Also, the "handshaking" operates on data transfers from the processor into the peripheral device. :251: 6545-1 CRT Controller (CRTC) ============================ Concept ------- The 6545-1 is a CRT Controller intended to provide capability for interfacing the 6500 microprocessor family to CRT or TV-type raster scan displays. Register Map ------------ +----+----------------------+---------------+---+------------------------+ |Reg#|Register Name |Stored Info |R W| 7 6 5 4 3 2 1 0 | +----+----------------------+---------------+---+------------------------+ |R0 |Horiz Total |# Chars |n Y| . . . . . . . . | |R1 |Horiz Displayed |# Chars |n Y| . . . . . . . . | |R2 |Horiz Sync Position |# Chars |n Y| . . . . . . . . | |R3 |VSYNC, HSYNC Widths |# Scan Lines & |n Y| V3 V2 V1 V0 H3 H2 H1 H0| | | |# Char Times | | | +----+----------------------+---------------+---+------------------------+ |R4 |Vert Total |# Char Rows |n Y| x . . . . . . . | |R5 |Vert Total Adjust |# Scan Lines |n Y| x x x . . . . . | |R6 |Vert Displayed |# Char Rows |n Y| x . . . . . . . | |R7 |Vert Sync Position |# Char Rows |n Y| x . . . . . . . | +----+----------------------+---------------+---+------------------------+ |R8 |Mode Control | |n Y| . . . . . . . . | |R9 |Scan Line |# Scan Lines |n Y| x x x . . . . . | |R10 |Cursor Start |Scan Line # |n Y| x B1 B0 . . . . . | |R11 |Cursor End |Scan Line # |n Y| x x x . . . . . | |R12 |Display Start Addr (H)| |n Y| x x . . . . . . | |R13 |Display Start Addr (L)| |n Y| . . . . . . . . | |R14 |Cursor Position (H) | |Y Y| x x . . . . . . | |R15 |Cursor Position (L) | |Y Y| . . . . . . . . | |R16 |Light Pen Reg (H) | |Y n| x x . . . . . . | |R17 |Light Pen Reg (L) | |Y n| . . . . . . . . | +----+----------------------+---------------+---+------------------------+ Notes: . Designates binary bit x Designates unused bit. Reading this bit is always "0", except for R31, which does not drive the data bus at all, and for CS "1" which operates likewise. Horizontal Total (R0) --------------------- This 8-bit register contains the total of displayed and non-displayed characters, minus one, per horizontal line. The frequency of HSYNC is thus determined by this register. Horizontal Displayed (R1) ------------------------- This 8-bit register contains the number of displayed characters per horizontal line. Horizontal Sync Position (R2) ----------------------------- This 8-bit register contains the position of the HSYNC on the horizontal line, in terms of the character location number on the line. The position of the HSYNC determines the left-to-right location of the displayed text on the video screen. In this way, the side margins are adjusted. :252: Horizontal and Vertical SYNC Widths (R3) ---------------------------------------- This 8-bit register contains the widths of both HSYNC and VSYNC, as follows: +---+---+---+---+---+---+---+---+ Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +---+---+---+---+---+---+---+---+ | | | | | | | | Value 8 4 2 1 8 4 2 1 \------+------/ \------+------/ | | VSYNC WIDTH* HSYNC WIDTH (NUMBER OF (NUMBER OF SCAN LINES) CHARACTER CLOCK TIMES) *IF BITS 4-7 ARE ALL "0" THEN VSYNC WILL BE 16 SCAN LINES WIDE Control of these parameters allows the 6545-1 to be interfaced to a variety of CRT monitors, since the HSYNC and VSYNC timing signals may be accommodated without the use of external one-shot timing. Vertical Total (R4) ------------------- The Vertical Total Register is a 7-bit register containing the total number of character rows in a frame, minus one. This register, along with R5, determines the overall frame rate, which should be close to the line frequency to ensure flicker-free appearance. If the frame time is adjusted to be longer than the period of the line frequency, then /RES may be used to provide absolute synchronism. Vertical Total Adjust (R5) -------------------------- The Vertical Total Adjust Register is a 5-bit write only register containing the number of additional scan lines needed to complete an entire frame scan and is intended as a fine adjustment for the video frame time. Vertical Displayed (R6) ----------------------- This 7-bit register contains the number of displayed character rows in each frame. In this way, the vertical size of the displayed text is determined. :253: :254: Vertical Sync Position (R7) --------------------------- This 7-bit register is used to select the character row time at which the VSYNC pulse is desired to occur and, thus, is used to position the displayed text in the vertical direction. Mode Control (R8) ----------------- This register is used to select the operating modes of the 6545-1 and is outlines as follows: +-+-+-+-+-+-+-+-+ |7|6|5|4|3|2|1|0| +-+-+-+-+-+-+-+-+ | | | | | | | | | | | | | | `+' | | | | | | | | | | | | | `-------INTERFACE MODE CONTROL | | | | | | | | | | | | BIT 1 BIT 0 OPERATION | | | | | | ---------------------- | | | | | | x 0 non interlace | | | | | | x 1 invalid (do not use) | | | | | | | | | | | | | | | | | `----------VIDEO DISPLAY RAM ADDRESSING | | | | | "0" for straight binary | | | | | "1" for row/column | | | | | | | | | `------------MUST SET TO "0" | | | | | | | `--------------DISPLAY ENABLE SKEW | | | "0" for no delay | | | "1" to delay display enable | | | one character time | | | | | `----------------CURSOR SKEW | | "0" for no delay | | "1" to delay cursor one | | character time | | | `---------------. | +--NOT USED `-----------------' Figure I.3 Scan Line (R9) -------------- This 5-bit register contains the number of scan lines per character row, including spacing. :255: Cursor Start (R10) and Cursor End (R11) --------------------------------------- These 5-bit registers select the starting and ending scan lines for the cursor. In addition, bits 5 and 6 of R10 are used to select the cursor mode, as follows: +-------+--------------------------+ | BIT | CURSOR MODE | | 6 5 | | +---+---+--------------------------+ | 0 | 0 | No Blinking | | 0 | 1 | No Cursor | | 1 | 0 | Blink at 1/16 field rate | | 1 | 1 | Blink at 1/32 field rate | +---+---+--------------------------+ Note that the ability to program both the start and end scan line for the cursor enables either block cursor or underline to be accommodated. Registers R14 and R15 are used to control the character position of the cursor over the entire 16K address field. Display Start Address High (R12) and Low (R13) ---------------------------------------------- These registers together comprise a 14-bit register whose contents is the memory address of the first character of the displayed scan (the character on the top left of the video display, as in Figure 1). Subsequent memory addresses are generated by the 6545-1 as a result of CCLK input pulses. Scrolling of the display is accomplished by changing R12 and R13 to the memory address associated with the first character of the desired line of text to be displayed first. Entire pages of text may be scrolled or changed as well via R12 and R13. Cursor Position High (R14) and Low (R15) ---------------------------------------- These registers together comprise a 14-bit register whose contents is the memory address of the current cursor position. When the video display scan counter (MA lines) matches the contents of this register, and when the scan line counter (RA lines) falls within the bounds set by R10 and R11, then the CURSOR output becomes active. Bit 5 of the Mode Control Register (R8) may be used to delay the CURSOR output by a full CCLK time to accommodate slow access memories. LPEN High (R16) and Low (R17) ----------------------------- These registers together comprise a 14-bit register whose contents is the light pen strobe position, in terms of the video display address at which the strobe occurred. When the LPEN input changes from low to high, then, on the next negative-going edge of CCLK, the contents of the internal scan counter is stored in registers R16 and R17. :256: 6560 (VIC) Video Interface Chip =============================== The 6560 Video Interface Chip (VIC) is designed for color video graphics applications such as low cost CRT terminals, biomedical monitors, control system displays and arcade or home video games. It provides all of the circuitry necessary for generating color programmable character graphics with high screen resolution. VIC also incorporates sound effects and A/D converters for use in a video game environment. Features -------- o Fully expandable system with a 16Kbyte address space o System uses industry standard 8 bit wide ROMs and 4 bit wide RAMs o Mask programmable sync generation, NTSC-6560, PAL-6561 o On-chip color generation (16 colors) o Up to 600 independently programmable and movable background locations on a standard TV o Screen centering capability o Screen grid size up to 192 Horizontal by 200 Vertical dots o Two selectable graphic character sizes o On-chip sound system including: a) Three independent, programmable tone generators b) White noise generator c) Amplitude modulator o Two on-chip 8-bit A/D converters o On-chip DMA and address generation o No CPU wait states or screen hash during screen refresh o Interlaced/Non-Interlaced switch o 16 addressable control registers o Light gun/pen for target games o 2 modes of color operation Register Map ------------ +------+-----------------------+----------+ | Loc | START VALUE-5K VIC | Bit | | Hex | Binary | Decimal | Function | +------+----------+------------+----------+ | 9000 | 00000101 | 5 | ABBBBBBB | | 9001 | 00011001 | 25 | CCCCCCCC | | 9002 | 10010110 | 150 | HDDDDDDD | | 9003 | v0101110 |