@L}5 _$% l0$)$$Hȱ$ UhL" `e$$%`$%`  R@P!( L(1   Y I`  d  Ld M * @  $ % CC$$)%1 Udߥ$9%: !0 S$% DD˙`  }J)Lr d M * @  $ % CC$$)%1 Udߥ$9%: !0 S$%} DD˙`  }J)Lr J  ((  p L ()   J}L= ( L 0q A    IB JC;? D W } LL  ` W )LA!  ߰")-݆ p" } $G@LL 08`Q")<2Q0 -G$Ș݆ UL# ; p8(()(0ʥ)NQ` }$GȘ݆LU )L ݆ L GȘ ݆LL )W>Z   HH)H }p h  hyhy D L> L JJ    ! LA*` BF }7'8  M HN H` 8 Z  \LdJJ!"! GFE@F (!L }EE !E^ ^ E E7EȩEdE/EȩE  D } .L }  ;F d  ;?F7F? ( .   Z D LL d } . D  L    p  E` , d)  D L) 0BM݊L݉} ML  N݆ L NLML [ TEqEHȱEqEh 0Gȹ G} HLL GɛL  LFREE SECTORS G) *Gȩ GȽG GȌ*jj >G} C8jJ3j2CD( C202C ԠBX` N 1? l LlD:RAMDISK}.COMLu L1 L ;LHL  T`  `8  ɐ     `TU  } L ? .  t`GBJ ~DEHI B V0dV!}QDEHI VF9 ,0 ,0 s0hhL  L` H hDHEh"}DEL8HI4 0 HI,0 0  9 .G VLO#},0 L4*IJ`llD1:AUTORUN.SYSNEED MEM.SAV TO LOAD THIS FILE.D8:MEM.SAV J y08 B|DEHI$} V0 0`B;DEL`?<0LV`@ʆ v s? F0Ξ05: [ BDEHI%} VY8 B V  @  /DE `E:D8:DUP.SYSERROR-SAVING USER MEMORY ON DISKTYPE Y TO &}STILL RUN DOS B;DE J  (` 9 V⪍ ઍ  -'}LLu ÝDEHILV 9 .l 9 .l  `` s$B BH(}I|DE V BLV nB,DE JLV B V BLVDEIʩ BꭝLu } 3E:}DISK OPERATING SYSTEM II VERSION COPYRIGHT 1984 ATARI CORP.A. DISK DIRECTORY I. FORMAT DISKB. RUN CARTRIDG*}E J. DUPLICATE DISKC. COPY FILE K. BINARY SAVED. DELETE FILE(S) L. BINARY LOADE. RENAME FILE M. RUN AT ADDRES+}SF. LOCK FILE N. CREATE MEM.SAVG. UNLOCK FILE O. DUPLICATE FILEH. WRITE DOS FILES P. FORMAT SINGLEL !N',}#"&))9(&*)/h)''-&؆莟R'S  vL/ˢ L }Insert DOS 2.0s, type Y Λx -}DEfHI 1莏#q! @ y0ɛ8A0,' ȅ 1 1ild! 1L!NO SUCH ITEMSELECT.} ITEM OR FOR MENU! 0 .z:*{}.|{ 1 0 0JB 18L%|DL/}%DIRECTORY--SEARCH SPEC,LIST FILE?[# 0 0 &|D3" 1L!NOT A DISK FILEN !B 1L!E# 1 !BD0}ED:}:1BJ|DE 1DEBHI 1 h0ߢ 0.1}  0?詛 1 y0YЛ 1 ;#L" ;#L! BL1TYPE "Y" TO DELETE...DELETE FILE SPEC2}COPY--FROM, TO?OPTION NOT ALLOWED134 FREE SECTORS COPYING---D2:HELP4TH.TXTl# 0|D .L/%#3}##JB|DE 1BHID#E 1#0: B 1L!#͑### B 1#c$0SY4}S1}:## # # .#Ƚ# # 𩛙## 1,#PD#ELJ- <.BJD#E 5}1 1HH 0hh|DL%1}:̳# L% #D#EL% 1 0 . .0O% 1L!WILD CARDS NOT A6}LLOWED IN DESTINATION 0 <.|K}N 2 FORMAT. t* 5) 1L!`) 0NΞ 0 L1) 1 L!BAD LOAD FILELOAD FROM WHAT FILE?) 0 ?}0#B 1L!WHAT FILE TO LOCK?) 0 0$B 1L!WHAT FILE TO UNLOCK?DUP DISK-SOURCE,DEST DRIVES?TYPE "Y" IF OK TO US@}E PROGRAM AREACAUTION: A "Y" INVALIDATES MEM.SAV.FE! +L1   `*  70 2 2A} 0.* 1 y0 0)INSERT BOTH DISKS, TYPE RETURN^, 1 y038逍 N, 1L! ,B}C, t*  Lx+, 0 ^, 1 y0 , ,0,0 ,L+ ,I0 ,Vǭ0C}Ξ, 0 }, 1 y0C,ШC, 0K'!" H H 'h h Lx+!EF 5L1L!D,I,HhD}` NOT ENOUGH ROOMINSERT SOURCE DISK,TYPE RETURNINSERT DESTINATION DISK,TYPE RETURNE}`  `8 rL1`-* 1P* 1 y0Y`hhL!NAME OF FILE TO MOVE?- 0 0|DL% <.F},^ 1 70 0 .@L# .BJ 1  DEHIB V L1 ,} 1 70,L.  G}JB|,#P#DE 1 HI BDEHHII 1 B 1 ,^ 1 70,0La- B V,#PH},^ 1 70 0L#L!-* 1P* 1 y0Yj383}mm ݭI}}`8}``|* ? ɛ,`|:-)| / 1L!`DESTINATION CANT BE DOJ}S.SYS0 0H{ 24Δ 28/L!/) 2 Π 2 0 ξK}hAΞB,0 J 1 BDEHI,HÝDE 1HIHIDELSAVE-GIVE L}FILE,START,END(,INIT,RUN)O S0 1`BDEPHI V` S0H 1 L!M}0 0 1L~0`PLEASE TYPE 1 LETTER,0`hhL! 70 1L0L<1 ,;ɛ7,"ɛ:ݦ1ݥN}A"D|ݤD|ȩ:|ȩ|ɛ,,(/+.ީ1 1,ɛ`轤{NAMEO} TOO LONG B VL!` L1I H1EΝDL1|mDiE` V0`8d/8 i:222 1 LP}!ERROR- 128ɛ+,' 20*.. өr2 1``2TOO MANY DIGITSINVALID HEXAQ}DECIMAL PARAMETER800 0 8 00`,0'D800 H,ɛh`2L1NEED D1 THRU D8uR} ECIMAL PARAMETER800 0 8 00`,0'D800 H,ɛh`2L1NEED D1 THRU D8u05  15 2 2151^116115ύ011$r2ԭ5 3)5)5ԭx GT}@@L00)+& 2 L0=ɛ -L!1LV1L1 /3 3ȹ441L-4 v3 25 2H 2h͔5U}L3L3 2 k3U hh`DOC`QL3P1Lk0S1H /3h0B k3@ VL0LU!#$53 1181118V}1111Ε5 1Lk0552 1Ȍ1i111i11115Lk0pppB4f5&&&&&&&&^6f^6&&&&&W}&&A1@BJ D2ELVK@BHILV^Щ6 2BD2EJ VBD5EHI0 V0%5X}5 2iХiL[2 25 25`D:*.*` i@`8 ``@i `8@`0 BLV525<4Y}5`HH i ԍΗ552 ԍhh@ Lح1Э1Ѣ 24.4 24Z}4`D4E` 2BJ k3LVRH` 2BD4EhK)I JLV333 BASIC languag i}e OBJ > MACHINE language DOC > DOCumentation PIC|MIC|FNT|MVM > Graphics AMS|MUS|MBD > Music LST j} > BASIC LISTing -Some MACHINE language programs can only be run by booting the disk MENU "without" BASIC! -For s k}ome programs, XL/XE models may require a "TRANSLATOR" program to be booted first! [Also available from BELLCOM] - l}Most programs are relatively self-explanatory; however, some have separate DOCumentation files available. Some BASIC m} programs may contain instructions in their program listings. -To use this great MENU program on your own disks, n}simply copy the file "AUTORUN.SYS" onto your disk. * MENU written by DAVID CASTELL * program on your own disks, ^ ** ASSEMBLY LANGAGE ** BY CHRIS CRAWFORD INTRO BY LARRY FRIED************************************************p}************* This approach to assembly language is meant to give the reader a practical basic concept of what assembly lanq}guage is and how it relates to the atari computer. Chris Crawford is a self taught programmer that was interested in developr}ing a computer wargame (s) simulation.Here he approaches learning assembly language in an informal light-hearted way with pes}rsonal comments and suggestions, thruout the technical material. The material is covered in 8 sections consisting of these t}headings LESSON1.TXT: WHY LEARN ASSEMBLY LANGUAGE? AN INTRODUCTION.----------------------------------- LESSON2.TXT: NUu}MBER SYSTEMS AND 6502 ARITHMETIC.----------------------------------- LESSON3.TXT: LOGIC AND BOOLEAN ALGEBRA (IF AND THEN)v}----------------------------------- LESSON4.TXT: BRANCHING.----------------------------------- LESSON5.TXT: INDEXING ANDw} LOOPING.----------------------------------- LESSON6.TXT: SUB-ROUTINES AND STACK MANIPULATION.---------------------------x}-------- LESSON7.TXT: INTERUPTS AND USE.----------------------------------- LESSON8.TXT: SUMMARY AND COMMENTS/SUGGESTIONy}S.-----------------------------------E.----------------------------------- LESSON8.TXT: SUMMARY AND COMMENTS/SUGGESTION( *** ASSEMBLY LANGUAGE *** BY CHRIS CRAWFORD************************************************************* {} LESSON ONE - 5/28/85 WHY LEARN ASSEMBLY LANGUAGE? Assembly language is the great barrier that divides the p|}rofessional programmer from the amateur. It is the most powerful language available for a microcomputer. There are four re}}asons for learning to program in assembly language. First, the speed of execution of assembly language is very high -- about~} ten time higher than BASIC on the average, perhaps a thousand times faster on certain operations. Even ACTION, the fastest h}igh-level language, is only about half as fast as assembly language. Second, assembly language tends to be more compact th}an many languages. Again, ACTION! provides a good comparison. Code produced by ACTION! is about twice as large as equivalent} assembly language. The third reason to program in assembly language is that assembly gives you access to features of the }machine that simply are not available in high-level languages. Interrupts are the most notable examples. Finally, the most} important reason for learning to program in assembly language is that it will help you to understand the machine better. An}d that is a very good place to begin, for you cannot learn assembly language unlessyou know a little bit about computers. } HOW COMPUTERS WORK I am now going to describe how computers work, in very rough terms. Computers operate on a hierarch}y of concepts that spans a great range, rather like the hierarchy that starts with protons and electrons, moves through atoms}, molecules, cells, people to civilizations. A civilization is composed of protons and electrons, but to understand how it} is so composed one must know a great deal about the intermediate steps. So too is a computer composed of transistors. There} are four intermediate steps between the transistor and the computer. A transistor is an electrically operated switch. We }can assemble transistors into gates that will turn circuits on or off depending on the states of other circuits. There are a} variety of gates reflecting the various Boolean operations: AND, OR, NOT, NAND, NOR and EOR. Gates can be assembled into }latches, decoders, and adders. A latch is the simplest memory element: it remembers one bit of information. A decoder trans}lates a number encoded in binary form on a few wires into a selection of one of many wires. An adder will add two one-bit va}lues, with a carry, and generate a carry of its own. We can next broaden each of these devices into an eight-bit device by} simply slinging the devices side by side. Eight one-bit latches slung side-by-side give one byte of RAM. Eight adders make }an eight-bit adder. We can thus create a RAM module by building many butes of RAM. We access this RAM module with three b}uses: a data bus, an address bus, and a control bus. The data bus carries information between the central processing unit and} the RAM module. The address bus is sixteen bits wide; a decoder in the RAM module takes the numeric value on the address }bus and decodes it to select the single byte of RAM that is indicated by the address. The control bus establishes the directi}on of the data flow on the data bus and the timing of data transfer. The central processing unit (CPU) represents the high}est intellectual level of the computer. It is composed of four parts: the Arithmetic and Logic Unit (ALU), the registers, th}e address bus controller, and the instruction decoder. The ALU is composed of adders and gate arrays that crunch numbers. Th}e particular device to use is selected with a decoder. The registers are simply on-board RAM. The address bus controller }is a device that puts the desired RAM address onto the address bus. The real heart of the CPU is the instruction decoder, a v}ery complex decoder that takes the program instructions out of RAM and translates them into action. It does this by feeding t}he instructions (which are numbers) into decoder circuits that activate the desired gateways in the CPU. PROGRAMMING A }MICROPROCESSOR Machine code is nothing more than a bunch of numbers that mean something to the CPU. It's hard to work wit}h pure numbers, so we use a little code that makes it easier for us to understand the codes that the computer uses. This pro}grammer-friendlier code is called assembly language, It is a direct, one-to-one translation of machine code.Here is an exam}ple of the two side by side: Machine Code Assembly Language A9 05 LDA #FINGERS 133 $9C STA COUNT The code} on the right may not look very readable, but you must agree, it's far more readable than the code on the left. And they bot}h mean exactly the same thing. Unfortunately, the computer cannot read the assembly code, only the machine code. Therefor}e, we need a translator program that will translate the easier-to-understand code on the right into the impossible-to-underst}and code on the left. This translator program is called an assembler. A program that goes in the reverse direction, trans}lating machine code to assembly, is called disassembler. It may seem like a bother to go through all the hassle of using an }assembler, but it is actually much easier. Assembly language is not only more readable than machine code, but it is also a}ssembly-time relocatable; this means you can move it around in RAM freely before you start the assembly process. A good asse}mbler also offers a number of extra features that make it easier to keep track of your program or modify it quickly. US}ING AN ASSEMBLER There are three steps involved in writing an assembly language program: editing, assembling, and debuggin}g. Editing is the process of typing in your assembly language statements. Assembling is the invocation of the assembler. Debu}gging is the process of running your program and analyzing why it doesn't work. Thus, the entire process of writing an assem}bly-language process can be described by a fictitious BASIC program: FOR 1= 1 to 1,000,000,000... EDIT PROGRAM ASSEMBLE PR}OGRAM DEBUG PROGRAM NEXT 1 THE 6502 MICROPROCESSOR The first item in the 6502 that I will describe is the accumulato}r. This is a single one-byte register in the 6502. It is the central workbench of the microprocessor; almost everything hap}pens in the accumulator. Your first three instructions on the 6502 are: LDA address (Load the Accumulator with the} contents of address) This instruction loads the accumulator with the contents of the memory location specified by the val}ue of address. The address can be specified by either an outright value, such as $0600, or a symbolic reference, such as FIS}H, where the value of FISH has been previously declared by, say, an ORG statement or an equate statement. LDA #value } (Load the Accumulator with value) This is much like the earlier statment; it loads the accumulator with a number, only t}he number loaded is specified immediately rather than stored in a memory location. Thus, the command LDA # 9 will put a 9 int}o the accumulator. STA address (Store the Accumulator into address) This command will store the contents of the }accumulator into the RAM location whose address is specified in the command. It is just like the first command, except that }the direction of data motion is reversed. The LDA command is like a read, which the STA is like a write. You are now equi}pped to move data around inside the computer. These commands will allow you to read data from one area of memory and store i}t into another. LDA and STA are the two most common instructions used in any 6502 program. Exercise: Write a program that w}ill read the contents of address $FE00 and store the result into addres $680. Your biggest problem here will be just getting }your assembler to work. Therefore, I will give the answer away: ROMADD ORG $FE00 RAMADD ORG $680 ORG $600 L}DA ROMADD STA RAMADD BRK END That's the program. Try to get it running with your assembler. Lx ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD************************************************************* } LESSON #2 NUMBER SYSTEMS In this lecture I will take up the problem of arithmetic on the 6502. I choose this topic onl}y because it is fairly simple to do on the 6502. There are a couple of nerve-jangling problems associated with 6502 arithm}etic, but I will breeze over those in a very cavalier fashion. Before we can do arithmetic, though, you must know a little} bit about number systems. There are three that you must know: decimal, binary, and hexadecimal. Decimal is the standard }number system that you have used since grade school. You count 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, and then you reach 0 again, }so you put down a 1 in the tens place and resume counting from 0. Binary works the same way, except that there are only tw}o digits, not ten. The two digits are 0 and 1. You count 0, then 1, then you reach 0 again, so you put down a 1 in the twos }place and resume counting from 0. Thus, counting from 0 to ten in binary like this: Decimal Binary 0 0 1 } 1 2 10 3 11 4 100 5 101 6 110 7 111 8} 1000 9 1001 10 1010 In binary, instead of having ones, tens, and hundreds places, we have }ones, twos, fours and eights places. It takes a lot more digits to express a number in binary, but then again, we have only }the two numberals 0 and 1 to work with, so what does one expect? The hexadecimal number system is a base-16 system. In this }system, you count from 0 to 16 like so 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10. The 10 in hexadecimal really means 16 in decimal. }So 10 is 16, right? Black is white, truth is likes....stay with assembly language long enough and you'll believe anything. } Actually, it's easy to avoid confusion. We use little prefixes to tell you and the computer whether a number is expressed }in decimal, binary, or hexadecimal. No prefix means decimal. A $ prefix means hexadecimal; a % means binary. Thus %10 mean}s 2 while $10 means 16, but 10 means just plain old 10. Hexadecimal is not hard to learn at all; if you go into any store you} will see that they use hexadecimal on all their signs.ADDITION AND SUBTRACTION Addition with the 6502 is very simple; it} uses the ADC instruction. This instruction means "Add with Carry"; I'll get to the Carry part in just a moment. For now, le}t me explain the instruction. The ADC instruction has an operand, normally a location in memory. When the instruction is exe}cuted, it takes the contents of that memory location and adds that value to the value in the accumulator. It leaves the su}m of the two numbers in the accumulator. This of course destorys the old value in the acucmulator. You can use the immediat}e mode of addressing with the ADC instruction, in which case it adds the value itself. Thus, "ADC # 9" will add a 9 to the c}ontents of the accumulator, while "ADC FISH" will add the contents of address FISH to the accumulator. Subtraction is just} like addition. The instruction to use is SBC, which means "Subtract Borrowing Carry". Again, I'll tell you about the Carry p}art in a moment. This instruction subtracts the operand from the contents of the accumulator, leaving the reslt in the accumu}lator, It also can be done in either immediate mode (e.g. SBC#5) or absolute mode (e.g., SBC GOAT).WORD LENGTH PROBLEMS I}f that were all there were to arithmetic with the 6502, programmers would be paid a lot less. The first killer problem is th}at the 6502 uses 8-bit words; that is, the numbers that the 6502 stores and works with are only 8 bits wide. This means that} the biggest number the 6502 can comprehend is 255. Uh-oh! What happens if you want to have a checkbook balancing program and} you have more than $255? What happens if you get more than 255 points in your "Decapitate the Orphans" game? In fact, what }happens if you just ignore the limit and add, say, 10 to 250? Well, believe it or not, the 6502 will give you an answer of} 4. Why? The number system that the 6502 uses is like a wheel, with 0 at the top, counting clockwise 1,2,3,...all the way up} to 255, which lies right next to the 0. If you go up from 255 you just wrap around past the 0 and start all over. Similarly,} if you subtract 2 from 0, you'll get 254. The solution to all this is provided by the Carry bit, discussion of which I've} been putting off unitl now. The Carry bit is a flag that the 6502 uses to remember when it has done arithmetic that carried} it over the boundary between 0 and 255. By using it properly, you can solve your arithmetic problems. The first trick to} using the Carry bit is to use multi-byte words. This means that, instead of using a single byte to store a number, you use }several. For example, if you use two bytes to remember a number, you can store a number as large as 65,535. three bytes lets} you to to 16,777,215. Four bytes ets you go to 4,294,967,295. big enough for you? To use multi-byte arithmetic, you set} up a series of additions of subtractions. Suppose, for example, that you want to add two two-byte words. The program fragme}nt to do this would look like this: LDA LOFISH CLC ADC LOGOAT STA LOANSR LDA HIFISH ADC HIGOAT STA} HIANSR This little fragment of code assumes that the first two-byte value is called (LOGOAT, HIGOAT), and that the , }HIANSR). The new instruction, CLC, stands for "Clear Carry" and it means that the Carry bit should be set to 0. It should al}ways be used with all additions except chained additions like this one. The code does the following: first it adds the two} low values. If the addition resulted in a wraparound (result greater than 255), then the Carry bit was set; otherwise, it w}as cleared. Then it performed the second addition, adding in the value of the Carry bit (That's why we call it "Add with Ca}rry"). Thus, if a wraparound occurred, an additional one was added into the high sum. This system insures that multi-byte a}ddition works properly. For subtraction, you use the SEC instruction ("Set Carry"). Otherwise, you handle subtraction the} same way that you handle addition. In both addition and subraction, though, the low bytes must be handled first, then the hi}gher bytes in the proper order (lower to higher).DECIMAL & SIGNED ARITHMETIC There are two variations on standard 6502 ar}ithmetic. Both are so rarely used that I will not treat them here. The first is decimal arithmetic using the Decimal flag. T}his allows you to set up an automatic decimal adjust mode. This is useful in certain types of arithmetic, decimal adjust mod}e. This is useful in certain types of arithmetic, primarily BCD arithmetic. If you don't know what this is, don't bother }with the Decimal flag. Your program should always begin with the instruction CLD, which means "Clear Decimal Flag". I will }tell you this just once: failure to clear the decimal flag is the source of the most frustrating and impossible-to-trace bug }in the 6502. Every single program should start with the instruction CLD. The second arcane bit of 6502 arithmetic is sign}ed arithemetic. It uses the V flag ("oVerflow"). Signed arithmetic is always confusing and seldom useful. In 7 years of wor}king with the 6502,I have never had need of it. Don't bother.LIMITATIONS ON 6502 ARITHEMETIC There are quite a few limit}ations on 6502 arithmetic. There is no facility for multiplication and division; you have to wirte subroutines to do that. Y}ou must design your programs to make do with 8-bit words; failing in that, you must use multi-byte arithemetic, with its cons}equent price in speed and TAM. All in all, arithmetic is a real pain on the 6502. This is the major reason why most 6502 pro}grams do so little arithmetic.l in all, arithmetic is a real pain on the 6502. This is the major reason why most 6502 pro! ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD************************************************************* } LESSON THREE: LOGICBOOLEAN LOGIC A great deal of programming involves the use of Boolean logic. This is a stan}dardized system for handling logical manipulations. It's sort of like algebra for logic. You must understand Boolean logic }if you are to write assembly language programs, so let's get started. Where algebra deals with numbers, Boolean logic deal}s with propositions. A proposition is just a statement such as "Fred eats worms." It can take only two possible values -- Tr}ue or False. In our programs we seldom bother with broad and glorious propositions such as "Love is the universal language of} truth" or "War is the extension of policy by other means". Instead, we normally deal with propositions such as "The joystic}k trigger has been pressed," or "There is a diskette in the disk drive." When we use Boolean logic with a computer, we may} think in terms of true and false, but the computer is actually working with 1's and 0's. We use the following convention: a} 1 corresponds to a Boolean value of "true", while a 0 correspons to a Boolean "false". Using this system we can represent} propositions inside the computer. However, programming requires more than the mere representation of data; we must also be a}ble to manipulate that data. This brings us to the Boolean operators. There are four common Boolean operations necessary for }most programming practices: Not This is the simplest of Boolean operators. It takes a single Boolean value as an input} and produces as its output the logical converse of the input. Thus, a true input yields output, while a false input generat }es a true input. Or This Boolean operator takes two Boolean values as its input and generates a single Boolean value as } its output. The value of the output depends on the values of the inputs according to the following rule: If one input is tr }ue OR the other value is true, then the output is true. Otherwise, the output is false. And This Boolean operator is j }ust like the or-operator, except that it uses a different rule. Its rule is: If one input is true AND the other input is tru }e, then the output is true; otherwise the output is false. Exclusive-Or This Boolean operator is just like the or-opera}tor, except that its rule is: If one input is true, OR the other input is true, BUT not both are true, then the output is tru}e; otherwise, the output is false. When we use the 6502 for Boolean operations, you must remember that the operations are }eight bits wide. Instead of working with one bit at a time, we use all eight bits of a word in parallel. The bits in a byte} are independent and do not affect each other in any way -- at least as far as Boolean operations are concerned. The 6502 }has three instructions for performing Boolean operations. These are AND, EOR, and ORA. The first performs an and-operation.} For example, consider the following code: LDA FISH AND GOATThis will first Load the accumulator with the v}alue of FISH. It will then And the contents of the accumulator with the contents of GOAT. The result of the and-operation wi}ll be left in the accumulator. The AND-instruction can use an immediate operand if you desire, just as the ADC-instruction} can. The EOR-instruction provides the exclusive-or operator. It works just like the AND-instruction. The ORA instruction} provides the or-operator in just the same way. If you wish to obtain the NOT-operation, just use EOR #$FF; this will inve}rt each bit in the accumulator. Because NOT is so easily reproduced with EOR, there is no special NOT instruction in the 6502}.APPLICATIONS OF BOOLEAN LOGIC If you have any sense at all, you are probably asking, "What good is all this Boolean nons}ense? What would I use it for?" Four applications are available: Program Logic Many times our programs encounter rathe}r complex logical situations. The program must be able to load a file; if the FMS is in place and there is a diskette in the} disk drive, and the diskette has the file we are looking for, or the file specification calls fora cassette load, then we w}ill load the program. Many programming problems involve such Boolean operations, Keeping them straight is certainly a headac}he. Masking Bits Sometimes we need to isolate particular bits in a byte. For example, in Eastern Front (1941) I used t}he character value to store the unit type. The color of the unit was encoded in the upper two bits of the byte, the type in t }he lower six bits. If I wanted to get only the unit type, I had to mask out the upper two bits. This I did with the followin!}g code fragment: LDA UNITCODE AND #$3F The AND-instruction eliminated the upper two bits, leaving me with ju"}st the unit type. Bit-masking like this is useful in many situations. We use it frequently when we pack bits into a byte to#} save memory. It is also handy with input handling. If you want to read the joystick port, you frequently mask out the bits $}in turn to see which is active. By the way, you mask out bits set to 1 with the AND-instruction. You mask out bits set to%} 0 with the ORA instruction. The logic is reversed. Setting and Clearing Individual Bits We also use the AND and ORA i&}nstructions to set or clear individual bits within a byte. This is most often useful for handling arrays of flag bits.Foldi'}ng Bytes Together This little fragment of code will fold bytes together: LDA FISH EOR GOAT AND MASK (} EOR GOAT STA ANSWER This is a magical piece of code. See if you can figure out what it does. Experiment with)} two values of MASK: $OF and $FO.SHIFT AND ROTATE INSTRUCTIONS The 6502 also has instructions that allow you to shift the*} bits around inside a byte. The first of these are the shift instructions. One, ASL, shifts a byte to the left; the other, +}LSR, shifts a byte to the right. Thus, the byte %01101011, when shifted left, becomes %11010110. Each bit is shifted one po,}sition to the left. The leftmost bit is rudely pushed right out of the byte and falls away ("Aaaaaaaaarrrrrggggg!"). A zero-} is shifted into the rightmost bit. The LSR instruction does the same thing in the opposite direction. Note that ASL also.} doubles the value of the byte, while LSR halves it. Two ASL's multiply by four; three multiply by eight. This makes it eas/}y to do simple multiplication, but be careful with round-off error here. What happens if you try to multiply by 256? What d0}o you get if you halve 3? A variation on the shift instructions are the rotate instructions. There are two: rotate left (1}ROL) and rotate right (ROR). These function just like the shift instructions, except that the bit that gets shoved into the 2}bottom is not necessarily a zero; it is the contents of the Carry bit. The bit that gets pushed off the edge of the byte goes3} into the Carry bit, so it is not lost. Thus, if you rotate either way nine times, you'll be right back where you started. 4} Rotate instructions are a handy way to get a particular bit into the carry bit where you can work on it. Conversely, once yo5}u get your desired bit into the carry bit the way you want it, you can put it back into a byte with some rotate instructions.6} INCREMENT AND DECREMENT INSTRUCTIONS The last instructions I will cover are the increment and decrement instructions. 7}These allow you to add one (increment) or subtract one (decrement) from a memory location. These are not considered to be ar8}ithmetic operations so they do not affect the Carry flag, nor are they affected by it. You cannot increment or decrement t9}he accumulator, only RAM location.ffect the Carry flag, nor are they affected by it. You cannot increment or decrement t$ ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD************************************************************* !;}LESSON FOUR: BRANCHING One of the most important ideas in computing is the concept of conditional execution. This is the a!<}bility of the program to execute different routines depending on conditions at the time of execution. The significance of !=}this capability is best realized by considering how programs would operate in its absence. A program without conditional exec!>}ution would not be able to change its program flow in response to conditions. In other words, it would always execute exac!?}tly the same code in exactly the same order. Every run of the program would follow exactly the same sequence and perform exa!@}ctly the same operations. Not very interesting, right? To get a grip on conditional execution, we need to look at it in it!A}s simplest expression. The simplest type of conditional execution is binary in nature. We have a chunk of code; the 6502 wi!B}ll either execute it or it will not execute it. The decision is made on the basis of a boolean value; a true value will tell!C} us to execute the chunk, while a false value will tell the 6502 not to execute the chunk. The basic mechanism for doing t!D}his is through an instruction that performs a transfer of control. This involves nothing more than altering the program coun!E}ter. You may recall that the program counter is a register in the 6502 that points to the address of the currently executed i!F}nstruction. When that instruction has been executed, the program counter is increased by the length of the instruction (1,!G}2, or 3 bytes, depending on the instruction). It now points to the next instruction. This little system allows the 6502 to!H} step through a program in sequence. But there are also instructions that will alter the value of the program counter, alo!I}wing the 6502 to jump to another area of memory and another part of the program. The simplest of these is the JMP instructio!J}n. It takes the form JMP LABEL. This loads the value of the LABEL into the program counter. Its effect is to make the 650!K}2 jump to the address of LABEL and continue execution from there. It is directly analagous to a GOTO instruction in Basic. !L} For conditional execution we need something more. We need the 6502 to have capability to make a binary decision based on a !M}binary value. The solution used by the 6502 involves flags. These are single-bit Boolean values stored together in a single !N}byteof the 6502 called the processor status register (SR). The status register is eight bits wide but stores only seven f!O}lags. These seven flags are labelled N,V, B, D, I, Z, and C. You have aready encountered the C (Carry) flag and the D (Deci!P}mal) flag. In this chapter, we are concerned only with the N, V, Z, and C flags. The magic instruction that makes possibl!Q}e conditional execution can take many forms. Its general form is Bfv LABEL. The B stands for "branch". The "f" stands for a!R} flag, and the "v" stands for the value of the flag, either true or false. However, in this case, we do not use the terminolo!S}gy "true or false". Instead we use the terms "set" or "clear". "Set" means the same thing as "1" or "true", while "clear"!T} means "0" or "false". The label is the address to which the 6502 should branch if the condition is satisfied. If the condi!U}tion is not satisfied, then the 6502 will simply skip this branch instruction and go to the following instruction. For exa!V}mple, suppose that we have the following instruction sequence: LDA #0 BCS KARELIA !W} LDA #5 KARELIA STA FISH This will first load the accumulator with a zero. Then the 6502 encounters the BC!X}S ("Branch on Carry Set") instruction. It looks at the Carry flag. If this flag is set then the 6502 will indeed branch to !Y}the label KARELIA. (For all you geography buffs, Karelia used to be in Finland.) In other words, if the Carry flag is set, t!Z}he 6502 will skip over the LDA #5 instruction. Thus, a zero will be stored into FISH. However, if the Carry flag is clear![}, then the 6502 will not take the branch. It will instead continue executing the next instruction, which will load a 5 into t!\}he accumulator. Then it will come to the label KARELIA and store that 5 into FISH. Thus, the value of the Carry flag determi!]}nes whether a zero or a five is stored into FISH. The converse of BCS is BCC ("Branch on Carry Clear"). This will cause th!^}e 6502 to take the branch if the Carry flag is clear. There is also a pair of similar instructions for the V-flag. These !_}are BVS and BVC. They will cause the 6502 to branch on the value of the V-flag. Now the situation gets unncessarily confu!`}sing. The istructions for the Z-flag should be BZS and BZC -- "Branch on Z Set" and "Branch on Z Clear". Unfortunately, the !a}dumb designer of the 6502 thought he would get cute at this point, so instead he called these instructions BEQ and BNE, for "!b}Branch on Equal" and "Branch on Not Equal". He never mentioned what he thought is supposed to be equal to what. We're stuck!c} with it, so make the best of it. Just remember what these instructions really mean BZS and BZC. If you think in terms of!d} the Z-flag, it will work out just fine. If you try to think in terms of equal or not equal, your attention will be diverted!e} from the real truth of the matter and you may make mistakes. So keep your eye on the ball and think in terms of Z! The ne!f}xt pair of branch instructions use the N-flag. These are even more insidious than the previous two. They are called BMI and !g}BPL, meaning "Branch on Minus" and "Branch on Plus". At first glance, these appear to be reasonable substitutions for BNS !q}UB%DOS SYSB*)DUP SYSB SAUTORUN SYSB_HELP DOCB oPREFACE TXTBAzLESSON1 TXTB@LESSON2 TXTB?LESSON3 TXTB?:LESSON4 TXTBDLESSON5 TXTB8LESSON6 TXTB@LESSON7 TXTB=>LESSON8 TXTand BNC. After all, if you load the accumulator with a signed number, and the number is negative, then the N-flag will be se!r}t, while if the number is positive, the N-flag will be clear. Thus, it would seem that BMI is truly equivalent to BNS and !s}BPL is truly equivalent to BNC. This is the source of many a bug in beginner's programs. Consider the following fragment of !t}code: LDA FISH SEC SBC BOAT BPL POSANSR This code is supposed to branch to POSANSR if FISH is greater than GOAT.!u} And indeed, if FISH is greater than GOAT, then when you subtract GOAT from FISH, you will get a positive result, right? Not!v} necessarily! Suppose, for example, that the value in FISH is $C1 and the value in GOAT is 1. When the 6502 subtracts GOA!w}T from FISH, it will get a result of $CO. Note that the highest bit of $CO is set to 1. This is the value that will go into!x} the N-flag. In other words, even though FISH is greater than GOAT, the 6502 will not take the branch, and this code will fa!y}il. The moral of his tale is, don't take those instructions literally. They are misleadingly named. When you see BPL, do!z}n't think "Branch on Plus", think "Branch on N Clear". Otherwise, you'll screw up someday. By the way, the correct branch!{} to use in the above problem is BCS. Now for a catch with the branch instructions. A JMP instruction is a simple absolute!|} jump -- you specify the target address and it goes there. The designers of the 6502 realized that the vast majority of bran!}}ch instructions only go a short distance They therefore decided to implement the branch instruction as a relative branch. !~} The machine code doesn't specify the target of the branch, it only specifies an offset. In other words, instead of saying, !}"jump there", it says, "jump so many bytes forward or backward." The allowable range is 126 bytes forward or backward. Thus!}, you can't branch anywhere you want, only to nearby locations, If you must branch further, reverse the logic of the branch !}and use the branch to skip over a JMP statement.y locations, If you must branch further, reverse the logic of the branch 3 ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD************************************************************* %} LESSON FIVE:INDEX REGISTERS & LOOPING We are now going to expand the model of the 6502 that you have been using. Until %}now, the 6502 I have described had nothing more than a status register, program counter, and accumulator. Now I am going to %}reveal the existence of two new registers in the 6502: the X- and Y-registers. These two registers are eight-bit registers%} just like the accumulator. You can load numbers into them and store them out just as you can with the accumulator. You canno%}t do arithmetic or Boolean operations with them as you can with the accumulator. But you can do a number of very special thi%}ngs that greatly increase the power of the 6502. Let's start with the simple move instructions. The first are LDX and LDY,%} which load the X- and Y-registers the same way that LDA loads the accumulator. Then there are STX and STY, which store the X%}- and Y-registers the same way that STA stores the accumulator. There are also four commands for transferring bytes between %}registers; these are TAX (transfer A to X), TAY (transfer A to Y), TXA (transfer X to A), and TYA (transfer Y to A). Then %}there are four special instructions that you will use very often. These are INX and INY, which increment (add one to) the X- %}and Y-registers, and DEX and DEY, which decrement (subtract one from) the X-and Y-registers. Finally, we have the CPX and %}CPY commands, which compare X or Y with the operand of the instruction. These two instructions operate in exactly the same wa%}y that the CMP instruction works, except that they use the X- and Y-registers instead of the accumulator. What are these t%}wo registers used for? Well, they are sometimes used as temporary registers. If you are in the middle of a lengthy computat%}ion, and you need to save a value currently in the accumulator to make room for something else, the X- and Y-registers are a %}handy place to stuff values away for temporary storage. Programmers do this all the time. However, temporary storage is n%}ot the real purpose and vaue arise from their utility as index registers. Index registers go hand in hand with loops; the be%}st way to show you how they are used is to dump the whole schmeer at once and then explain it. So consider the following p%}roblem: your program has to deal with the possibility of user errors. Suppose you require the user ty type in a file name fo%}r your program to read. What happens if this file is not on the disk? You have to put an error message on the screen that s%}ays, "FILE NOT ON DISK!" How do you print the message? Here's a sample bit of code that will do it: LDX #(ENDMSG-ERRMSG-1)%} LOOP1 LDA ERRMSG,X STA SCREEN,X SEC SBC #$20 DEX BPL LOOP1 JMP ELSWHR ERRMSG DB 'FILE NOT ON DISK1' END%}MSG DS 1 Let's take apart this code and explain it step by step. First thing we do is load the X-register with the numbe%}r of characters (minutes one) in the message. The expression (ENDMSG-ERRMSG-1) will calculate that length at assembly time. %} This turns out to be 17 characters long. If we were pedestrian about it we could have just written LDX #16, but this way, if%} we decide to change the message we don't have to remember to go back and change the LDX command. Neat, huh? OK, so now we%} have a 16 in the X-register. Now the 6502 comes to the next command -- LDA, ERRMSG,X. This command tells it to load the ac%}cumulator with the byte at (address ERRMSG, indexed by X). What this means is as follows: the 6502 will take the address ERR%}MSG and add the value of the x-register to that address. It will then go to the address so calculated and load the accumulat%}or with the contents of that address. Since X contains a 16, the 6502 will go to the 16th byte after the first byte in the t%}able ERRMSG. If you count characters, you will see that the 16th byte is the exclamation point. So the 6502 will load the AS%}CII code for an exclamation point into the accumulator. The next two instructions (SEC, SBC #$20) are necessary to correct%} for the Atari's nonstandard handling of ASCII codes. They make sure that the exclamation will be printed on the screen as a%}n exclamation point. The next instruction (STA SCREEN,X) stores the reslt indexed by X. The 6502 will add the contents of%} X (still 16) to the address SCREEN. It will then store the contents of the accumulator into that address. If that address i%}s part of screen RAM, then you will see an exclamation point appear on the screen. The next instruction that the 6502 enco%}unters is the DEX instruction. This instruction subtracts one from the X-register, making it a 15. Next, the 6502 comes t%}o the instruction BPL, LOOP 1. This will branch if the N-flag is clear. The value of the N-flag is affected by a DEX instruc%}tion. The value of bit D7 of the result is transferred to the N-flag. Bit Dd7 of 15 is a zero, hence the N-flag is clear, he%}nce the 6502 will indeed take the branch. Note that it branches back up to LOOP 1. Now it will repeat the process, only t%}his time X contains a 15, not a 16. It will therefore grab the 15th character, an ASCII 'K', and store that to the screen po%}sition just before the exclamation point. Then it will subtract one form X to get a 14, and will continue the loop. This %}process will continue, with the 6502 grabbing bytes in reverse order form the table and storing them onto the screen, until a%}fter the 6502 does the seroth byte. When X contains a zero, and the 6502 executes a DEX, it obtains the result $FF. This set%}s the N-flag. When the 6502 encounters the BPL command, it will NOT take the branch; instead, it will skip the branch and go %}on to the JMP statement. The loop is terminated. In this one fragment of code you have seen two major ideas: indexed addr%}essing and looping. They are so closely related that it is hard to talk about one without talking about the other. You ca%}n use indexed addressing with either the X-register or the Y-register. You most commonly use indexed addressing with the LDA %}and STA commands, but you can also use it with many of the other 6502 commands: ADC, SBC, CMP, AND, ORA, EOR, LSR, ROR, ADL, %}and ROL can all be used with indexed addressing. Indexed addressing allows you to work with tables or arrays of data. Ther%}e is one ugly catch: all of your arrays must be less than 257 bytes long, because the index registers are only eight bits wid%}e. Most of the time this is not a serious problem. However, if you must address a larger table or array, you can use indirec%}t addressing. To do this, you calculate the address that you desire to access, store that address in two contiguous bytes on %}page zero (low, then high) -- we call these two bytes a pointer -- and then refer to the pointer like so: LDA (POINTER), Y%} This instruction will take the address out of pointer, add the value of Y to it, and load the accumulator with the contents %}of the address so calculated. If POINTER contains $4567 and Y contains a 2, then the 6502 will load the acumulator with the c%}ontents of address $4569. You are still restricted by the size of Y, but you can always go back and change the POINTER if you%} need to span larger arrays. In this case, you frequently just leave Y equal to zero and do all of your indexing directly wi%}th changes to POINTER. The last topic I will take up is termination techniques. Every loop must somehow be terminated if y%}ou are to avoid the problem of the Sorcerer's Apprentice. You will note that the programming example I gave used a rather od%}d approach. I started at the end of the array and worked backwards. Why not start at the beginning and work forwards? It's sl%}ightly more efficient going backwards than forwards. When you go forwards, you have to terminate the loop with: INX CPX %}#17 BNE LOOP1 Whereas when you go backwards, you need only use: DEX BPL LOOP1 Going backwards you save one instr%}uction. However, if this confuses you, feel free to count forward; that works, too, only it's a little less efficient. Th%}ere is also a problem on choosing whether to BNE or BPL. BPL restricts you to a range of only 127 bytes, but BNE, but index %}from ERRMSG-1 and SCREEN-1 instead of ERRMSG and SCREEN. There are lots of other sneaky ways to terminate loops, but they %}fall into advanced topics.stead of ERRMSG and SCREEN. There are lots of other sneaky ways to terminate loops, but they $ ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD*************************************************************LESSON S)}IX: SUBROUTINES & THE STACK We now take up the first topic in this series that is not absolutely essential to writing pro)}grams: The loops and indexed addressing discussed in the previous lecture are truly essential: it is hardly possible to wri)}te a useful program that has no loops. Subroutines are a matter of convenience, not necessity. It is quite possible to wr)}ite an entirely adequate program without using a single subroutine. However, you will find that the convenience of subroutin)}es with large programs is so great that you would never want to write such a program without them. The primary purpose of )}a subroutine is to perform some function that is frequently needed at many points in the program. Instead of having to repet)}itively insert the same code over and over again, we simply write it once, place it in a subroutine, and call that subroutine)} many times from the main program. The use of subroutines dramatically reduces the size of a program. Subroutines are imp)}lemented on the 6502 in a fashion very similar to that used by BASIC. You may recall the two BASIC commands for subroutines:)} "GOSUB lineno" and "RETURN". The two corresponding 6502 commands are "JSR label" and RTS". The label in "JSR label" is the)} label of the beginning of the subroutines. Thus, writing and using subroutines in 6502 is trivially simple. First, you w)}rite the subroutine. You give it a name (say, "MYSUBR") and stick that label in front of the first instruction. You put an )}RTS command after the last normal command of the subroutine. To call the subroutine, you just put JSR MYSUBR. That's all it )}takes! However, in order to understand how it works is not so easy. Here's the problem we must solve when the 6502 jumps )}to a subroutine, the JSR instruction tells it the destination address to which the 6502 must jump. But when the 6502 hits the)} RTS instruction, how does it know the address to which it must return? The RTS doesn't say, "Return to THIS address"; it )}says only "Return". Moreover, how could the 6502 know where to return? If the subroutine can be called from, say, five diffe)}rent points in the program, how would the 6502 know which of those points to which it must return? What if we gave the 650)}2 a special register for remembering return addresses? That is, whenever the 6502 encounters a JSR instruction, it stores the)} current address into its return address register. Then when it encounters an RTS instruction, it simply takes the address o)}ut of the return address register. There is only one problem with this: what if we use nested subroutines (one subroutine )}calls another)? The second subroutine call will erase the return address for the first subroutine call. Trouble! The solut)}ion to all this is called a stack. A stack is a chunk of RAM allocated for certain special operations such as subroutines. )}The 6502 stack is stored on page one -- that is, addresses $0100 to $01FF. The stack operates like 128 return address registe)}rs arranged in sequence (remember: two bytes per address). The 6502 keeps a stack pointer register to keep track of which b)}yte in the stack is currently being used. I will now trace through the operation of the stack in a subroutine. We start wi)}th the stack pointer set equal to $FF. That means that the stack is empty; the stack pointer is at the very top of the stack)}. The 6502 encounters a JSR instruction. It takes the current value of the program counter and breaks it into two bytes. It)} pushes the first byte onto the stack. This means that it stores the first byte at $01FF, then decrements the stack pointer.)} Now the stack pointer is $FE. Then the 6502 then pushes the second byte of the return address onto the stack, storing t)}hat byte at $01FE and decrementing the stack pointer to $FD. Then the 6502 jumps to the subroutine. When it encounters the RT)}S instruction, it pulls the two address bytes off of the stack (increments stack pointers and loads byte at address $0100,SP))}. Those two bytes go directly into the program counter, returning the 6502 to the original entry point. The advantage of )}this approach is that it allows very deep nesting of subroutines. If one subroutine calls another, the 6502 simply stores mo)}re values onto the stack. The addresses won't be confused because you always exit subroutines in exactly the reverse of the )}order that you entered them. You can use the stack yourself, if you wish. You have six instructions that allow you to pla)}y with the stack: PHA, PLA, PHP, PLP, TSX, and TXS. The PHA instruction pushes the value of the accumulator onto the stack)} and decrements the stack pointer. The PLA instruction increments the stack pointer and pulls the current stack value into t)}he accumulator. These two instructions allow you to store and retrieve values onto the stack. They must be exactly balanced)}, though, or you will generate that most feared of bugs, the stack crashes. Consider: you are in a subroutine. You push a )}value onto the stack, but forget to pull it off. When the 6502 attempts to return to its original location, it pulls two add)}ress bytes off the stack -- but they're the wrong two bytes. One of them is the value you pushed but didn't pull. Result: t)}he 6502 return to the wrong address. You program goes haywire and the computer crashes. This is caled a stack crash. This)} type of crash tends to the particularly difficult to recover from. Prevention is the best medicine here. The rule for preve)}nting stack crashes is simple and absolute: each and every push onto the stack must be balanced by one pull from the stack. )} Violate this rule and you will certainly experience a stack crash. The next pair of stack manipulation instructions are P)}HP and PLP. These push and pull the process status register pmtp the stack. They are useful for two purposes. First, you ma)}y wish to save the values of the various flags before performing some operation, then restore them so that you can branch on )}a previously created condition. Second, it is sometimes handy to PHP, than PLA to get the processor status register into the)} accumulatorada where you can more directly manipulate it. Again, each push must be balanced by one pull. The third stack )}manipulation pair of commands do not modify the stack. They are TSX and TXS. These transfer the stack pointer to and from th)}e x-register. Once in the x-register, you can change the value of the stack value and then TXS to jump over sections of the s)}tack. This can be a very handy way to pass parameters to subroutines, but it is also very tricky. If you make a mistake, yo)}u will generate a stack crash. So be careful with this one. I have always avoided these commands like the plague. They are )}very dangerous and never essential.careful with this one. I have always avoided these commands like the plague. They are (& ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD*************************************************************LESSON -}SEVEN: INTERRUPTS We now approach one of the most difficult topics in the world of assembly: interrupts. This is such a .}messy topic that very few high-level languages make any provision for interrupts. Moreover, interrupts are one of the best wa.}ys around crash your program hard. Programmers using interrupts must be very careful. The standard way to handle this prob.}lem is with a technique called polling. Your program runs out every now and then to check whether the high-priority situation.} has arisen. If it has, then the program responds to it. If not, it returns to its original work. The problem with pollin.}g arises from the choice of polling interval. If you choose a long (infrequent) polling interval, then you may not respond t.}o a demand quickly enough. If you choose a short (frequent) polling interval, then you will respond quickly to the demand, b.}ut you will never have any time for your regular computations. You may think this type of situation is infrequent, but I c.}an list quite a few situations where this is fairly common. Most I/O operations involve short bursts of computation at infre.}quent intervals, but they must be attended to on a tight schedule. For example, talking to a cassette deck involves very lit. }tle real work from the CPU, but it must be done according to a precise schedule. Even a disk drive is very slow by the sta. }ndards of a 6502. Or how about keyboard response? When the human operator presses a button, he wants to see response NOW, n. }ot two or three seconds from now. Yet he could press that button at any time. So should your program sit on its hands waiti. }ng for a keypress, or should it ignore the human operator? The solution to all of these problems is the interrupt. An int. }errupt is rather like a subroutine that can be called by a hardware action. There's a wire going into the 6502 called IRQ (I.}nterrupt Request). That wire is normally quiet. But when something important happens, like a keypress, the computer's hardw.}are puts a signal on that wire to interrupt the 6502. Here's what happens next: The 6502 is busy running a program, but w.}hen it gets the interrupt signal it first checks the 1-bit (Interrupt) in the processor status register. If the 1-bit is set.}, it decides to ignore the interrupt, but if it is clear, it proceeds to the next step. It saves the processor status regist.}er and the current value of the program counter onto the stack. Then it loads the program counter with the address stored .}at a special place in ROM -- it's either $FFFC or $FFFE, I can never giet it straight. It thus jumps to the address specifie.}d in ROM. It expects to find an interrupt service routine there, which presumably will deal with the keypress in the appropri.}ate manner. This routine will probably start by pushing A, X. and Y into the stack to preserve them. When done, the routi.}ne will then pull them off the stack and execute an RTI instruction, which causes the 6502 to pull the processor status regis.}ter off the stack, and then pull the program counter off and resume operating. The important thing about this rather compl.}ex sequence is that it allows the 6502 to drop whatever it is doing, service the interrupt, and then return to its earlier fu.}nctioning without skipping a beat. The overriding goal of all this is to be absolutely certain that, when the 6502 reeturns .}from the interrupt, it returns in EXACTLY the same state that it was in when the interrupt hit. Otherwise, all sorts of horr.}ible, untraceable bugs would result. Imagine -- you're in the middle of some huge computation when an interrupt strikes. .}It subtly changes some very tiny parameter, just enough to insure that when the computation resumes, it will be slightly inco.}rrect. When you try to find the bug, you discover that sometimes the code works perfectly and sometimes it fouls up, and you.} can't figure out why it should do that. Very bad business! Moral: interrupts must follow a very tight discipline if they.} are to be of any utility. Now let's get into some of the technical gore involving interrupts. First, there are two interr. }upts on the 6502. They are called IRQ (Interrupt Request) and NMI (Non-Maskable Interrupt). The idea is that the IRQ can be m.!}asked out by setting the 1-bit with the SEI instruction. Then you use the CLI instruction to clear the 1-bit. Thus, IRQ is u."}sed for interrupts that have second priority. NMI is reserved for first-priority interrupts, it is not maskable. However, .#}the designers of the Atari computers routed IRQ and NMI through the POKEY and ANTIC chips respectively. And they put mask re.$}gisters into these chips. Thus, the NMI can be masked out after all -- but only on Atari computers. Other 6502-based compute.%}rs don't allow that. The NMI and IRQ interrupts have separate interrupt vectors in ROM, so they can be treated differently.&}. These vectors route the interrupts to the OS, but the OS is smart enough to route interrupt flow thorugh some RAM locations.'}. This means that you can intercept these two interrupts by altering the contents of the RAM-vectors. (I won't list them he.(}re, there are a number of them for different ituations.) You must be careful, though, when altering such a RAM vector. Wh.)}at happens if an interrupt strikes after you have changed one byte of the address and before you have changed the other byte?.*} The 6502 will fly off into never-never land and you have crashed. Sure, it's unlikely, but good programmers don't count on l.+}uck to make their programs work. You have to guarantee that the interrupt won't occur before you mess with the vector. Use .,}SETVBV from the OS. The two primary applications of interrupts with the Atari computers are for VBIs (Vertical Blank Inter.-}rupts) and DLIs (Display List Interupts). These are very involved topics covered quite thoroughly in th book De Re Atari. V..}BIs are most often used for animation control, input handling, and other time-critical operations. For example, the entire p./}layer I/O of my game Eastern Front (1941) is handled by VBIs. The scrolling, giving of orders, identifying units, and so for.0}th is all done by VBIs. The mainline routine meanwhile figures the artificial intelligence. DLIs are used to enhance the g.1}raphics on the screen. You can get more colors, more use out of players, more scrolling, and more character sets with proper .2}use of DLIs. Again, consult De Re Atari for a full treatment of this complex subject. Interrupts are extremely difficult t.3}o debug because they tend to crash the system when they fail. You must exercise the strictest discipline in writing interrup.4}t code. Timing problems, seldom of concern in mainline programming, can become critical with interrupts. What happens, fo.5}r example, if your interrupt service routine takes so much execution time that more interrupts arrive than you can service? .6}Bad things, I assure you. You must always ask yourself, what happens if an interrupt strikes here? Or there? You must assum.7}e that an interrupt will strike at the worst possible time, and write your code to deal with that possibility. The most im.8}portant discipline to follow in writing interrupt service routines is this: keep your interrupt database separate from your m.9}ainline database. If the ISR can freely write to variables used by the mainline, you will certainly have problems when the ma.:}inline attempts to work with variables whose values change in unpredictable ways. You must set up ironclad rules about when t.;}he ISR can mess with variables used by the mainline, what it can do to them, and how it notifies the mainline routines that i.<}t has indeed altered them. Approach interrupts with extreme caution. They are very powerful, but every programmer can tel.=}l you horror stories about debugging interrupt routines.eme caution. They are very powerful, but every programmer can tel,; ** ASSEMBLY LANGUAGE ** BY CHRIS CRAWFORD************************************************************ LES2?}SON EIGHT: SOME ADVANCED TOPICS We have covered all of the traditional material associated with 6502 assembly language pr2@}ogramming. However, there remain a number of topics that should be addressed before we finish. They are not closely associat2A}ed with each other, so I will take them in random order. The first topic is perhaps the most difficult one for a beginning2B} assembly-language programmer: Where do I begin? How do I put together an entire assembly language project? The problem he2C}re is seldom a technical one. Most beginners are stopped by their own lack of goals rather than any lack of technical expert2D}ise. One does not just write an assembly language program because one knows assembly language -- that is putting the cart be2E}fore the horse. One starts with goals and then considers means. A story from my early days with micros will illustrate thi2F}s point. I did not have anybody to teach me assembly language. I decided in 1976 that I wanted to do wargames on computers.2G} Accordingly, I bought a KIM-1, an early 6502-based single-board computer. I received it in January 1977. I studied the ma2H}nuals and taught myself 6502 machine language. I had my first wargame up and running in six weeks. That means that I not on2I}ly taught myself 6502 in six weeks, but I wrote and debugged a program at the same time. Now, the point of this story is N2J}OT "Wow, isn't Chris Crawford the smartest programmer who ever lived!" The point of this story is that goal-oriented learning2K} is far more effective than goal-less learning. Had I sat in on some technical course on 6502, I would have taken months and2L} months to learn the material. Because I had a clear goal, I learned very quickly. My advice to you, the beginning assemb2M}ly programmer, is this: You have acquainted yourself with the rudiments of 6502 programming. If you have some project you wo2N}uld like to pursue, some goal you would like to achieve, then do it. If not, don't waste your time trying to use a tool for 2O}its own sake. Assuming you pass this first test, there remains the broad problem of organizng your assembly language progr2P}am. I suggest that you break your program up into six modules, each forming a separate source code file. These six modules w2Q}ould be: EQUATES file: this file defines all of the equates used by the program: the data areas, the page zero and page si2R}x usage, and perhaps some of the large graphics and screen structures. DATA file: this file contains all of the static tab2S}les used by the program. This would include all the text messages that would be printed onto the screen, bitmaps of graphics 2T}images, graphics character set definitions, and so forth. INITIALIZATION code: this file contains the routines that initia2U}lize the program when it first fires up. They set up the screen, clear out all the special graphics and sound registers, zero2V} out all the arrays that need to be cleared, and do all the other legwork associated with clearing the decks for a program. 2W} INTERRUPT code: this module contains the code associated with any interrupts used by your program. This would most commonl2X}y involve vertical blank interrupts and display list interrupts. Inasmuch as your interrupts should be well-separated from yo2Y}ur other code, you might as well keep the code in a separate file. MAINLINE code. This includes the main program loop tha2Z}t controls the primary behavior of the program. If you have problems imagining this, think of it as nothing more than a seri2[}es of subroutine calls arranged in a loop, with each subroutine handling one chunk of the overall process. SUBROUTINE code2\}: After a while you build up a collection of subroutines for handling standard processes in the program. Keep them here. 2]} The second topic I would like to talk about is the place of the 6502 in the larger world of microprocessors. The 6502 is un2^}doubtedly the most successful of microprocessors to date, having been installed in more systems than any other microprocessor2_}. It is also a very old microprocessor, having first appeared in 1976. That makes it nine years old. A very simple way t2`}o approach the world of microprocessors is to group them into two sets -- the Sixes and the Eights. The Eights represent the2a} earliest group of microprocessors, they trace their lineage al the way back to the 4004, the first microprocessor. The 40042b} was followed by the 8008, the first eight-bit micorprocessor. The 8008 was superseded by the 8080, which was in turn follow2c}ed by the Z-80. The Z-80 was the most advanced eight-bit processor in the Eights line. The next step was to go to 16 bits w2d}ith the 8088 and 8086. These were followed by the more powerful 80186, 80286, and 80386. The fundamental philosophy of all2e} the Eights can be expressed in two words: features and compatibility. The designers of the Eights were always adding new fea2f}tures to the microprocessors with each successive generation. The goal seemed to be to pack as many bells and whistles in as2g} would fit. The second goal, compatibility, meant compatibility with the previous microprocessor in the series. This insure2h}d that software developed for previous versions would still run on the newer versions. The result of this design philosoph2i}y was a series of powerful microprocessors that were quite complex in layout and rather difficult to learn. The features wer2j}e piled up on each other in a bewildering array. Once you learn the system, it seems natural enough. But it is something of2k} a mess. The Sixes include the 6800, the 6502, the 6809, and the 68000. The two key words guiding the design of the Sixes2l} are cleanliness and speed. The idea was to make the instruction sets clean, powerful, and fast. The hope was that the proc2m}essors would be so easy to learn that compatibility would not be a problem. The design approach was to use just a few simple2n} instructions, but give them variations that greatly extend their power. Thus, the 6502 has a LDA instruction that can be us2o}ed with a great many addressing modes. The 68000 is the 32-bit entry into the Sixes line. It carries the idea of cleanline2p}ss even further than the 6502. The 68000 uses a single instruction with different modes to replace the 6502 instructions LDA,2q} LDX, LDV, STA, STX, STY, TXA, TAX, TYA, PHA and PLA. That's quite a simplification! The 68000 also boasts sixteen registe2r}rs, each 32 bits wide. That's a total of 512 bits of register space, the 6502 has 32 bits of equivalent register space. Thos2s}e sixteen registers eliminate many of the data-shuffling problems so common with the 6502. The 68000 has a linear address 2t}space 24 bits wide -- that's sixteen megabytes! Thus, a 68000 can directly address 16 megabytes of RAM and ROM. The 6502, by 2u}contrast, can only address 64K directly -- it must use paging sytems that slow it down to address more memory. Finally, th2v}e 68000 has a number of advanced capabilities that make possible a number of special capabilities. I will describe just one 2w}stack frames. The 68000 makes it easy to set up a local, temporary stack when you enter a subroutine. Thus, subroutines can 2x}have their own local variables stored on the stack, accessed via a special stack pointer register. The 68000 will manage all2y} the housekeeping necessary to keep such a system straight. Did I mention that 68000 has hardware multiply/divide?*****2z}* THE END ********* CHRIS CRAWFORD ***a system straight. Did I mention that 68000 has hardware multiply/divide?*****0)