Copyright 1984, COMPUTE! Publications, Inc. All rights reserved.

Reproduction or translation of any part of this work beyond that permitted by Sections 107 and 108 of the United States Copyright Act without the permission of the copyright owner is unlawful.

Printed in the United States of America

ISBN 0-942386-53-1

10 987654321

COMPUTE! Publications, Inc., Post Office Box 5406, Greensboro, NC 27403, (919) 275-9809, is one of the ABC Publishing Companies, and is not associated with any manufacturer of personal computers. PET, CBM, VIC-20, and Commodore 64 are all trademarks of Commodore Electronics Limited and/or Commodore Business Machines, Inc. Apple is a trademark of Apple Computer Company. Atari is a trademark of Atari, Inc.

Contents

Preface v

1: How to Use This Book 1

2: Defs:

Equates and Definitions 13

3: Eval:

The Main Loop 27

4: Equate and Array:

Data Base Management 77

5: Openl, Findmn, Getsa, and Valdec:

I/O Management and Number Conversions 103

6: Indisk:

The Main Input Routine 137

7: Math and Printops:

Range Checking and Formatted Output 177

8: Pseudo:

I/O and Linked Files 197

9: Tables:

Data, Messages, Variables 219

10: 6502 Instruction Set 237

11: Modifying LADS

Adding Error Traps, RAM-Based Assembly, and a Disassembler 275

Appendices 353

A: How to Use LADS 355

B: LADS Object Code 357

C: Machine Language Editor for Atari and Commodore 415

D: A Library of Subroutines 433

E: How to Type In Basic Programs 440

Preface

This book shows how to put together a large machine language program. All of the fundamentals were covered in my first book, Machine Language for Beginners. What remains is to put the rules to use by constructing a working program, to take the theory into the field and show how machine language is done.

Showing how to construct an assembler—written entirely in machine language—would serve two useful purposes. It would illustrate advanced programming technique and also provide the reader with a powerful assembler to use in other ML programming.

This book, then, offers the reader both a detailed description of a sophisticated machine language program (the LADS assembler) and an efficient tool, a complete language with which to write other machine language programs. Every line in the LADS assembler program is described. All the subroutines are picked apart and explained. Each major routine is examined in depth.

LADS, the Label Assembler Development System, is a fast, feature-laden assembler—it compares favorably with the best assemblers available commercially. And not the least of its virtues is the fact that few programs you will ever use will be as thoroughly documented and therefore as accessible to your understanding, modification, and customization.

LADS is a learning device too. By exploring the assembler, you will learn how to go about writing your own large machine language (ML) programs. You will see how a data base is created and maintained, how to communicate with peripherals, and how to accomplish many other ML tasks. Also, because you can study the creation of a computer language, the LADS assembler, you will gain an in-depth knowledge of the intimate details of direct communication with your computer.

Most programming involves a tradeoff between three possible objectives: speed, brevity, or clarity. You can program with the goal of creating the fastest running program possible. Or you can try to write a program which uses up as little memory as possible. Or you can try to make the program as understandable as possible, maximizing the readability of the program listing with REMarks.

LADS emphasizes clarity so that its source code will serve as a learning tool and as the focus of this book. It's designed so that important events in the program can be easily explained and understood. Virtually every ML instruction, every tiny step, is commented within the source code listings following each chapter.

This doesn't mean that LADS is flabby or slow. Assembling roughly 1000 bytes a minute and taking up 5K in memory, LADS is considerably faster and more compact than most commercial assemblers. That's because, in ML, you can have the best of both worlds: You can comment as heavily as you want, but the assembler will strip off the comments when it creates the object code. In this way, clarity does not sacrifice memory or speed.

The frequent comments contribute considerably to the educational value of this assembler. Exploring LADS is a way to learn how to achieve many common programming goals and how to construct a large, significant program entirely in ML. An additional advantage of this comprehensibility is that you'll be able to modify LADS to suit yourself: Add your own pseudo-ops, define defaults, format output. All this is referred to as a language's extensibility. We'll get to this in a minute.

What BASIC is to BASIC programming, an assembler is to ML programming. LADS is a complete language. You write programs (source code) which LADS translates into the finished, executable ML (object code). Unlike less advanced assemblers, however, symbolic assemblers such as LADS can be as easy to use as higher level languages like BASIC. The source code is very simple to modify. Variables and subroutines have names. The program can be internally commented with REM-like explanations. Strings are automatic via the .BYTE command. There are a variety of other built-in features, the pseudo-ops, which make it easy to save object programs, control the screen and printer listings, choose hex or decimal disassembly, and service other common programming needs.

Perhaps the best feature of LADS, though, is its extensibility. Because you have the entire source code along with detailed explanations of all the routines, you can customize

LADS to suit yourself. Add as many pseudo-ops as you want. Redesign your ML programming language anytime and for any reason. Using an extensible programming language gives you control not only over the programs you design, but also over the way that they are created. You can adjust your tools to fit your own work style.

Do you often need to subtract hex numbers during assembly? It's easy to stick in a — command. Would you rather that LADS read source programs from RAM memory instead of disk files? (This makes it possible to assemble using a tape drive. It can also be a bit faster.) In Chapter 11 we'll go through the steps necessary to make this and other modifications. You'll be surprised at how easy it is.

Finally, studying the language (the LADS assembler) which produces machine language will significantly deepen your understanding of ML programming.

I would like to thank Charles Brannon for his translation and work with the Atari version of LADS, Kevin Martin for his translation and work with the Apple version, and Todd Heimarck for his many helpful discoveries about the assembler.

Vll

-

r

Chapter 1

How to Use This Book

--

How to Use This Book

The dual nature of this book—it's both a text and a program—offers you a choice. You can follow the ideas: reading through the chapters, studying the program listings, and deepening your understanding of machine language programming.

Alternatively, you can type in the LADS assembler and experiment with it: learning its features, trying out modifications, and using it to write your own machine language programs. Appendix A describes how to use the assembler and Appendix B provides instructions on typing it in. If you choose this second approach, the rest of the book can serve as a reference and a map for modifying the assembler. The tutorials can also help to clarify the structure and purpose of the various subroutines and subprograms.

LADS is nearly 5K long, and for those who prefer not to type it in, it can be purchased on a disk by calling COMPUTE! Publications toll free at 1-800-334-0868. Be sure to state whether you want the Commodore, Atari, or Apple disk. The disk contains both the LADS source and object code (these terms are defined below). To create customized versions of the assembler, you will need the source code. It, too, can be typed in (it is printed in sections at the end of Chapters 2-9). If you don't type in any of the comments, it is roughly 10K long. The Commodore disk contains the various PET/CBM (Upgrade and 4.0 BASIC), VIC, and Commodore 64 versions.

Definitions

There are several concepts and terms which will be important to your understanding of the rest of the book.

ML programming, and programming in general for that matter, is a new discipline, a new art. There are few rules yet and few definitions. Words take on new meanings and are sometimes used haphazardly. For example, the word monitor means two entirely different things in current computerese: (1) a debugging program for machine language work or (2) a special TV designed to receive video signals from a direct video source like a computer.

Since there is no established vocabulary, some programming ideas are described by an imprecise cluster of words. When applied to machine language programming, the terms pointer, variable, register, vector, flag, and constant can all refer

How to Use This Book

to the same thing. There are shades of difference developing which distinguish between these words, but as yet, nothing has really solidified. All these terms refer, in ML parlance, to a byte or two which the programmer sets aside in the source code. In BASIC, all these terms would be covered by the word variable.

Loose Lingo

Purists will argue that each of these words has a distinct, definable meaning. But then purists will always argue. The fact is that computing is still a young discipline and its lingo is still loose.

Some professors of BASIC like to distinguish between variables and constants, the latter meaning unchanging definitions like SCREEN = 1024. The address of the start of screen RAM is not going to vary; it's a constant.

In BASIC, something like SCORE = 10 would be a variable. The score might change and become 20 or whatever. At any rate, the word SCORE will probably vary during the execution of the program. In ML, such a variable would be set up as a two-byte reserved space within the source code:

100 SCORE .BYTE 0 0

Then, anytime you ADC SCORE or ADC SCORE+ 1, you will add to the SCORE. That's a variable. The word pointer refers to those two-byte spaces in zero page which are used by Indirect Y addressing—like LDA (155),Y—and which serve to point to some other address in memory.

Register usually means the X or Y or Accumulator bytes within the 6502 chip itself. As generally used, the word register refers to something hard wired within the computer: a circuit which, like memory, can hold information. It can also refer to a programmer-defined, heavily used, single-byte variable within an ML program:

100 TEMP .BYTE 0

A vector is very much like a pointer. It stores a two-byte address but can also include the JMP instruction, forming a three-byte unit. If you have a series of vectors, it would be called a "jump table," and the Kernal in Commodore computers is such a table:

How to Use This Book

FFD2 JMP $F252 FFD5 JMP $A522 FFD8 JMP $B095

Thus, if you JSR $FFD2, you will bounce off the JMP into $F252, which is a subroutine ending in RTS. The RTS will send you back to your own ML code where you JSRed to the JMP table. That's because JMP leaves no return address, but JSR does.

A flag is a very limited kind of variable: It generally has only two states, on or off. In LADS, PRINTFLAG will send object code (defined below) to the printer if the flag holds any number other than zero. If the PRINTFLAG is down, or off, and holds a zero, nothing is sent to the printer. The word flag comes from the Status Register (a part of the internals of the 6502 chip). The Status Register is one byte, but most of the bits in that byte represent different conditions (the current action in an ML program resulted in a negative, a zero, a carry, an interrupt, decimal mode, or an overflow). The bits in the Status Register byte are, themselves, individual flags. ML programmers, however, usually devote an entire byte to the flags they use in their own programs. Whole bytes are easier to test.

Source code is what you type into the computer as ML instructions and their arguments:

100 *= 864

110 LDA #$0F ; THIS WILL PUT A 15 ($0F) INTO THE

ACCUMULATOR 120 INY ; THIS RAISES THE Y REGISTER

After you type this in, you assemble it by turning control over to the LADS assembler after naming this as the source code. The result of the assembly is the object code. If you have the .S pseudo-op on, causing the object code to print to the screen, you will see:

100 0360 A9 OF LDA #$0F ; THIS WILL PUT A 15 ($0F)

INTO THE ACCUMULATOR

120 0362 C8 INY ; THIS RAISES THE Y

REGISTER

Properly speaking, the object code is the numbers which, taken together, form a runnable ML program. These numbers can be executed by the computer since they are a program. In the example above, the object code is A9 OF C8. That's the computer-understandable version of LDA #$0F: INY. It's gen-

How to Use This Book

erated by the assembler. An assembler translates source code into object code.

A complex assembler like LADS allows the programmer to use labels instead of numbers. This has several advantages. But it does require that the assembler pass through the source code twice. (When an assembler goes through source code, it is called a pass.) The first time through, the assembler just gathers all the label names and assigns a numeric value to each label. Then, the second time through the source code, the assembler can fill in all the labels with the appropriate numbers. It doesn't always know, the first time through, what every label means. Here's why:

100 LDA 4222

110 BEQ NOSCORE

120 JMP SOMESCORE

130 NOSCORE INXJMP CONTINUE

140 SOMESCORE INY

150 CONTINUE LDA 4223

As you can see, the first time the assembler goes through this source code, it will come upon several labels that it doesn't yet recognize. When the assembler is making its first pass, the labels NOSCORE, SOMESCORE, and CONTINUE have no meaning. They haven't yet been defined. They are address-type labels. That is, they stand for a location within the ML program to which JMPs or branches are directed. Sometimes those jumps and branches will be forward in the code, not yet encountered.

The assembler is keeping track of all the addresses as it works its way through the source code. But labels cannot be defined (given their numeric value) until they appear. So on the first pass through the source code, the assembler cannot fill in values for things like NOSCORE in line 110. It will do this the second time through the source code, on the second pass. The first pass has a simple purpose: The assembler must build an array of label names and their associated numeric values. Then, on the second pass, the assembler can look up each label in the array and replace label names (when they're being used as arguments like LDA NAME) with their numeric value. This transforms the words in the source code into numbers in the object code and we have a runnable ML program. Throughout this book, we'll frequently have occasion to mention pass 1 or pass 2.

How to Use This Book

The Two Kinds of Labels

There are two kinds of labels in ML source code: equate and address labels. Equate labels are essentially indistinguishable from the way that variables are defined in BASIC:

100 INCOME = 15000

This line could appear, unaltered, in LADS or in a BASIC program. (Remember this rule about labels: Define your equate labels at the start of the source code. The LADS source code shows how this is done. The first part of LADS is called Defs and it contains all the equate definitions. This is not only convenient and good programming practice; it also helps the assembler keep things straight.)

The other kind of label is not found in BASIC. It's as if you can give a name to a line. In BASIC, when you need to branch to a subroutine, you must:

10 GOSUB 500

500 (the subroutine sits here)

that is, you must refer to a line number. But in LADS, you give subroutines names:

10 JSR RAISEIT; GOSUB TO THE RAISE-THE-Y-REGISTER-SUBROUTINE

500 RAISEIT INY; THE SUBROUTINE WHICH RAISES Y 510 RTS

This type of label, which refers to an address within the ML program (and is generally the target of JSR, JMP, or a branch instruction), is called an address-type label, or sometimes a PC-type label. (PC is short for Program Counter, the variable within the 6502 chip which keeps track of where we are during execution of an ML program. In LADS, we refer to the variable SA as the Program Counter—SA keeps track, for LADS, of where it is during the act of assembling a program.)

Subprogram is a useful word. LADS source code is written like a BASIC program, with line numbers and multiple-statement lines, and it's written in a BASIC environment. The source code is saved and loaded as if it were a BASIC program. But if you are writing a large ML program, you might write several of these source code "programs," saving them to disk sepa-

How to Use This Book

rately, but linking them with the .FILE and .END pseudo-ops into one big chain of source programs. This chain will be assembled by LADS into a single, large, runnable ML object program.

Each of the source programs, each link in this chain, is called a subprogram. In the source code which makes up LADS there are 13 such subprograms—from Defs to Tables—comprising the whole of LADS when assembled together. This book is largely a description of these subprograms, and some chapters are devoted to the explication of a single subprogram. To distinguish subprograms from subroutines and label names, the subprogram names (like Tables) have only their first letter capitalized. Subroutines and labels are all-caps (like PRINTFLAG).

The word integer means a number with no fraction attached. In the number 10.557, the integer is the 10 since integers have no decimal point. They are whole numbers. ML programs rarely work with anything other than integers. In fact, the integers are usually between 0 and 65535 because that's a convenient range within which the 6502 chip can operate—two bytes can represent this range of numbers. Of course, decimal fractions are not allowed. But virtually anything can be accomplished with this limitation. And if you need to work with big or fractional numbers, there are ways.

In any case, when we refer to integer in this book, we mean a number that LADS can manipulate, in a form that LADS can understand, a number which is a number and not, for example, a graphics code. For example, when you write LDA $15 as a part of your source code, the computer holds the number 15 in ASCII code form. In this printable form, 15 is held in the computer as the numbers $31 $35 which, when printed on the screen, provide the characters 1 and 5 (but not the true number 15). For the assembler to work with this 15 as the number 15, it must be transformed into a two-byte integer, an actual number. When translated, and put into two bytes, the characters 1 5 become: $0F 00. We'll see what this means, and how the translation is accomplished, in Chapter 5 where we examine the subprogram Valdec. It's Valdec's job to turn ASCII characters into true numbers.

How to Use This Book

The Seventh Bit (Really the Eighth)

For most of human history, we had to get along without the 0. It was a great leap forward for mankind when calculations could include the concept of nothing, zero. But now there's another mental leap to be made, a private adjustment to the way that computers use zero: They often start counting with a zero, something humans never do.

Imagine you are driving along and you've been told that your friend's new house is the third house in the next block. You don't say "house zero, house one, house two, house three." It makes no sense (to us) to say "house zero." We always count up from 1.

But the computer often starts counting from zero. In BASIC, when you DIM (15) to dimension an array, it's easy to overlook the fact that you've really DIMed 16 items—the computer has created a zeroth item in this array.

It's sometimes important to be aware of this quirk. A number of programming errors result from forgetting that unnatural (or at least, nonhuman) zeroth item.

This situation has resulted in an unfortunate way of counting bits within bytes. It's unfortunate in two ways: Each bit is off by 1 (to our way of thinking) because there is a zeroth bit. And, to make things even tougher on us, the bits are counted from right to left. Quite a perversity, given that we read from left to right. Here's a diagram of the Status Register in the 6502 chip, each bit representing a flag:

76543210 (bit number within the Status Register byte) N V - B D I Z C (flag name)

As a brief aside, let's quickly review the meanings of these flags. The flag names in the Status Register reflect various possible conditions following an ML event. For example, the LDA command always affects the N and Z flags. If you LDA #0, the Z flag will go up, showing that a zero resulted (but the N flag will go, or stay, down since the seventh bit isn't set by a zero). Here's what the individual flags mean: N (negative result), V (result overflowed), - (unused), B (BRK instruction used), D (decimal mode), I (interrupt disable), Z (result zero), C (carry occurred).

But in addition to the meanings of these flags in the Status Register, notice how bytes are divided into bits: count right to left, and start counting from the zeroth bit.

How to Use This Book

This is relevant to our discussion of LADS when we refer to bit 7. This bit has a special importance because it can signify several things in ML.

If you are using signed arithmetic (where numbers can be positive or negative), bit 7 tells you the sign of the number you're dealing with. In many character codes, a set (up) seventh bit will show that a character is shifted (that it's F instead of f). In the Atari, it means that the character is in inverse video. But a set seventh bit often signifies something.

One common trick is to use bit 7 to act as a delimiter, showing when one data item has ended and another begins. Since the entire alphabet can easily fit into numbers which don't require the seventh bit up (any number below 128 leaves the seventh bit down), you can set up a data table by "shifting" the first character of each data item to show where it starts. The data can later be restored to normal by "lowering" the shifted character. Such a table would look like this:

FirstwordSecondwordAnotherwordYetanother.

BASIC stores a table of all its keywords in a similar fashion, except that it shifts the final character of each word (enDstoPgotOgosuBinpuT...). Either way, shifted characters can be easily tested during a search, making this an efficient way to store data. Just be sure to remember that when we refer to the seventh bit, we're talking about the leftmost bit.

Springboard

In the 6502 chip instruction set, there aren't any instructions for giant branches. Some chips allow you to branch thousands of bytes away, but our chip limits us to 127 bytes in either direction from the location of the branch. Normally, this isn't much of a problem. You JSR or JMP when you want to go far away.

But as you assemble, you'll be making tests with BNE and BEQ and their cousins in the B group. Then, later, you'll add some more pieces of programming between the branch instruction and its target. Without realizing it, you'll have moved the target too far away from the branch instruction. It will be a branch out of range.

This is pretty harmless. When you assemble it, LADS will let you know. It will print a bold error message, print the offending line so you can see where it happened, and even ring a bell in case you're not paying attention. What can you do,

How to Use This Book

though, when you have branched out of range? Use a springboard.

The easiest and best way to create a giant branch is this:

100 LDA 15

110 BEQ JTARGET

170 JTARGET JMP TARGET; THIS IS THE SPRINGBOARD

930 TARGET INY ; HERE IS OUR REAL DESTINATION FROM LINE 110

When you get a BRANCH OUT OF RANGE ERROR message, just create a false target. In LADS, the letter J is added to the real target name to identify these springboards (see line 170 above). All a springboard does is sit somewhere near enough to the branch to be acceptable. All it does is JMP to the true target. It's like a little trampoline whose only purpose is to bounce the program to the true destination of the branch.

One final note: To make it easy to locate programming explanations in the text of this book, all line numbers are in boldface. Most of the chapters in the book cover a single major subprogram. At the end of a chapter is the appropriate source code listing. It is these listings to which the boldface line numbers refer.

Now, let's plunge into the interior of the LADS assembler. We'll start with the equate labels, the definitions of special addresses within the computer.

-

~

Defs:

Equates and Definitions

Let's get started. Recall that the boldface numbers within the text refer to line numbers within the program listings at the end of each chapter. The first section of LADS defines many of the variables which are used throughout the program. It's called "Defs."

Defs for Relocatability

One of the advantages of advanced assemblers, LADS included, is that they create object code (runnable ML programs) which are both relocatable anywhere within a computer's RAM memory as well as transportable between computer brands and models.

If you want to put LADS at $5000 instead of $2AF8, you can relocate it quite simply: Just change line 10 in the Defs source code file, the first file in the chain of LADS source code files. As written, line 10 reads *= 11000 (equivalent to * = $2AF8) and that causes the entire object program to start at that address. Changing line 10 to *= $5000 relocates LADS when you next assemble it. If you include the pseudo-op .D, the object program will be saved to disk under the filename you specify.

In the source code of LADS itself, at the end of this chapter, the ".D LADS64" in line 30 will create a version of LADS on disk by the name of LADS64 and if you later LOAD "LADS64",8,1 it will come into your computer ready to run with a SYS 11000. If you change the start address in line 10, however, to $5000, and then reassemble the source code, your LADS will start with a SYS 20480 (decimal for $5000).

The numbers generated by the assembly (the object code) will be sent to a disk file if you specify that with ,D. They will be sent into RAM memory if you use the .O pseudo-op. If you do turn on storage of object code to memory, LADS will send the results of the assembly right into memory during the assembly process. This can cause mysterious difficulties unless you are careful not to assemble over LADS itself. If you have created a version of LADS which starts at $4C00 and you then start assembly of some object program at $5000, you'll eat into LADS itself. LADS is about 5K long. This, of course, would

Defs: Equates and Definitions

cause havoc. Using the .D pseudo-op is safe enough, since the new ML program assembles to disk. But the .O pseudo-op will send bytes right into RAM during assembly.

Be aware, too, that LADS builds its label array down from the start of its own code. During assembly, the labels and their values are stored in a growing list beneath the start address of LADS (where you SYS to start the assembler). If you send object code into an area of RAM which interferes with this array, you'll get lots of UNDEFINED LABEL errors. So be sure you know where you're putting object code if you store it in RAM during assembly by using the .0 pseudo-op.

Defs for Transportability

The only part of LADS which is intensely computer-specific is this first file, this first subprogram, called Defs. Here we define all the machine-specific equates. (An equate is the same thing as a variable definition in BASIC. For example, RAMSTART = $2B is a typical equate.) We'll use the Commodore 64 Defs (Program 2-1) as our example. The labels (variable names like RAMSTART) for all other computers' versions of LADS will be the same—only the particular numbers assigned to these labels will vary. The addresses of pointers and ROM routines vary between computer models.

Defs contains the definitions of all zero page or ROM addresses that will be used in the rest of the source code. Once again, remember that all zero page equates must be defined at the start of the source code (Defs illustrates that rule: Defs is the first part of the LADS source code). From lines 60 to 170 we define the locations within zero page that we'll be using. In line 70 we define the top of the computer's RAM memory. We're going to lower it from its usual spot to fall just below where LADS itself starts.

ST is the location where errors in disk file manipulation can be detected. Like all of these zero page equates, this location varies from computer to computer. LOADFLAG (line 90) signals the computer that we want to LOAD a program file (rather than VERIFY a previously SAVEd program file). This flag will be set in the version of LADS which assembles from RAM memory (and LOADs in chained source code programs from disk). This RAM-based version of LADS will be created later in Chapter 11, the chapter on modifying LADS.

Defs: Equates and Definitions

Disk I/O Information

The next five definitions show where information is stored just before a disk operation. They tell the operating system where in memory a filename is located, how long the name is, the file number, the file's secondary address, and the device number (8 for disk, 4 for printer, in Commodore computers).

CURPOS always contains the position of the cursor onscreen (as a number of spaces over from the left of the screen). We'll use this to format the screen listings. And the final machine-specific zero page definition is RAMSTART. It tells LADS where BASIC RAM memory starts. It, too, is used in the version of LADS which assembles from RAM.

Why do we need to define these locations if the operating system uses them? Because we're going to use a few of the built-in BASIC routines to handle the I/O (Input/Output) operations for us when we need to communicate with a peripheral. To OPEN a file, for example, we need to set up several of these pointers. To OPEN file #1, we have to put a 1 into address $B8 (that's where the file number is held on the Commodore 64). But why not just use LDA #1: STA $B8? Why do we want to use these labels, these variable names?

Programming with pure numbers instead of labels prevents transportability. It locks your program into your computer, your model. It's far easier to change this single equate in line 120 to $D2 to make the program run on a PET/CBM with BASIC 4.0 than it would be to go through the entire source code, changing all B8's to D2's. Also, if you buy a newer model and they've moved things around in zero page (they almost always do), making the adjustments will be simple. You just use a map of the new zero page and make a few changes in the Defs file.

LADS Zero

Because LADS needs to use the valuable Indirect Y addressing mode—LDA (12),Y or STA (155),Y—it will want to usurp a few of those scarce zero page locations itself. Line 170 defines a two-byte temporary register called TEMP which will be used in many ways. SA is going to function as a two-byte register for the LADS Program Counter which will keep track of where we are currently storing object bytes during the assembly process.

MEMTOP is used in the construction of our label data

Defs: Equates and Definitions

base. It will always know where the last symbol in our label table was stored. All through pass 1 it will be lowering itself, making room for new symbols and labels. (This data base will later be referenced as we fill in the blanks on pass 2.) PARRAY makes that search through the symbol table on pass 2 easy and fast. It points us through the array. PMEM is used as a pointer during assembly from RAM, if you decide to use the RAM-based version of LADS described in Chapter 11. The uses of all these variables will become clear when we examine, throughout the book, the techniques which utilize them.

Borrowing from BASIC

The next section, lines 190-320, defines the routines within BASIC ROM memory that we're going to use. Naturally, these are particular to each computer brand and model, so we want them up front where they can be easily identified and changed.

BASIC always has an entry point called the warm start address, a place where you can jump into it "warmly." But there's another entry that's not as gentle. Many BASICs clear out RAM memory and radically reset pointers, etc., when you first turn on the computer. This is called the cold start entry point, and it's as much of a shock to the computer as walking outdoors into a winter wind is to you. We don't want this shock when we return from LADS to BASIC. Instead, we want the RAM memory left alone. After all, LADS is in there and possibly an object or source program is in there too. So when assembly is finished, we want to go into BASIC via the warm start entry point.

KEYWDS is the address of the first BASIC keyword. We'll see why we need this address in the chapter on the Indisk subprogram. OUTNUM is a ROM routine which is used to print line numbers for the BASIC LIST command. We'll use it in a similar way to list the line numbers of our source code.

OPEN, CHKIN, CHKOUT, CLRCHN, and CLOSE allow us to communicate with the disk drives and printers. CHARIN

Defs: Equates and Definitions

is like BASIC'S GET command, PRINT like PRINT. STOPKEY sees if you've pressed the STOP or BREAK key on your keyboard. And, last, SCREEN tells LADS where in RAM your video memory starts.

The use of these routines, and the ways that ML programs can borrow from BASIC, will be covered in detail as they appear in the LADS source files. For now, we only need to know that they are defined here, in Defs, and can be quickly changed to suit different computers, different BASICs.

There you have it. We'll be explaining these pointers and registers as we come upon them in the explication of LADS. Now on to the heart of LADS, the section which evaluates all the mnemonics (like LDA) and addressing modes and turns them into opcodes (like A9) that are the machine's language. This next section, Eval, is—by itself—a complete assembler. It would stand alone. The rest of the sections of LADS add things to this core, things like disk management, arithmetic and other pseudo-op routines, label interpretation, screen and other output, and a host of other niceties. But Eval is the sun; the rest of the routines are lesser bodies, planets in orbit around it.

Note: Because the Defs subprogram is computer-specific, there are five source code listings at the end of this chapter, one for each computer. There are also multiple listings in Chapter 5 since it deals with computer-specific peripheral communication. However, the majority of chapters will have only a single complete listing, followed by the few modifications required by the different computers, because the majority of LADS' source code is identical and entirely transportable between 6502-based computers.

Defs: Equates and Definitions

a w w <c^-

PH >1 >H O M

CQ CQ O >

J «

w w &j a- K

Z 21 2 O

ZOOUh

BSSSBSSSBSSSSH

Defs: Equates and Definitions

Defs: Equates and Definitions

Defs: Equates and Definitions

h1

<

>

m

SSSSSSBSSSSShSSSSISS

"a

en

UJ

h-o:

D UJ

UJ

in <x

ii-

o ii: cc

UJ UJ

N I— Z

U -1

M O

Ll Cl

u >

UJ cc

0, O

CD CD £

z u

M <t

u. z

ui

Q

Q

Z

<r UJ

<N

GJD O

Q

u. in

z <r

o 3

M GJ

U3 UJ CC UI

> :

en

UJ u.

_J UJ

a. a

a z

X

UJ I-

u. o

UJ

ui £ h- <i > z

DO

UJ h- _J

X HH

UI LL

z

LL

o o h-I

CC H

Ul CD

h Z

Z UI

H _J

o

U. «»■

0-.,. LL

CD *

» II

LL

o

!--

E LU E BO

Z

LU _J LU

z <i z

LL

o o o o o o o o in

-< N M t 111 -0 N CO CO

Defs: Equates and Definitions

3

h _J CD

>

x uj

U. UJ

z 3

X h

<r. a en

(-■ o X X

in a.

h-

x u. o a

Q UJ Z

H- UJ

ill o

UJ UJ

z t~

uj a

cd a.

-H u. CD <t

u a

_J IX

CD W

H

u. z

a h

. en

UJ UJ z H-

_J 3 3 Z UJ LU

LU Ul IX CD CJ <X Q ID 0. UJ *» Z O UJ X II > LU

M M >

HI <t -I X <X X

1

z a

Z X

a lu

h-

x z

O w

en

x en

LP

lu

(-

U) UJ

LU _J

Q ill

I M

en x

i >

en

LU

h-<x 3

a

LU E

a

x

u

u

LU

J

ill

X 1

LU h-

U.

IX

UJ LU HI Z

£ M

Z> \~ Z 3

O

■-. X

III

CO H

_J 3

"• X X

h- LU

X 3 h-

a z.

UJUlLQlD

x h 3 x E co oi o a -

X IB LU

UJ IB Z

M O I

u

<I

3

t >

3 LU X ii Z

i-i X G

X

u.

X

o

XI X r-

>■ z

u ib a

X

h-J

<x e z a

CD X

X

H E

x a

X X

p

z

CJ LU

i-H LU

UJ X

<i a

CD Ul

a x

h o

CO

>

Q <I

UJ

ix

x > z en

x

X IB E CJ

h

co

* Cr- ..

O -O -0 « « (0

en ib en h a a <r o

hi Z I h- X CD X CD X X

UCOhCD

I > u

X X CJ X

O Ul Cr- &>

CJ en <E

IB U X

a

OCOI-

cn <i en

IB

UJ h- (-

H tl H HI J

X X X O

x a h v,

Q 3 w

... <r o

X X

X z

X H

X X

CD

cn x

i a a cn

h cn

x x

x x

cn a

z a

Q O Ki O W- CN

U II

M

cn x

<r 3

IB IB

a <x

I IB

a ki o o-

X U"J X o

ClL« II » *

uud w O -0

u a t « a a

E II II

3 X

Z Q r h-

r 3 3 Z

3 UJ 5 X

O CJ U X

Q r-

X X

I CD

z z

X i-i

» _J

<r

ii >

x z

x x x x

X M

U X X .

O O O O O LO O O in O O Q O O Q O O O O

o -i to t \r> \n o r-- n en o o -< CN w * in oo t

HHrtHHHHHHHrtflNN CN CN CN CN -0

Defs: Equates and Definitions

Program 2-5. Defs: Atari

100 *= *8000

110 .D D:LADS.OBJ

120 ST = *01

130 FNAMELEN = $80

140 FNAMEPTR = $81

150 FNUM = *83

160 FSECDND = *B4

170 FDEV = *85

180 CURPOS = 85

190 TEMP = *86

200 SA = $88

210 MEMTOP = *8A

220 PARRAY = $8C

230 INFILE = *8E

240 OUTFILE = $8F

250 PMEM = *A0

260 RAMFLAG = *A2

270 BABUF = *0500

280 SAVMSC = *58

290 .FILE D:EVAL.SRC

1

Eval:

The Main Loop

Eval is the heart of LADS. It is the main loop. It starts assembly at START (line 30) and ends assembly at FINI (line 4250). Throughout Eval, JSRs take us away from the main loop to perform various other tasks, but like mailmen, all the other routines in the assembler start out from Eval, the post office, and they all RTS back to it when their work is done.

For convenience, references to lines within the source code listing at the end of the chapter are boldface inside parentheses. Also, to distinguish label names like FINI from the names of one of the 13 sections of LADS (a subprogram like Eval), we'll put label names in all caps, but just capitalize the first letter of the subprograms of the assembler.

Preliminaries, Preparations

Most programs have a brief initialization phase, a series of steps which have to be taken to fix things up before the real action of the program can commence. Variables have to be set to zero, files sometimes have to be opened on a disk, defaults have to be announced to the program. (Defaults are those things a program will do unless you specifically tell it not to. A game might default to single-player mode unless you do something which tells it that there are two of you playing. LADS defaults to hexadecimal numbers for printer or screen listings and turns off all its other options.)

At its START, LADS loads the Accumulator with zero and runs down through 48 bytes of registers, flags, and pointers, stuffing a zero into each one. These flags are all needed by LADS to keep track of such things as which pass it's on, whether or not you want a printer listing, or want the results of an assembly to POKE into memory, or whatever. This initialization fills them all with zero. The label OP is the highest of these registers in memory, so we LDY with 48 and DEY down through them (see line 30).

Let's take a minute to briefly review our terminology:

Register usually refers to the Accumulator (A), or the X or Y Register in the 6502 chip. It can also mean a single byte set aside to temporarily hold something. It's like a tiny buffer.

A buffer is a group of continuous bytes used to hold infor-

Eval: The Main Loop

mation temporarily. An input buffer, for example, holds the bytes you type in from the keyboard so they can be interpreted by BASIC. The bytes stay there until you type RETURN, BASIC stores the information into your program, and you type a new line into the input buffer.

A flag is a byte which is either on or off (contains either zero or some number) and signifies a "do it" or "don't do it," yes or no, condition. Of course, a single byte could hold a number of flags because each bit could be on or off. In fact, the Status Register in the 6502 chip does just that—it's only a single byte, but its bits are flags tested by CMP and the BNE, BEQ-type instructions. When you need a flag, though, it's easier to just use a whole byte and test it for zero or not-zero. An example of a flag in LADS is the PRINTFLAG. If nonzero, the assembler sends a printout of the assembly process to a printer. If zero, the printer remains silent and still. You set (turn on) the print flag with the pseudo-op .P; otherwise, the default is no printing.

A pointer holds a two-byte address. Many times pointers are put into zero page so they can be used by Indirect Y addressing: LDA ($FB), Y gets the byte from the address held in $FB and $FC (seen as a single, two-byte-long number). If

00FB 00 00FC 15

(remember that the 6502 expects these numbers to be backward; this two-byte group means $1500) then LDA ($FB),Y will load the A register (the Accumulator) with whatever byte is currently in address $1500. We can set up our own pointers. If they're not in zero page, they're likely holding some important address which a program needs to remember. In LADS, ARRAYTOP is such a non-zero-page pointer; it tells LADS where to start looking through the label table for a match. We'll look into this when we get to the subprogram Arrays.

Cleaning the Variables

At its start LADS must initialize its variables. If we didn't fill them with zero, there could be some other number in these bytes when we fire up LADS and that could cause unpredictable results. Then (80) we get the low byte of the start of LADS (using the pseudo-op #<START) and put it in the low

Eval: The Main Loop

byte of MEMTOP (used by the Equate subprogram). We also put it into the pointer BASIC uses to show how much RAM memory it has available, BMEMTOP (line 70 in Defs). And, finally, put it in ARRAYTOP. ARRAYTOP will show where the LADS' data base of labels starts in memory (it builds downward from the location of LADS).

Then we take the high byte of START and put it into the high bytes of these three pointers.

Now for the defaults. There is only one. We want listings to be in hexadecimal unless we specifically direct the assembler otherwise with the .NH, no hex, pseudo-op. So we put #1 into the HXFLAG. The rest of the flags are left at zero. If you want different defaults, put #1 into some of the other flags. For example, if you usually want to watch the results on screen during an assembly, just create a new line: 185 STA SFLAG. This will cause a screen disassembly every time you use LADS. Putting this default into LADS itself merely saves you the time of adding the .S pseudo-op if you generally do want to watch the assembly onscreen. That does slow up the assembler, but with shorter programs, you might not notice the difference.

Where's the Source File?

LADS needs to know what you want to assemble. If you're using the RAM-based version of LADS (see Chapter 11), there's no need to give a filename to LADS; just SYS, and LADS will assemble what's already in RAM. But if you're in the normal LADS mode, assembling from a disk file, you'll have to announce which file. LADS looks at the upper left-hand corner of the screen to read the filename (190). If it finds a space #32, it checks for another space (310) before giving up. This way you can have continuous names like FILENAME as well as two-word names like FILE NAME. Whatever it finds onscreen, it stores in the buffer FILEN. It also takes care of characters which are below the alphabet in the ASCII code by adding 64 to them if they fall below 32 (240). The Atari version asks for the filename from the keyboard in the manner of a BASIC INPUT command.

When the filename is stored in the buffer, we JSR to Openl, the subprogram which handles all I/O, all communication with peripherals. In this case, communication will be with the disk drive.

Eval: The Main Loop

After the file is opened for reading, we JSR to another subprogram, Getsa, the get-start-address routine. It just looks for *= (the start address pseudo-op) and, finding it, returns to Eval where the number following that symbol will be evaluated. If it doesn't find a * = , that can only mean two things. Either there is no program on the disk by the name you put onscreen or LADS did find the program, but no starting address was given as the first item in the source code. Both of these situations are capable of driving LADS insane, so Getsa aborts back to the safety of BASIC after leaving you a message onscreen.

This SMORE routine (370) will be used again when we've completed the first pass of the assembly process. The first pass goes through the entire source file, storing all the names of the labels and their numeric values into an array.

When we finish making this collection of labels, our label array, we've got to make a second pass, filling in the opcodes and replacing those labels with numbers. It's here, at SMORE, that we jump to start the second pass.

A zero is given to ENDFLAG to keep the assembler running. If the ENDFLAG is left up, is not zero, the assembler assumes it has finished its job and stops.

The initialization is completed with a JSR to the subprogram Indisk which pulls in the number you wrote as the starting address following *=. This number is left in LADS' main input buffer called LABEL. Before dealing with this number, though, we check to see if we're on the first pass (410) and, if so, print the word LADS onscreen after a JSR PRNTCR which prints a carriage return. Routines beginning with PRNT like PRNTSPACE and PRNTLINE are all grouped together in the subprogram Findmn. They're used by most of the subprograms and print various things to the printer or screen.

Now we need to put the starting address into the pointer SA which always holds the current target for any of our assembled code during execution. If the HEXFLAG is up, that means you wrote something like *= $5000 and hex numbers are translated by the subprogram Indisk before it RTSs back to Eval. Decimal numbers like *= 8000, however, are not translated into the two-byte integers that ML (machine language) works with, so we need to send decimal numbers to Valdec (another subprogram) to be turned into ML integers (610). The

Eval: The Main Loop

pointer called TEMP is made to point to LABEL so Valdec will know where to look for the number.

It's important to realize that numbers coming in from the disk or from RAM memory are in ASCII code, as characters, not true integer numbers. That is, the characters in a number like 5000 will come into the LABEL buffer as they appear in RAM or on a disk file. 5000 would be (in hexadecimal notation) 35 30 30 30; these are the character codes for 5-0-0-0. It's Valdec's job to transform this into 00 50, an ML integer. When we get to Valdec, we'll see just how this is done. It's a useful technique to learn since any numbers input from a keyboard will also be in this ASCII form and will need to be massaged a bit before they'll make sense to ML.

Remembering the Start Address

When, at STAR1, we finally have an ML integer in the little two-byte variable called RESULT, we can transfer the integer to SA. And we put the integer into the variable TA, too, so that we'll have a permanent record of the starting address. SA will be dynamic; it will be changing throughout assembly to keep track of the current assembly address. It will be LADS' Program Counter. TA will always remember the original starting address.

By this time you might be thinking that all this is hard to follow. TA and RESULT and LABEL don't mean much at this point. We've plunged into Eval, the most condensed, the most intensive, section of the entire program. As the main loop, Eval will send tasks to be accomplished to many subroutines, in subprograms which we've not yet examined. It's like landing in a strange city without a map. You see street signs, but they mean nothing to you yet. But this is one of the best ways to learn if you can be patient and ignore the temporary gaps in your knowledge and the momentary sensations of confusion.

We're gradually building a vocabulary and mapping out some of the pathways which make up the language LADS and the ways the ML works. The subprograms are, by and large, easier to follow. They're more self-contained. But bear with this tour through Eval. It makes what follows easier to grasp and offers a foundation—however unconscious at this point— for a deeper appreciation of the ways that ML does its magic.

Eval: The Main Loop

The Main Routine

Every line of source code which LADS examines begins with STARTLINE (690). The ML between STARTLINE and P (5520) is, in effect, an assembler. The rest of the routines and subprograms deal with the niceties, the auxiliary efforts of the assembler—pseudo-ops, built-in arithmetic routines, I/O, printout formatting, and so forth.

In fact, this section of LADS is based on the BASIC assembler, the Simple Assembler, from my previous book, Machine Language for Beginners. If you want to see how a large BASIC program can be translated into ML, you might want to compare the Simple Assembler to the rest of Eval. There are some comments within the listing of LADS' source code which refer to the BASIC lines within the Simple Assembler (see lines 3270 and 3410 for examples), and a number of the labels, starting at 4670, also refer to their BASIC line number equivalents in the Simple Assembler. L680 is a label to LADS, but is also a reference to an equivalent line, 680, in the BASIC of the Simple Assembler.

It's LADS' job to take each line in the source code and translate it into runnable ML object code. LADS would take the source line 10 LDA #15 and change the LDA into 169 and leave the 15 as 15. The value 169 is the ML opcode for the Immediate addressing mode of LoaDing the Accumulator. Then LADS would send these two bytes of object code, 169 15, to any of four places depending on what destinations you had specified as pseudo-ops in the source code. The .D pseudo-op would send 169 15 to a disk file, .P to the printer, .S to the screen, and .O directly into RAM memory.

When LADS first looks at at each source code line, STARTLINE checks the ENDFLAG to be sure it's safe to continue. If ENDFLAG is zero, we BEQ to the JSR to Indisk. (Otherwise, the program would go down to FINI and close up shop, its work finished.)

Indisk is the second largest subprogram, and LADS will be gone from Eval a long time by the computer's sense of time. For us, this detour happens in a flash, and a lot happens. Indisk can even JSR into other subprograms, but we'll see that in a later chapter. All we need to realize now is that each source line needs to be pulled onto our examination desk so LADS can pick it apart and know what to assemble.

Eval: The Main Loop

Our examination desk is the buffer called LABEL. First a line of source code is laid out on the desk. To prepare for the exam, we put down the EXPRESSF(lag) and the BUFLAG, although they might be raised again during the evaluation to come. EXPRESSF tells LADS whether the expression following a mnemonic like LDA is a label or a number. It signals the difference between LDA SPRITE and LDA 15. BUFLAG tells whether or not there is a REM-like comment attached to the line under examination. If there is a comment, we'll want the assembler to ignore the remarks, but the screen or printer should nevertheless display them.

Now, as we often will, we check PASS (760) to see if it's the first or second time through the source code. On the first pass, we're not going to print things to a printer or the screen, so we'd jump to MOE4 and ignore the next series of printouts.

But if it's the second pass, we check the SFLAG, the screen flag, to find out if we should print to the screen. If the answer is yes, we print a line number, a space, the SA (current address), and another space. Don't worry about LOCFLAG just yet.

Now we want to know if there's any math to do. PLUSFLAG is up when the line contains something like this: LDA SCREEN+ 5. If it does, we briefly detour to the subprogram Math to replace SCREEN+ 5 with the correct, calculated number.

The Inner Core

Now we're at the true center, the hot core, of LADS: Line 900 is the pivot around which the entire structure revolves. This JMP to Findmn accomplishes several important things and sets up the correct pathways for the assembler to follow in the future. Findmn finds a mnemonic. Say LADS is examining this line:

10 LDA 15

After Findmn does its job and JMPs back to Eval, there would be a 1 in the TP register (it's like a BASIC variable, called TP for "type"). And there would be a 161 in the OP, for opcode, register.

That 161 is not the number we'll want POKEd into memory. 161 is the right number for the LDA (something,X) addressing mode, but it's wrong for the other modes, includ-

Eval: The Main Loop

ing LDA 15. Nevertheless, any LDA will first get a 161, the base opcode. It's the lowest possible opcode for an LDA; the other LDA addressing modes can be calculated by adding to 161. LDA 15 is Zero Page addressing and its opcode is 165. Eval's main job is to start off with the lowest, the base opcode for a particular mnemonic like LDA, and then make adjustments to it when the correct addressing mode is detected. Eval establishes the addressing mode when it examines the line and looks for things like the # symbol and so forth. As we'll see, this examination will modify the OP number until the correct opcode is calculated.

For now, though, it's enough that we return from Findmn with a base opcode number, something reliable to work from, stored in the variable OP. By the way, Findmn gets these numbers, TP and OP, from a table in the subprogram Tables. We'll look at it at the very end of our exploration of LADS in Chapter 9. Tables is where all the constants are stored.

When No Match Is Found

Sometimes Findmn won't find a match when it looks through the table of mnemonics in the subprogram Tables. This means that the first word in the line under examination was not a mnemonic. If this happens, Findmn returns (via a JMP) back into Eval where labels are analyzed. Eval then knows that this first word isn't one of the 6502 commands. Instead, it must be a label.

Labels in this first position in a line can be of two types: address labels and equate labels. An address label identifies a location within the program that will be the target for branches, jumps, JSR, etc. It's like giving names to subroutines so you could later JSR PRINTROUTINE. Here's an example:

100 START LDA #0

After the assembler finishes assembling this, we'll have:

100 3A00 A9 00 START LDA #0

The OP 161 has been changed to 169 (the hex number A9 in the example above), and we'll see how that was arrived at presently. But START has had no visible effect. It's just listed there, but doesn't affect the A9 or 00. START is a place marker. It hasn't been ignored. During the first pass, LADS stored START in an array along with the 3A00 address. That's why START can be called an address label. This is very much

Eval: The Main Loop

the way that BASIC reads a variable name, sticks it in an array, and puts the value of the variable up there with the name.

On pass 2, when all these labels are needed, the correct address will be there, waiting in the array. If LADS comes across a JSR START or a BEQ START, it will be able to search the array and replace the word START with the right number, the address.

The other possible kind of label is the equate label. It looks like this:

1100 SCREEN = $0400

It, too, is stored during the first pass and looked up during the second pass. But the equals sign shows that we should remember the value on the other side of the = symbol, not the address of the location of the label. In this example, whenever we want to store something onscreen, we don't need to calculate the correct address. $0400 is the first byte in screen memory (on the Commodore 64 in this example). So we can just STA SCREEN to put whatever is in A into the upper left-hand corner of the screen. Or STA SCREEN+ 200, or STA SCREEN+ 400, or whatever. (Adding numbers to SCREEN will, in this case, position our A lower on the screen.)

It's here that we decide whether we're dealing with one of the labels or with an ordinary mnemonic. If we JMP back from Findmn to EVAR (920), the first thing on the source code line was a mnemonic. If we JMP back from Findmn to EQLABEL, it wasn't a mnemonic (hence it's a label). EVAR evaluates the argument, the 15 in LDA 15. EQLABEL evaluates the other kind of argument, the label SPRITE in LDA SPRITE.

Simple and Other Types

Some of the mnemonics are quite straightforward. They've got no argument at all: INY, ROL, CLC, DEC, BRK, RTS, etc. There's no argument to figure out, and all of these self-contained instructions have the same addressing mode, Implied addressing. Fully 25 of the 56 mnemonics are of this type. We've called them type 0 (see the chapter on the Tables subprogram for an explanation of the types), and so Findmn puts a 0 into the TP variable. Our first step in the evaluation of any argument (920) is to check the TP, and if it's 0, go to the type 1 (meaning only one byte, the opcode itself) area. There, the

Eval: The Main Loop

single byte will be POKEd and printed if you've requested that with your pseudo-ops. And then we can go on to fetch a new line.

If it's a more complicated addressing mode, though, we continue evaluating, comparing it to type 3 (940). If you want, you can look up the mnemonics and the parallel types and ops tables in the Tables subprogram. Type 3's are the bit-moving instructions ROL, LSR, ROR, and LSR. They have a pattern of possible addressing modes in common. (It's this common pattern of addressing modes which underlies these types. They share the same potential addressing modes and can be evaluated and adjusted as a category rather than individually.)

In any case, we turn them into type 1 and then look at the fourth position in the storage buffer LABEL. If we could peer into this buffer, we might see either:

ASL

or

ASL 1500

That bare ASL is not an implied address like INY and CLC and the rest of those self-contained instructions we discussed above. These bit-moving instructions (ASL, ROR, etc.) are just like type 1 (LDA, etc.) with this single exception: They can have a special addressing mode all their own called Accumulator addressing. It's a rare one. In this mode, ASL would Arithmetic-Shift-Left the number in A, the Accumulator.

The point to grasp here is that, rare as a nude ASL is, we've got to include it in the assembler. So we check to see if there is a zero in the fourth position in our buffer, LDA LABEL+3. A zero means end-of-line. So we can detect from a zero that there is no argument and, hence, this is a case of Accumulator addressing. If it is, we need to add 8 to the base opcode for these bit-movers and then jump to the type 1 exit. If it isn't, we've already turned it into a type 1 (970) and from here on, we'll treat it as a member of that family. In effect, type l's can have several addressing modes, so we must evaluate the mode. We go to EVGO.

Fat Y Loops

Before entering most ML loops, you'll first LDY #0. Y often functions as a counter, so it's set to zero, and then INY occurs

Eval: The Main Loop

at the end of the loop. But some loops require that we INY at the start or at least early within the loop. In such cases, we must LDY #255 before entering the loop. The first event within the loop is an INY, so in effect, Y becomes 0 right off the bat. When you increment 255, you get a zero.

EQLABEL is where we determine what kind of label we're dealing with. On the first pass, we don't care. All labels must be stored in our label table array for later reference on pass 2. On pass 2, though, we must go through the test in EVX1 (1090). And it's one of those fat Y loops that start off with a bloated Y Register. We put 255 into Y at the start.

We load the first character in the LABEL buffer. If it's zero (end of the line), there wasn't any argument. There should have been. This is a mistake. By this time, there has to be an argument. We've already eliminated the only addressing types that have no argument: Implied (type 0) and Accumulator (a variant of type 3). If there's no argument, the source code is defective. There should be an argument. We've got to print an error message.

NOAR is tucked away at line 520 of the Equate subprogram. We'll get to it later. It just prints a "no argument" error message. But we should clear up the little mystery surrounding the bounce we just took. We BEQ GONOAR (1110) only to JSR NOAR (1320). Why? This is one of those springboards we discussed in Chapter 1.

The B instructions, the branchers like BEQ, can move us only 127 bytes in either direction, forward or backward, from their location. This is sometimes not far enough. LADS will alert you to this if you should try to branch further than you can. It will print BRANCH OUT OF RANGE and ring the bell. The easiest solution to this problem is to simply have the branch go to a nearby JMP or JSR. They can fly off to any address in the computer. Have them act as springboards, bouncing you to your actual target.

The alternative is to move your target closer to the branch. The target is probably a subroutine. But moving a subroutine is often a lot more trouble than simply creating a springboard.

Back to the evaluation (1120). If there is an argument, we move it up to another buffer called FILEN. Then we check for the blank character, 32, before leaving this loop. The label

Eval: The Main Loop

name gets moved up to FILEN for further analysis. Then we INY and look at the next character.

Which Kind of Label?

If the first thing after a blank character is =, we've got an equate label like:

100 NAME = $500

If it is an equate label, we ignore it because we're on the second pass here. Line 330 sends us over this section if it's the first pass. There's no need to pay any attention to equate labels on the second pass, so we jump to INLINE, the preparations for getting a new line to evaluate.

But it might be the other type of label, an address label like:

100 START LDA #15

On pass 2 we can also ignore START, the label part of this line. Both types of labels have already been safely stored in our array during pass 1. Nevertheless, following the address-type label is some code we cannot ignore. On pass 2 LADS must assemble that LDA #15.

NOTEQ (not equate type) moves the address label up to a buffer called FILEN while at the same time moving the LDA #15 over to the start of the LABEL buffer. It's doing two things at once. This is how these buffers look before NOTEQ (1180-1200):

LABEL START LDA #1500000000000 FILEN 000000000000000000000000

and after NOTEQ:

LABEL LDA #150A #1500000000000 FILEN START0000000000000000000

START is up at FILEN and can be printed out later for a listing. But what good is that mess in the LABEL buffer? It will work perfectly well because that 0 in the eighth position is the delimiter. It tells LADS to ignore any random characters following it. Remember that these numbers are stored in memory as ASCII code, not as literal numbers. 15 would be stored as 49 53. 150 (the number 150) would be stored as 49 53 48. But a different kind of 150, where that final 0 is a true zero, a delimiter, would be stored as 49 53 0. So when we go to look at and assemble the information in LABEL, LADS will only

Eval: The Main Loop

work with LDA #15 and ignore the OA #150000, etc., the remnants of the old line. All is now ready for the assembler to take a look at a mnemonic and its argument, so we JMP to MOE4 (1310). If this had been pass 1, we would have bypassed all this and leapt from 1070 right down to 1330, where we go to the subprogram Equate, which stores labels and their values in the label table array. But both pass 1 and pass 2 must continue to work out the addressing modes by going to MOE4. Why should we need to worry about addressing modes on pass 1 since LADS doesn't POKE anything into memory or save anything to disk during pass 1?

LADS must keep an accurate PC (Program Counter) during pass 1 to know what value to assign to address type labels. Otherwise, the address labels would be inaccurate:

10 START INC 15 20 LDA 15 30 BEQ FINISH 40 JMP START 50 FINISH RTS

Notice that both INC 15 and LDA 15 are Zero Page addressing. They occupy two bytes in memory. But they could have been Absolute (LDA 1500) addressing, or other modes which use up three bytes. LADS has no way of knowing, by reading LDA or INC alone, whether to raise the program counter by two or by three. All this wouldn't matter much except for that label FINISH in line 50. It has to be assigned its proper address during pass 1 and stored in the array. That means LADS needs to know exactly how many bytes it is from START to FINISH.

Consequently, LADS has to check out the arguments of INC and LDA to see whether they're addressing modes using up two or three bytes. This Program Counter is kept in a variable in LADS called SA. It's constantly changing during both passes of the assembly, but it is used during pass 1 to assign numbers to address labels like START and FINISH.

We'll deal with the next routine, EVEXLAB (1360), shortly. Let's go first to MOE4 and see how LADS analyzes arguments.

We've Been Here Before

Recognize MOE4 (900)? We already discussed it. It JSRs to

FINDMN and JMPs back to EVAR (920) having recognized a

Eval: The Main Loop

6502 mnemonic or JMPs to evaluate a label if it didn't recognize a mnemonic. In our example, it will find LDA #15 this time, JMP to EVAR, and end up going to EVGO (from 950).

Here at EVGO, LADS has to decide whether it's dealing with a normal numeric argument like #15 or an expression label, a word like SOUND. Imagine that we'd started off by defining the label SOUND:

10 SOUND = 15

When we later wanted to indicate 15, we could substitute the word (LDA #SOUND) for the number (LDA #15).

EVGO distinguishes labels from numbers by using the ASCII code. In this code, letters of the alphabet have a numeric value 65 (the letter A) and go up from there. Thus, if the character in the fourth position (see line 1490) is less than 65, if it triggers a BCC, we don't raise the EXPRESSF(lag). That flag indicates a nonnumeric expression. In other words, the expression has a letter of the alphabet so it must be a label. Similarly, EVM02A raises the Y offset and tests the fifth character. If it's a zero, we've got a single-letter label, like P (1540). Meanwhile, we're moving the label up to a buffer called BUFFER. And, again, we check for a character with a value lower than 65.

EVM02 (1600) continues to move the label from one buffer (LABEL) to another (BUFFER). It only stops when it finds a zero indicating the end of the line. Note that both number expressions (arguments) like #15 as well as label expressions like #STOOL are moved from the LABEL buffer up to the BUFFER buffer. The only distinction between them is signaled by the raising of the EXPRESSF(lag) when there's a label rather than a number. For numbers, EXPRESS stays down, stays 0.

Hex Numbers Are Already Evaluated

EVM03 (1660) puts the label's size, the number of characters in the label, into the variable ARGSIZE and checks to see if the HEXFLAG is up. The HEXFLAG is sent up in the subprogram Indisk if a $ symbol is noticed as a line is streaming into LADS. So if HEXFLAG is BNE, not equal to zero, it's up and we can jump right down to L340, which starts to figure out the addressing mode. If the EXPRESSF is up, that means a word label, not a number, so we have to go to EVEXLAB to

Eval: The Main Loop

get the number to substitute for the label. Otherwise, we've got a decimal number to work with as our argument (1730).

The whole function of lines 1730-1840 is to have the variable TEMP pointing to the first ASCII number in the label. That's why we keep INCrementing TEMP until we point to a character that is not BCC, less than the 0 ASCII character (48) in line 1830. Then we have to test for the ( left parenthesis or , comma character. If it is one of them, it can put in a true zero as a delimiter.

When the number is properly set up, it is analyzed by the Valdec subprogram, which turns this ASCII string of numbers into an ordinary ML two-byte integer.

If, however, we were sent to EVEXFLAG (from 1710), it checks for something less than an alphabetic character (such as a ( or a # symbol). When it locates the first alphabetic character, it stores it into the variable WORK and JSRs off to the subprogram Array where the stored labels will be looked through. Then it joins up again with the numeric expressions by going to L340 for addressing mode evaluation.

How Is It Addressed?

This is the final job the assembler must perform—distinguishing between Immediate (LDA #15), Absolute (LDA 1500), Zero Page (LDA 15), Indirect Y (LDA (15),Y), and the other addressing modes. Recall that we've already eliminated nearly half the possibilities by previously handling type 0, the self-contained, implied ones like CLC and INY. What's left is to check for # and ( symbols and to see how big the argument is. That tells us if our argument (the expression) calls for Zero Page addressing or not.

First off, LADS checks for the # character (2130) and, finding one, goes to the IMMED routine to handle Immediate addressing. Next it looks for the ( character. Finding one of those, it goes off to the INDIR routine to deal with Indirect addressing.

Failing to find either of these symbols, it loads in the type variable, TP, and looks to see if it's an 8. All the B instructions, the branches like BNE and BCC, are grouped together as type 8. Finding a type 8, LADS goes to the REL subroutine to handle Relative addressing.

From here (line 2220) to the end of Eval, there will, from time to time, be adjustments made to the OP variable which

Eval: The Main Loop

are neither easy to explain nor easy to immediately understand. They're based on the logic of the interrelationships between the various addressing modes. For example, if we've reached this point (2220) without branching to one of the routines like IMMED, INDIR, or REL, we now need to add 8 to the opcode value. Why? It just works that way. If you're truly interested, study the table of opcodes and you'll begin to notice certain similarities between the opcode for LDA absolute and INC absolute, etc. It's not necessary to work all this out. For a detailed discussion of the logic of these adjustments to OP, see the explanation of the Tables subprogram in Chapter 9.

At any rate, INDIR looks at the character of the argument in BUFFER and sees if it's a ) symbol. If not, and it's type 1, we add 16 to OP. If we have a type 6, we know we've got an indirect JMP, so we go there. Otherwise, we go to TWOS, where two-byte addressing modes, like LDA (15),Y, are handled.

JIMMED (2420) is one of those springboards to handle a BRANCH TOO FAR for an unassisted B instruction with its 127-byte reach.

The Hardest Part of LADS

REL handles the B group. This was the hardest part of LADS for me to write. For some reason, I kept hoping for a simple way to test and translate forward and backward branches. No simple way presented itself. There may be a more clever solution than the one you'll see described below, but I couldn't find it and had to go on.

REL first checks PASS. On pass 1, we simply go directly to TWOS. On pass 2, though, we look at RESULT. RESULT is a two-byte variable which holds the integer form of all arguments—labels, hex, or straight decimal. They're all left in RESULT by the various subprograms, Array, Indisk, and Valdec, which translate labels, hex ASCII, and decimal ASCII. These three possible original forms of the arguments are translated into two-byte integers that can be POKEd into memory or saved on disk as parts of an ML program.

If we're on pass 2, we look at RESULT and now calculate the correct argument for a branch instruction. It requires that LADS first determine whether we're branching backward or forward in memory. It does this by subtracting SA (the Program

Eval: The Main Loop

Counter, the current address, the address of the B instruction to which its argument will be relative). It subtracts SA from RESULT, the argument of the B instruction:

The target, END, of the BEQ above is address 1008. The location of the PC at the BEQ is 1003. MREL (2470) first subtracts the PC in variable SA from the target's address. Remember that RESULT holds the correct integer after the Array subprogram looked through LADS' array and found the label END. So 1008 minus 1003 gives 5.

BPL and BMI

BCS tests the result of the subtraction—the carry is still set if the target is higher than SA and, consequently, we've got a branch forward. We BCS FOR. Otherwise, it's an attempt to branch backward in memory, and we test the high-byte result of the subtraction (the number in the accumulator) against $FF. That high byte must equal $FF, or we've branched too far and we go to the error-message printout routine (2570). Then we check the low-byte result of the subtraction (which was pushed on the stack temporarily in line 2500) to see if it's a correct value. The PLA (2580) will set the N flag in the Status Register if the number is greater than 127. We want it to be, since this is a backward branch. If this flag is not set, we BPL to the error message. Otherwise, we jump to the concluding routine, setting up a correct branch.

The FOR routine handles forward branches in a similar way, going to the error routine if the high byte is not zero (2610) or if the low byte has the seventh bit set (proving it's greater than 127, an incorrect forward branch).

Let's pause for a minute to see what BPL and BMI do for us in this test. In binary, $80 looks like this: 10000000. We don't care about the bits in the positions where the zeros are. We're only interested in the leftmost bit, the so-called seventh bit. Note, too, that PLA affects the N and Z flags in the Status Register.

After a PLA of 10000000, BPL would not branch anywhere, but BMI would. It would mean that the seventh bit is

Eval: The Main Loop

set, the "minus sign" in signed arithmetic was found. The sign in signed arithmetic is held in the seventh bit. 1XXXXXXX would signify a negative number, OXXXXXXX a positive number. (There's a connection here with the fact that forward branch arguments can range from $00 to $7F, and backward branches from $FF down to $80.)

Now some people will point out that there are eight bits in a byte, and we keep referring to the seventh bit when we're talking about the eighth. Recall that, in computing, much counting begins with the zeroth bit. A byte can hold only the numbers 0-255. The lowest number it can hold is a zero. But that still means that there are 256 possibilities, 256 possible states for a byte: 1-255 plus 0.

Signed Arithmetic Branching

If all this seems an unnecessary detour into messy detail, consider how Relative addressing uses signed arithmetic to calculate where it should branch. When the 6502 chip comes upon one of the B branch instructions like BNE, it looks at the argument in a unique way. If the number is higher than 127, it knows it must go backward. If lower or equal, it must go forward. That's why you cannot branch further than 128 backward or 127 forward. The argument can't use the entire byte to hold a number—the seventh bit must be reserved to hold the plus or minus sign. Remember, if the seventh bit is set, it means minus. If clear, it means plus. BPL (Branch if PLus) is triggered when the seventh bit is clear. BMI responds to a set (1) seventh bit.

Take a look at the assembly in the example above. Line 120 shows that BEQ END became the opcode F0 and the argument is 03. 03 will take us to END because all branches are calculated from the address of the mnemonic following the branch instruction. Count three from address 1005. You hit END.

A branch backward, too, counts backward from the address of the mnemonic following the B instruction. All branches count from their own PC location plus 2. Look at a branch backward:

40 1000 A0 00 START LDY #0

50 1002 C8 LOOP INY

60 1003 DO FD BNE LOOP

70 1005 60 END RTS

Here line 60 is branch backward, but the argument, $FD, is pretty strange. $FD looks like this in binary: 11111101. So the

Eval: The Main Loop

seventh bit is set signifying minus, a backward branch. $FD is 253 decimal. $FF would be —1, $FE would be — 2, and $FD is — 3. From address 1005, —3 lands us at 1002, LOOP, where we want to land. Luckily, we needn't perform these calculations. LADS will handle all branch arguments. But you might want to use BPL/BMI branches as well as signed arithmetic in your ML programming. It's sometimes worth knowing the details of how these things are handled by the microprocessor.

One final adjustment needs to be made before LADS can POKE in the correct argument for branches. This adjustment takes place at RELM, where both forward and backward branches end up, unless they were found to be out of range.

After the low byte of SA was subtracted from the low byte of RESULT (2500), we pushed it onto the stack with PHA. That's sometimes a convenient place to stuff something you want to set aside for a minute while you perform other calculations. You could STA A or STA TEMP or put it in other temporary holding variables, but PHA is safe as long as you remember to PLA to leave the stack clean. You don't want to keep PHAing, or your program will soon fill up the stack, resulting in an OVERFLOW error and a machine-wide collapse. The 6502 chip won't ignite, the CRT screen won't melt, but the program will grind to a halt.

When we have a BRANCH OUT OF RANGE error we are going to go down to the DOBERR routine at line 5800, but we do need to PLA in lines 2560 and 2620 to keep the stack clean.

If there is no error, we've saved the result of the subtraction of the low bytes (it sits in the low byte of the RESULT variable). That's the number we really care about anyway. A single byte is all that can be used as a branch argument.

To make it a correct branch argument, we've got to subtract 2 from it. This, you recall, is because all branches are calculated from the address of the mnemonic which comes just after the branch instruction. Counting starts from the B instruction's address, plus two. Subtracting two will fix this up for branches in either direction.

Further Evaluation

We've seen how LADS calculates the branch addresses. At this point in the source code, we come upon a continuation of evaluations of other addressing modes. EVM05 (2740) gets the

Eval: The Main Loop

size of the argument in order to enable us to look at the character second from the end: LDA (ZERO),Y has a comma in this second-from-the-end position. INX NAME does not. By now, the variety of possible addressing modes has been somewhat narrowed.

If we did find a comma in that second-from-last position, that means the label ends in ,X or ,Y and we go to XYTYPE to deal with it. Otherwise, we check to see if it's a JMP (opcode 76). MEV eliminates two other possible modes, both Zero Page, sending LADS to the TWOS, two-byte, line-ending events.

We're headed for TWOS by now in any case, but we need to once again adjust the value of the opcode in OP if the type in TP isn't 6 or 4.

TWOS, like TP1 (for one-byte-long instructions) and THREES, is where LADS goes after an addressing mode has been determined. The opcode has been correctly adjusted and waits in OP. The argument waits in RESULT. TP1, TWOS, and THREES are quite similar. TP1 doesn't have an argument, so it just JSRs to a subroutine within the subprogram Printops. There, the bytes are POKEd into memory or to disk and PRINTed to screen or printer. Then LADS JMPs to INLINE to prepare for the next line of code.

TWOS (2970) and THREES (3400) also JSR to that same subroutine in Printops (which POKEs, SAVEs, or PRINTs an opcode), and then TWOS and THREES JSR to PRINT2 or PRINT3 as appropriate to store or print the byte or bytes of the argument.

Immediate addressing (LDA #12) is a variation of TWOS, but it first must make one of those adjustments to the value of the opcode before JUMPing to TWOs (see line 950).

THREES also requires some opcode adjustments before storing or printing its bytes; PREPTHREES (3240-3390) accomplishes that.

The JUMP subroutine (3010) handles the mnemonic JMP. It's a special case because it can have a strange addressing mode called Indirect Jump. JUMP tests for this and makes the necessary adjustment to the opcode if it finds the ASCII code for a parenthesis, indicating an Indirect Jump, for example JMP ($5000).

IMMED handles the # type, Immediate addressing. It first looks to see if the #" pseudo-op is in effect (3100) and, if so, stores the argument directly from the buffer. Then IMMED ad-

Eval: The Main Loop

justs the base opcode (in the OP variable) if necessary, and behaves like any other two-byte addressing mode, jumping to TWOS.

Preparations for a New Line

We come now to the cleanup routine, INLINE (3440). Its primary job is to handle the correct formatting of the printout of the source code. By the time LADS gets to INLINE, it's already printed a line's number, the address of the PC (the location of the code), and the object code bytes themselves:

line # /addr /bytes of object code 40 1000 A0 00

However, there are still three items to print: an address label (if any), the source code, and remarks (if any). To make listings easy to read, address labels should be set off by themselves, and source code should line up vertically on a printed page or screen:

line # /addr /bytes / addr label /source / comments

40 1000 A0 00 START LDY #0 ; begin here (entry)

Since each column should line up correctly, we're going to need to construct the ML equivalent of BASIC'S TAB function. Those first three items—line number, address, and object code bytes—can take care of themselves. But any address labels must always be in the same position on a line. And since there can be one, two, or three object code bytes, the address labels wouldn't line up if we just printed a couple of spaces after the final object byte.

TAB

The first thing INLINE does is to check if we're on the first pass. Nothing gets printed out on pass 1, so we jump over the entire INLINE routine. If it's pass 2, we look to see if the screen flag, SFLAG, is up (3470). If it isn't, we again jump past INLINE.

Then the LOCFLAG is checked. It is up when there is a PC address label (like the label START in the example above). If it's up, we use something from BASIC: the cursor position byte. We've been using BASIC'S PRINT routine all along. One of the advantages of this is that PRINT keeps a record in zero page of the current screen position; we could just LDA #20:STA CURPOS, and the next printout would be at position 20.

Eval: The Main Loop

Tab to Printer

Things are more complicated, though, since LADS has an option to print listings to a printer as well as to the screen. We cannot use the same technique with a printer.

To find out how many blanks to print to the printer, it's necessary to subtract the CURPOS value from 20. Assume that we've printed 14 characters so far: 20 — 14 = 6. We use this result in a loop to print blanks to the printer (3660) to cause a simulated TAB.

Following the TAB, we're set to print an address label which is still waiting for us up in the buffer FILEN. As usual, we set TEMP to point to the message we want printed, and JSR PRNTMESS, thereby printing whatever is in FILEN, delimited by 0.

Source Code Printout

It's time to move over to the thirtieth position (on screen or printer) to the place where the source code is printed. This is handled basically the same way as the TAB 20 above. The main difference is the BEQ and BMI checks (3920) to take care of extra long labels. In most cases, your labels will be less than ten characters long, but LADS allows labels to be any length. How will we balance the need for neat, vertically aligned printouts against the option of labels of any length? How can labels which potentially range in length from 1 to 200 characters be formatted?

Since address labels always start in the twentieth position, and source code always begins in the thirtieth position, we've allowed ten spaces for address labels during printout. Onscreen, an address label 12 characters long would be truncated: STARTLINEHERE would be printed as STARTLINEH. But on the printer, the entire label would be printed and simply push the source code printout over. You can adjust any of these formatting options rather easily if they don't suit your needs. If you want to truncate address labels to five rather than ten character lengths on screen, just change LDA #30 to LDA #25 (3830).

In INLINE, we've done some output switching between screen and printer. We've called upon routines like CLRCHN, CHKOUT, and CHKIN. The protocol for using these routines is discussed in Chapter 5, the chapter on peripheral communications.

Eval: The Main Loop

PRMMFIN (4000) prints the characters in the buffer LABEL. That will be the source code. Then, LADS checks to see if there was a < or > pseudo-op in this line. If so, it tags one of these symbols onto the end of the source code label. If your source code looks like this: LDA #>STARTLINE / the printout will be LDA #STARTLINE>. This will help to call attention to this special pseudo-op addressing mode. The < and > symbols are not buried within the label.

The underlying reason for doing things this way, however, is not its visual appeal. It's easier and faster for LADS to analyze #STARTLINE than to analyze #>STARTLINE. During the analysis phase, LADS pulls out the < or > and raises BYTLFAG to show that the pseudo-op was originally a part of the label. Then it can assemble the label the same way it would assemble any other label.

The final job to be performed by INLINE is to check BABFLAG to see if there is a REMark, a comment, to print out (4100). The Indisk subprogam sends any comments to the buffer called BABUF to keep them safely out of the way. BABUF is the same buffer that BASIC uses for input. If there is a comment, we print a semicolon (4130), point TEMP to BABUF (4160), and PRNTMESS.

Then a carriage return is printed and we check to see if this was the final line of the source code. If ENDFLAG is set, we go to the assembly shutdown routine, FINI. If not, we pop back up to where we first started this line, STARTLINE, and pull in the next line of source code.

FINI: Which Pass?

As a two-pass assembler, LADS, of course, goes through the entire source code twice. When we get to FINI, we need to check which pass we're on. If it's pass 1, we INC PASS (from its zero condition, thereby setting it). After this INC, the next time we reach the end of the source code and come to FINI, we'll be sent to FIN, the shutdown routine.

But assume we've just finished pass 1 at this point. What we must do is reset the PC, the Program Counter. Back at the beginning, we saved the starting address in TA. SA has been LADS' PC variable, counting up and always keeping track of the current address during each event. Now it's time to reset SA by putting TA in it. Then we close the source code file on disk and promptly open it up again. This has the effect of reset-

Eval: The Main Loop

ting the disk's PC to point to the first byte in the disk file. Now we're ready to read in the source code all over again. We're ready to start the second pass.

We jump back up, just below START, to SMORE and read in, once again, the first line of the entire source code.

If we've already completed pass 2, however, we don't want to restart source code examination—everything's already accomplished, POKEd and PRINTed and SAVEd to disk as the case may be. We want to gracefully exit the assembler. FIN (4390) does this. It closes down any read or write files on disk, closes down communication to a printer, and jumps to BASIC mode. Now would be the time to try the object code program, to make some adjustments to your source code if you want, and then SYS back into LADS for another assembly.

Each computer has a "side entrance," a warm start into its BASIC. This entrance doesn't wipe out what's in RAM memory, doesn't blank out the screen. It's here that the LADS goes to move gently back into BASIC mode. The address of TOBASIC for each computer is defined in the subprogam Defs.

Evaluating ,X and ,Y

Although FINI is the logical end of the evaluation process, it's not the physical end of the Eval subprogram. Just below FINI is XYTYPE where such addressing modes as LDA $5000,Y are analyzed.

They too require some opcode adjustments before going to TWOS or THREES for printing and POKEing. We JMP to XYTYPE after having found a comma in a source code line like:

LDA SCREEN,X

and so the Y Register is pointing to the character just beyond the comma when we arrive at XYTYPE. All we need to do is load BUFFER,Y to check if the character following the comma is an X or a Y. If it's an X, we jump down to L720 which handles X type modes.

Otherwise, we're dealing with something involving a Y addressing mode. It might be this:

LDA (15),Y

so we have to check for the right parenthesis. We DEY DEY to move over to just in front of the comma and see if there's a ) symbol. If not, we've got a Zero Page Y addressing mode like LDX 10,Y or STX 10,Y LDX and STX are the only two

Eval: The Main Loop

mnemonics which can use Zero Page Y addressing. They're rare. It's quite likely you haven't ever used them; it's possible that you haven't ever heard of them. But LADS must check for them just in case. LADS goes to ZEROY if there was no ) symbol.

LADS is likely to find the ), however, because Indirect Y addressing is a mode which is both common and useful. Encountering this mode, LADS goes to INDIR to process the Indirect addressing mode.

ZEROY (4660) is a somewhat misleading name, for it also handles the popular mode, Absolute Y: LDA SCREEN,Y. This addressing mode is not Zero Page. To find out whether it's dealing with the Zero Page Y, LADS checks the high byte of RESULT, the argument. If the high byte contains nothing, it must be zero page, and we process the opcode as such. If the high byte does contain something, the argument is thus larger than 255 and the opcode cannot use a Zero Page addressing mode. Again, the opcode is adjusted depending on the type (TP).

The routine at L700 (4950) prints out an error message because LADS was unable to calculate a correct addressing mode and the source code must contain a syntax error.

The concluding adjustments to the opcode take place between L720 and L809 (5040-5450). You might notice several JSRs to P in this section. P (5520) is a short subroutine which was used in debugging LADS, but was left in because you might want to use it when fixing up your own programs.

How P Works

P prints the current PC on screen, but doesn't destroy what's in the A, Y, or X Registers. Saving A, Y, and X is straightforward enough (5520), but where is the PC?

Whenever you JSR, the return address is pushed onto the stack. We can pull it off the stack with PLA, transferring its two bytes (one to the X Register and one to the Accumulator), and then push it back on with PHA. That leaves the stack ready to RTS correctly, but a copy of this RTS address is now in the registers as well, OUTNUM is a BASIC routine which normally prints line numbers during BASIC'S LIST. But it will print any integer number if the low byte is in X and the high byte is in A. (See Atari notes for Atari's OUTNUM.)

Character $BA on Commodore machines is a check graphics symbol (r ) t and it's a convenient way to show that what

Eval: The Main Loop

follows is not part of a normal LADS printout. You could use any other symbol to highlight the special nature of the number being printed by P. What's important is that you are alerted to the fact that somewhere within your ML program, you did JSR to P. And the number that P prints will be the address of that JSR.

How is P useful? An ML program is like a rocket. It's so fast that you need to send up balloons now and then just to mark its passage from subroutine to subroutine. When you're not getting what you expect (and that's often in large, interacting ML programs), you can put JSR P into various parts of the program. Then, as the program zips along, you'll be able to see what's happening and in what order it's happening.

P is like setting BRK into the code or putting STOP into a BASIC program. The difference is that P just gives you a simple location report and lets the program continue, uninterrupted. If you wanted more information, you could expand P to print the registers at the same time. With that, you'd be on your way toward constructing the single-step debugging feature available in some monitor programs.

CLEANLAB (5720) is janitorial. It wipes the main buffers clean. It puts 80 zeros into LADS' main input buffer starting at LABEL (see Chapter 9, where the Tables are described). We don't want any remnants of the previous line left over to confuse things.

Finally, DOBERR is the error message printout routine for branches out of range. It rings the bell (ERRING), prints the offending line number, then points TEMPS to its message (stored with the other messages in the Tables subprogram), and jumps to TWOS so that the Program Counter will still be correctly increased by two.

Now we've seen the innards of Eval, the main evaluation engine, the heart of the LADS assembler. It's time to turn our attention to the data base managers Equate and Array. They build and search the array of labels.

Eval: The Main Loop

as W

w

a S

H CO

B

H

Eh D

g H

s

D

_ Z

n h W g

5

co

o < l-H-fa

H

O

O Eh

P-. O

o

IS

a

Eh

a

Eh CO

cu o

<

Eh CO 00

-* Pn

Eh >H « Q Eh

u a

Eh O

a 0i

I*

K O E H

£

a

fa

o o

&H

o

Eh

z

w

Q

a

fa

c

E

o

Eh Eh

o

03 fa

2

o

Eh

w

CO Si

O O

>H

w

Eh iJ

D <C fa

w

Q

Eh Q

a a

CO <

a

fa

o •z

H

Eh

CO

H

J fa

X Z

o

fa J

w u K

a

o

CO

Eh

fa

O

.. Eh

Cu a cu

j a o

Eh Eh Eh

« CO E

Eh V W

CO * X

0u O

CO CO J CO CO

K S3

D H

Eh M

S

<

^

CO

K W K Eh O

O fa

u

w DG U

H H K

* s s

fa * a

w «; to

Q

E E

Eh Eh CO CO

pel

e

a

CD

M

CO J Del H W fa

Eh

u z

g M

a Pel X W

U Eh

a

U <

co K

< CJ

S u

O 3

J o

Eh

Pi CO

o

fa •«

Eh » CO

D

a fa

Eh _

CO CO O

- < <* Eh

>s co

OWUUE W CJ J D Eh

oa cq u < co

x

CQ

a a

w S3

Eh (N

u

u « o

PS w

w E

Eh

o •-

Si

E

Eh

CO

S

IS IS IS IS is

sss r~ oo e>>

tStSlSISIStSISISISISlStStSCStSISIStStSIS

iSi-HCNn^j-in^r^oDCTiiSrHtNnTjLnkor^ctic^

HHHHHrtHHHHNNNCv|(NN(NCNN(N

Eval: The Main Loop

co

g

m n

o

H

s

H

W

2

o

£

OS

O fa

« CJ

< W fa

u

< o

fa O

co

S3 K fa

5 a

< Eh

m

co

W

Q O U

w u

OS

D O

CO

O

co

W W

J < H fa

fa

CM > fa

a h

H

x cn a

U n H CD * CO

< fa w >h

Q 2 g H

j u m q

S3 O E- U Eh O !Z

a w h

HUO J fa fa

D fa O >h S CO fa S3 — Eh

a a

i-q iJ i

HHU fa fa K

fa Q fa < O H Eh fa CO

•z

w .- fa a o

fa

H

>H fa

Eh CO CO In

O

fa a o u

w o

fa

D O GO

fa

o

fa

Q

fa

o

o

a

H (H

Z

M fa fa

fa fa >

o

fa s

D

b

CD U

fa J

CO fa

in Q IS 2

H * H fa

O < <

x a Eh

co j co

o fa s

a ^

fa co D

Eh CO

fa CJ

fa H

a

w w

a o

Eh <

a w

H CO

co < fa

Q Eh g W CM

u

fa

•~ l-l fa

to »«

H CO

Q CO

a <

H fa

fa «c

CO Q

ID J

o

fa u o J

CQ

a

H Eh

fa a

fa fa

a

W

fa fa

-Eh

& a

fa

s a

Eh

Q

fa

fa

oo fa

H II Eh

* a

M

•• o fa fa fa

w

X

Eh

^

2

n h kD h in h

CM fa r~ fa \Q fa

CO fa * fa * fa # fa

fa

Eh Eh O

- a •- a Eh 03 h n h a

\0 fa 00 fa fa * fa * a, Oi

wfa<fa<fa<«<«<:fafa

acoQcoQcoQcocicoQcoco

< J fa X

fa a

<; m

Q fa

j <

Eh X CO

H

a w S a

CJ fa

a o

H H

a a

Eh H fa

2 fa

O W

CO fa

fa

CO D

H CQ

fa fa

w a

fa Eh

fa

D fa

CO. o

fa co H co m h

D H <

a

Eh fa

a

a Eh

M

Eh •- D fa fa ■

< < < < Q Eh Q Eh J CO J CO

StStSlS'SIISfSISSllSl'SOISISISllStaSSllStS'SGllSISllSlSQQISlIS

(SrHCNco^Lnvor^ooc^S^cNco^Lri^r^ooo>iSrHCNco«H/inuDr--coo>s rornrocororomrornro^^^^T?^^^t^'*ininir)ir>ininLr>ininiri*£

Eval: The Main Loop

i i

< <

CO

D CO

53

CO

co H

s

Q Q <

fa O

fa H

£ £

S fa

H <

Eh

H W

£ CO

CQ -

M O fa W

w fa

m o

s EH

p co

i

M I

M O

co <

53 H

fa J

D D

E-. CO

»B

§g

fa

a < <c

W Eh E-i 1-3 CO CO

< . Q CO J

fa

D 53

H fa D S

fa H W O 53

fa fa 53 H fa

as > a

U H fa

< CO

w a co

fa fa

fa PQ fa

O " &

fa e>

< CO

t<JH

53 fa

H Q >H

O 53 fa

a, w fa

fa

53 W

a fa

•• w

|H fa

w to

+ +

< <

CO E-i

< «C

CO CO

fa

CO fa

id 33

53 fa

H fa

J 33

E E-"

fa H

< W Eh

CO •*

ssisissiiscstssitsKKSiisiisisissiistststststscsisistscsisis iHtNr^^invi>t^cocT>cs>-itNf r >^l , Lnvflr--coc^S'Hojro^j"irior^oocTitSrH i^vo\D^vDvDvo^^^^r^r~i^i^r~r^t^r~cocDcococorocococococ^cTi

Eval: The Main Loop

Eval: The Main Loop

tatstststatsstastatsitatstatataistatatastaststarMntttatsts m^in^r^coc^tarHMro^incDr^cDO^tarHCNr r )'Hy.in^Dr^r~-t^r^cocTita

(N<NtMtNNINNnnClnrOCin(»lmnH/rt;^.rt;H/rf/ri/rt;n)'rf/rJri;H; u1 rHrHr-Hi-liHi-liHi-Hi-lrHi-tFHi-|i-li-li-(>HiHiHi-li-li-liHiHiH»-liHrHi-liHi-l

Eval: The Main Loop

K

w

fa

D ffl

9.

*£

W fa H D 33 CQ E- =

w a

W M H

81

o

CO

E-i 2 H £ D C3 — OS

J < w m fa

a o

u < K B

a u

H W

E-| H K

CD W

< «

K O

Dm E-<

in P5 vO H

A fa

fa

•- D

fa ca w

Cfi <

W E-i K W

a.

X <

o

O 2 2 >

h W

^ < O <C a CJ Z Q M E-i S U

h j m w o m

<<i<<>h»s:dhwu

QE-iQHQQSUJ

HtNin^i/noMl)ffiiSH(N(»iTfinifli^co(jiSHMn"*ir)*MiioiiSH ininiriinuiinininini^i£iii>ii)ixiixiiX)ii)vDvDr~i~-r--r~~r~r~t~r--r--r~-oooo

.Hi--l.-Hi--l.--li--l>-li-lr-li--ti--li-l>--lr-lr-li-(r-li-li-lr-liHr-lr-lr-(.Hi--l,--(i--|i--(,--|rH

Eval: The Main Loop

Eval: The Main Loop

<

Q

&H z w

£

D

<

fa

E-<

fa O

fa

35

u

CO

a w fa fa

D

CQ

fa

D Q <

U

s

H D

2

o

Eh

O

3

Q O

£

fa Eh < H Q W

D

P

O fa

J

o ca £

>h co

* H

D fa

co

a

w

CQ

fa

fa

Q O E

Q Q

<

fa >

M

< B

a in £

j m h

=»*= in

^ fa a n S w

• JUffl

co a m

<t z

* M

fa CO Eh *

fa D

M Q

< in - O J •-£

U n > oo

fa =«= fa =*t=

O

2

M

co

CO W fa Q

a <C

a w

M

fa

£

Eh 2

fa

£

D

a

<

a x

fa fa

O Eh

o

S3 H 2 CO H CO

w a: fa w

Q Eh co

q u h

< rf CO .

fa fa fa

Eh < 35 fa

U XEh >h

fa CJ 2 " fa fa

M Eh fa

Q W <

Eh

H H X fa

J X(5 J

Q Eh H o

S3 fa 2

Eh

<

Eh < X

X

X

W O __ p tsj O <C to

fa

Q O O fa

o o

Eh Eh

2

M

o fa

CO

H X Eh

a

Q D

3

fa a, F

Eh

fa fa

l-H M

D

•» 2 vc

HHHri Eh =«= £ *

faO<faOfafa<UU £WQ£H£2QiJQ Ufa.JCJCQUCQJU<

<faD<faO<fafa<U Eh£2Q£HQS2QJ

rH(Nro^inior~c0<^iSirHr»i(T)'*ini£>r~c0(TiiSrHCNro^inu5t--C0CT>Sr-(CN

HHHHHHHHHM(NMNCNN(NN(NN(«)nnmrlrOn(»)fOn<t^'^' MCVlfS(N(NCN(N(N(NCNf^tNtNCN(NfS(NtN(N}(NtNCNlCNCNCNO)(N(NfN(NCNrg

Eval: The Main Loop

oo

H

fa

W

Z <N CQ

DQ 00

oo o

w fa £

Q Q

H

00

< w w a > u

H Z H H

<

J EH

w oo 2 p

b W

J »

d a

Z w

O CQ

5S

oo oo

<

Cm

2 O

OO

oo

<

Cm

<d

Q

8

00

oo 2

CM O

00 u

o w

2 00

H fa fa;

a s 52 m ^ s

E-<

j

D

oo

w < fa oo

< V <

Q m k

J 00 fa

w

oo

fa

>h CQ

s

o

00

w

fate

D U

Eh

D

a

fa 00 « &H x O «/> Cm

fa oo fa ** s

< u oo cm a <

DDUEW J

ij oo m u cq cm

fa

w

CI

<

00 00 _

fa fa

s a; o

O H

fa oo a; d

m

H

2

fa fa

U CQ

Ss

° 9 u fa

I ~^ fa fa fa O fa

o o

Z Eh fa

53 u

fa Cm fa

S E

fa D U

o b

fa

fa fa fa

CQ

8 &

a ^

z oo

X

fa

fa £ fa J

BSSISSSSSSSSBS8S8518BBB851SSI8SSBSS

m^j-invor~-cx)(riiS)rH(Nro'i} , inkor~-coc^S'HCN< v )»ij>irivDr^coCT\iSiH(Nrn

(MNNNINNN(SCMMNNMN(N(N(NN(NtN(NNM(NM!NM!NCN(N(M

Eval: The Main Loop

w

KCSSCSQCSCSKStStSCStSCSStSSCSISlSCSISISISIStSCStsCaKIS ^invflt^roc^QiHCNrn^Lnixit^oo^QMCNrn^Lnixit^cocriCSrHtNfO'a' i^i^i^r~r--r^cocococococDoocococx)cTNC^c^^c^^<r>c^c^<yicsicais r^r^cNCN(Nr^rviCNcNCNrvi(N(NCNCNCN(NCNCN<NCNCNCNicsi<NCNrnrnrofnro

Eval: The Main Loop

sbssssssssbsbsssss is><sta<s<statata<sisita<s<s> SSssshhhhiHHhhhhinMm fNCNtNCNfscNfsnronronco

Eval: The Main Loop

t9tscsis)isicacats)is)(scststsic9tatstsscaiscscsiscs(sistscs)isicsis vor-cDt^cai-HcNroTi'in^or^oocTiisiiHcNfi^j'irikOt^cDCTiiSiHcNoo'tinvD rnrororoTH>^^Tt'H>"d , rj"^}"'tr('inuniriir)iriiriifiinir)uOvDvflvovo^ovo\0

nnncincinnnnnnnnnnnnnni'ii'inriKiinnrnnnn

Eval: The Main Loop

tas<siisssiss5i<sica<ssicscas5isitasisisiisis)isisis(ss>sits r~coffiSHNfnrf/ui*or~coiTiSH(Nn'*in\Br-cooiiSHMto'tirnor^ vDvD^i^t^r~t^t^r~t~i^r^r^cx)COa)COcococDCDCOcjocri^cJ<<yvfyi(Tio^tT>

Eval: The Main Loop

BSSSSSSBSBBBSlSISISISISiaB BISSQSSGlSSISIS

Q0ffitSi-icNn'i'invor--Q0CTiSi-icNnTtinkOr~ rawiSHNn'tuikBr^cD

Eval: The Main Loop

St?SSSSNiaiSISS511SSISlSllSBBlSlS51ISSlSISISISISlSIIS cAS-HCNro^tinvDr-co crita>-HCNro^.in>i)r^OD<^cs^H<N' r ) ■* miur-cocTi Nnfinnnnnnn n^^c^'Tt^'^ji^'^Tj'^ioiriininin in io in in in

^TH/^^^^^^^^^^^^'^*'*'*'J , 'H ; .'*'H«'J''*'*^t'"* , J'^}'' < *'i''*

Eval: The Main Loop

Eh

a

£ D O OS

o

Q S3

w

2 O OS Pti

OS

H

Eh

Eh

o

H B

a

Eh O

CO K S Eh

2 H OS

<

a,

Eh 33 U

Eh

8

H O

Q PS

O W

2 N

O

K5 Eh

H J

W D

W W

W H

2 2 o

Q b

tf O

Eh H

U H

H >n

K CQ

M

Q K

2 O

Eh O S5

Z 3

w w

- H Eh

H Eh

Eh > O O

&H p4

>H OS

O H

OS Q

tsl H

W Oj

>H

55

o

Q W CO

ffl

w u

>H >-< < Oj H Oj

W W Q £ g £

QaJUBb

O Eh

OS w

W D

si Fj Q •-<

is

00 •«

f> Oj CN

J Eh *

w < cu

53 a £

m j o

ts ts r» m h».

s .-h r-

0j

Eh

<c

Q IS

■■ J o

CN1 Oj

* o

ao<OB<acoa<pLi<uu<

W2W£iav02£5QiJQEH

CQU0DUmjUfflJU<IO

Qj

CO Eh

W

H < -H IS

OS a n is

X iJ in vow r--

Eh *2*B. J

IS

0j <Ti 0j Q < OS 0j

2 VD 2 W Q W £

ID J CJ DQ J 1-3 1-3

CJ

u

Eval: The Main Loop

CO =«=

U

Q <

H O < CO

co w 2

o

Cd

a; w

W CQ

o a;

O H

a; w w

aq O

2 E-i

&. w a; a: :z o

K E-i

w 2;

id K

IS

is a;

r- w

■ J id

w

Q O

E

a

o

w

Q O U

CO CL4 H O

2

o a

a +

2 2 2 .

H a pq K

Eh =H= E-i Oi

< < < a

En Q En W

W J CO 1-3

GO

z a a

p 2

Z Eh M CO

D

b

a <

a w S5 Eh

O

s <

2 0.

-O

w

< ts < <s co cu en

Q CO D "* vo O 2

J * J# O O Eh

tS IS (S

r-2r-2ZQJQEH2r~

in m in r- n r^ in r-

on ao- oS

W 2 W 2 H H

(ouffiua j

m

oo-

Q IS

IStSISIS<3tSKIStSISIS!SISIStSIStS<StSISISIS<SISISISISISK>tSS

HtMto^M/iiOi^cowsHMn^'in^or^oooMSHNn^'uiioi^coffiiSiiH

C^W0^ONCT\CriCTi(jN(3^ISISlSIStSlSISISSIS'-l'H'--<'-H'-H'-l'-H'--l'-,, -|CS, <N

^ , <t*^ , ^ , ^ i ^'^'*ininiflininuiiniflinininiflifiinifliniflinifliniflifl

Eval: The Main Loop

is

CN

ts <

tS Q r- J

fa in 2r-

U U u <

04

< o

Eh J

ss

fa «

O O

fa fa

fa

w fa

w fa

H Eh

Eh

u

H

fa fa O U

CQ

H

W Q O

£

S3 Eh

fa O

Ss

o

fa

EH

D O J

en o w

fa D3 H <C

Eh I

<

53 -

< <s

j vc

fa i->

X a

M 2

OS H

o z

fa CQ

X >h

Q »

iJ in

i-H O <S>

M IS

55 co-O

2 < W Q

53 J

s~

H Eh

m i-H

Eh

fa

w 2

a, o< o o

rH CO •-

=#= cs cc vo

H fa I--Eh S J

fa U 2

< ••

ffi !x O

<n m

w + ■•

H fa CN W fa CO

fa rH

fa =tt= D CQ fa

2

sr

u

IS

r-

Eh

o

53 w

p

fa a

H Eh

. fa

tS 2

CO hJ ^D

J tS

fa r-

2 J

b 2

fa Eh

IS

CN P» CN

* J * IS

CO fa w <

r- 2 S3 Q

fa fa

o o

U U rt!

J Q Eh

u < w

n i-H t/>

w * =*

H

H fa ff\ (Ti cr\ <

fa 2 S IS ts Q

EUCOnQOiritDJ

Eh j =»= J =tt= J fa

S IS

fa CTi a fa O fa OS fa 2t-H2H2WC0Cn

CO CM

< a

fa o

ON

ts U U

CO J Q J U <

StStatSStS'-HCNm'>tfintS<SCSIStS<StS!St3tSIStStSIStS(StS<SISC3 (Sn'fi/i^^t'-i^^i'-i^cooiiSrHMn'^invCt^cowiSHNfTj'inkO NNNN(~)(NNNMoiMtMNnnfocnnnmnnn"t^"*^>*rj'H;Tj ininininininininLnvnininininininininininLninininininininininin

Eval: The Main Loop

Eh H

CO

I

o

OS

h

N

X

Eh H

3

fern

<;>!<:<;«;<<

SSSSSSBSBSSSSSSSSSSSSSSSSSISSSSS X^is^cNfnM'in^r^fflc^S^r^rn^invDr^co<T'iai-ifsrO'H,Lnkcr--co

ini/iininininiAini/iini/iintriirii/iLnioininiriininininini/iininininin

Eval: The Main Loop

Eval: The Main Loop

+

CL in

Z in

W oj

J *

< ><

Eh 0*

CO W V

r- co Si

co oo r^

(N(N h

-* >tf in

Eval: The Main Loop

_

01 • ■

Z

tH

lX

ll X

w n

LL

at en a

Q

e a.

I- E

01 U

Si tS

t 1/1

M CM CM

Z LU

-J

E h

I CD

w

a e >

LU I- Z

iii 01 m

•ONCO

CN cm CM

z

LU -J LU E <x z

LL

LU

K O

*

Q J

E h

h Q

01 _J

Si Si O S CM W M

Si Si Si Si Si

n m t in n

f-'j W K' Vi t

»h N M

r-> r^ r»

N CM CM

4 4 «-

CD

-I 1

+ _J

<X LL

LO E

j a

j ix

<r a

h a

01 _i

<■ -i

S IH

CM K)

z

LU ll

O <H

iX z

LU LU

> ll

a a

LU IX

Z 01

IS 1

3 a

01 i£

X

a. u

E

"-J IX 01

n

cn w s in

111 UT <l -H M (0 fO t

u

X LTJ

LU

1

3

a

LU Si «»

■o a

* LU J

a ll _j ■

Si Si liT -i

in o* in m

76

Chapter 4

Equate and Array:

Data Base Management

1 1

Equate and Array:

Data Base Management

The job of setting up an array in machine language is simpler than you might imagine. The subprograms Equate and Array build and access a data base.

There are two basic ways to go about storing information: in fixed or in variable length fields. (A field in data base management means a single item, such as a single label name in LADS.) Fixed fields are easier to search, modify, and sort. Variable length fields save memory space. LADS uses variable length fields so the label table will take up as little space as possible.

A fixed field label system of managing data assigns a specified size in bytes for each item. If we had wanted to use this method of data storage for LADS' labels, we could have made a rule that label names cannot be larger than ten letters long. This would obviously make it simpler to manage the data.

However, then any label, even short labels, would always take up ten bytes. That would use up memory rather inefficiently. Instead, LADS allows labels to be of any length. If you are like me, the labels that you will think up naturally (without any restrictions imposed on your imagination) will normally average about five characters in length. Some will be longer, some shorter, but the average label will take up five bytes. Two bytes will be attached to each label to hold the integer number value which the label stands for. So, the average LADS variable (label name plus two-byte integer) takes up seven bytes. However, these variable length fields use up about 40 percent less memory when you consider that fields fixed at ten bytes would always take up ten bytes plus the two-byte number, never less.

Sons, Daughters, Clones

LADS itself is, of course, an ML program. You can have LADS object code assemble the LADS source code to disk or somewhere in RAM memory. This would create a new version of the assembler. If you'd made any changes to the source code, it would be an offspring, a son or daughter of LADS. If you didn't change the source code, you'd have created a clone, but the start address would differ.

Equate and Array: Data Base Management

LADS is about 5K long and uses 402 different labels. When it assembles itself from its own source code, it builds a label table which is 2851 bytes large. If it had fields fixed at ten bytes, the label table would be 4824 bytes large.

Why worry? It's true that the label table matters only during the actual assembly process. As soon as object code has been created and LADS returns to BASIC, the label table has served its purpose and can be tossed out like an eggshell after the egg is in the pan.

There are two good reasons for conserving memory: (1) the environment and (2) interactive freedom. Picture this: While assembling itself (or a comparably large program), LADS uses up about 8K of memory—5K for itself, perhaps 3K for the label table that builds down from the bottom of the assembler. And if you've chosen the option of assembling object code to RAM memory, add another 5K for the object code (the resulting ML program). A total of 13K. In some computers, this represents a significant bite out of the available memory.

What's more, LADS is supposed to be interactive. You are to have the psychological freedom you have with BASIC, to change things, to experiment, and then to quickly assemble and test the result. This means that you need space to write your source program (in RAM where a BASIC program is normally written). Perhaps you'll want a monitor extension in RAM too, like "Micromon" or "Supermon" or some other collection of ML utilities which permit single-step analysis of ML object programs, and other tools which are useful when debugging object code. And you might want "BASIC Aid" or "POWER" or some BASIC auto numbering, and other BASIC aids to manipulate the source code. You might want two different versions of your object code in RAM simultaneously so you can compare them in action.

The Programming Environment

All of these options require available RAM. If you can have them all in memory at once, you've got a better environment for developing an ML program. You won't always need to wonder if it's worth loading in a certain routine or utility: They're all there and ready to go. All your tools are at hand. This is a more efficient way to program. Tools that are out of reach are usually tools left unused.

Second, you want as few restrictions as possible when

Equate and Array: Data Base Management

working with ML. You don't want to concern yourself about the length of each label name. Is it short enough? Does it duplicate a similar name? Eliminating these questions, too, is part of the interactivity, the mental freedom that comes with a smoothly running, efficient program development system. Variable length labels promote both effective memory conservation and an efficient programming environment.

Equate

The Equate subprogram starts off with one of those LDY #255 initializations. Remember that we don't always want to LDY #0 before a loop. There are times when the first event is the zeroth event. This is one of those times.

Line 40 sets Y to 255 so the INY in line 50 will make Y = 0. This allows us to LDA LABEL,Y and receive the first character in the buffer called LABEL. If we had set Y = 0, the INY would have forced us to look at the second character in the buffer. Why not put the INY lower in the loop somewhere? That way, we would load in the first character the first time through the loop.

Obviously we can't INY just before the BNE in line 90. That would branch depending on the condition of Y itself, not on the item in A (which is our intention). For the same reason, we can't put it just before the BEQ in line 70. The only other safe place for it would be in a line between 70 and 80. That wouldn't do any damage to the branches because the CMP will reset the flags and the following BNE will act correctly.

This loop isn't moving characters from one buffer to another or anything. Its sole purpose is to count the number of characters in a label name, to find the length of the label. Y is the counter.

While locating Y in a line 75 would work correctly, it would be less clear what the loop is accomplishing. In cases like this, you have to decide where your personal priorities lie: Do you want to emphasize the function of a routine in a way that's more easily understood, or do you want to emphasize a uniform style of coding loops? If you prefer to always start such loops with LDY #0, by all means, go ahead. But that LDY #255 serves to alert you that this loop is a special kind of loop. If you come back later to modify a program, such signals can be helpful.

Equate and Array: Data Base Management

Once the length of our label is discovered, we add 2 to it by INY INY, to make room for the two-byte integer which will be attached to the label in our array. Each label stands for a number. And any legal number in ML can be stored within two bytes as an integer between 0 and 65535 ($0000-$FFFF).

Equate is called upon only during pass 1. On pass 1, the assembler puts each label into the array and attaches the two-byte integer onto the end of the word. So Equate's first job is to find out how much room to make in the array for each new label it comes upon. It makes room by lowering the MEMTOP variable by the length of the label name, plus two.

Building the Array Downward

SUBMEM moves our pointer down to make room for a new label. When SUBMEM is finished (200), the array is larger by the size of the new word we're adding to it, plus two bytes for the value of the word. The array is thus expanded, lowered.

Now we can store the label in the array. The first letter of each label in the array is special. It's shifted. That is, we add $80 (128 decimal) to the normal ASCII code value of the character. This is the same as setting the seventh bit.

If the label is "addnum," we want to store it as 'Addnum" so that when we later search through the array, we can locate the start of each new label. The shifted letter will be our delimiter, separating the different labels. With fixed length fields, we wouldn't need a delimiter at all—each label would be exactly the same size as every other label. But our labels can vary in length, so we have to know where one begins and another ends.

The array will look like this (the xx is the two-byte value of each label): AddnumxxSecondwordxxThirdwordxxFourthlabelxxFifhlabelxx

What exactly does it mean to say that a letter is shifted? In the ASCII code for alphabetic, numeric, punctuation (! or . or ,), and symbolic (# or % or *) characters, everything is assigned a code number which is lower than 128. Above 128 are the uppercase versions of letters, etc. Hence, above 128, the characters are shifted. For the purposes of ML, a shifted character is something with an ASCII code value greater than 127. It has the seventh bit set in its byte: 10000000. That leftmost bit would always be up in any shifted character. This phenomenon

Equate and Array: Data Base Management

makes it easy to distinguish between shifted and unshifted characters. We can just LDA CHARACTER and then BMI (branch if seventh bit up) or BPL (branch if seventh bit down). The subprogram Array will make good use of this clue.

For now, all we want to do is shift the first character before we store it into the array. We just set up the seventh bit. If that's the same as adding $80 to a character, why not simply ADC $80 instead of EOR $80 (230)? With EOR we get a 1 if either of the compared bits is set. We get a 0 if both bits are 1 or if both bits are 0. The only way we get a 1 is if one of the bits is 0 and the other bit is 1. Any other situation results in a 0. Look at a bit comparison:

1EOR 1 = 0

0 EOR 0 = 0

1 EOR 0 = 1

Consequently, EOR $80, with the $80 (binary 10000000) acting as a mask, will leave all the bits in the Accumulator unchanged, but will set the seventh bit. The main reason to use EOR is that we don't have to bother with clearing the carry (CLC) as we normally would prior to any addition.

After we store the shifted first letter in what is currently the lowest position in the array, we INY. This serves two purposes: It points us to the second character in the label word and also points us to the second space from the bottom of the array (where the second character of the label word belongs).

Address or Equate?

Now we load the second character and check if it's a space (260-280). We might be dealing with a one-character-long label, like P. We've got to check for this eventuality. Finding such a short label, we would jump down to see if there's an = sign. But if the label is more than one character long, we store the second letter in the array (290) and jump back up to fetch and store the third and any additional letters in the label name.

The essential thing to notice here is that a space is our delimiter in the buffer—letting us know when we've reached the end of the label word. And after finding a space, we are then prepared to distinguish between the two types of labels: PC and equate.

We compare the character following the space to $3D (this is the = sign). If it is an = sign, we branch to the routine

Equate and Array: Data Base Management

which assesses the argument following the equals sign (is it hex? is it decimal?). Otherwise, we go through this BEQ to the routine which handles PC-type labels (Program Counter types like: LABEL LDA 15, where the label indicates a location within the assembled program).

Storing the value of this kind of label is pretty simple: We just put the SA into the array. SA is the variable which always holds the current address during an assembly. But one thing remains to be done before we can return to the Eval subprogram to evaluate the LDA 15 part of this line. We've got to wipe out the word LABEL which precedes the LDA 15. Eval wouldn't know how to evaluate it. It's not a mnemonic.

After loading LABSIZE (the length of the label) into X, we load Y with 0. Y will point to the first space in the buffer, while X will count down until we've covered over the word LABEL (430).

Removing an Address Label

We load the leftmost part of the mnemonic/argument pair (the L of LDA is first), and we store it in the leftmost space in the buffer. In other words, the L of LDA covers up the L of LABEL. We continue with this process until we've loaded in a 0 and have therefore replaced LABEL LDA 15 with LDA 15, whereupon we store the final 0 as a delimiter and can return to Eval (510).

This next subroutine, NOAR (520), isn't in any sequential relationship to the other routines. It just happens to be here. It could be anywhere else in LADS just as easily. Its function is to ring the error bell and point TEMP to the message NAKED LABEL and then print that error message. It handles those cases when a programmer forgot to put anything after a label:

00 LABELrlNY

or 100 LABEL

or 100 LABEL =

Equate Labels

If we're not dealing with a PC-type label, though, we come here to store an equate label like LABEL = $22 (590) into the

Equate and Array: Data Base Management

label array. We need to store Y first (in the variable LABPTR) so we can remember where in our array to put the value, the number following the equals sign. Remember that we've already stored the label name. What we need to do now is to put the value in the two bytes just following that name. When we arrive at this subroutine, Y is holding the correct offset from MEMTOP, the correct distance up in memory, from the bottom of the array to store the value.

There are now two possibilities. We are dealing with either a decimal number or a hex number. Hex numbers are translated by Indisk, the input subprogram, as they flow in from a disk file or RAM memory source code. So a hex number is already in the RESULT variable, waiting to be stored in the array.

But decimal numbers aren't translated as they come in. What's more, they arrive in ASCII form and must be converted into an integer by the subprogram Valdec.

We check the HEXFLAG to see if it's a hex number (610). If so, we can just put RESULT into the array and return to Eval (750).

But if it's a decimal number, we add the value of Y + 3 to the start-of-buffer address and point TEMP to the first character in the number we need to evaluate. We have to add this three to Y because the expression "space-equals sign-space" takes up three bytes. If we add this to the start of the buffer address, we're pointing to the first character in the number, pointing to the 1 in an example like: LABEL = 15.

Then we JSR to VALDEC, which looks at the number pointed to by TEMP and translates it from ASCII to an integer and puts the answer in the two-byte variable RESULT.

After this, we go through the same process as with hex numbers described above. The RESULT is transferred to the array, we pull off the two-byte RTS left on the stack (when we JSRed here from the Eval subprogram), and then jump back into Eval at INLINE, the place where a new line is pulled in from disk.

Array

The Array subprogram is essentially a search routine. It looks up a label's name in the array that was built by the Equate subprogram. When it finds a match, it puts the integer value of the array word into the variable RESULT. In effect, Array replaces a

Equate and Array: Data Base Management

label with its number. Here's an example fragment of source code:

10 *= 864

100 NAME = 2

110 LABEL = 15

120 START LDA LABEL

On pass 1, Equate would store "Start864Labell5Name02" into the array. The LADS label array builds down from the location of the start of LADS object code in memory. That is, the first part of LADS itself would be right above Name02. Line 120 contains two labels, START and LABEL. However, Equate ignores any labels which are not the first word in a given line. It only stores labels when it comes across the line in which they are defined. Any label being defined will be the first item in a given line. And if they are defined twice in the source code, that's an error.

(Note that, in the example of array storage above, Start864 is for illustration only. The number 864 is stored as a two-byte integer, not as 864, the ASCII characters we can read.)

While Equate ignores any label which is not the first thing on a line, Array ignores any label that is the first thing on a line. In the example above, Array would pay no attention to any of the labels except LABEL in line 120. It's Array's job to evaluate expression labels. An expression label is one that is used in an expression, one that is used as the argument of a mnemonic.

Array Works on Both Passes

Nevertheless, Array must operate on pass 1 as well as on pass 2. This is because pass 1 must keep an accurate PC, an accurate Program Counter. For Equate to store the correct number for labels, of the address (PC) type (like START in the example above), it must be able to find out precisely where in memory a given line is to be assembled. It must know that START is located at 864.

This problem derives from Zero Page addressing. LDA 15 takes up only two bytes in memory when assembled. LDA 1500 takes up three bytes. If labels were used in place of 15 and 1500 in these instructions, we must know whether to raise the PC by two or by three. So Array must look up all arguments on pass 1 to decide how much to increment

86

Equate and Array: Data Base Management

the PC. (This PC, or Program Counter, is held in the LADS variable SA.)

In line 30 where Array begins, it moves the "bottom-of-LADS" (top of array) address from its permanent storage place, the variable ARRAYTOP, to the dynamic, changing pointer PARRAY. PARRAY will be lowered frequently as it points us down through the entire array.

Then we JSR to DECPAR which is the subroutine that lowers the PARRAY pointer by 1. And we stuff a $FF into the flag called FOUNDFLAG (90). This is a simple way to test if we've found our match. If we do find a match, as we'll soon see, we INC FOUNDFLAG. This means that FOUNDFLAG can more easily be tested in the way we want to test it. If it gets INCed once, it will be 0. INCed twice, it will be 1. INCed twice (or more) would mean that a label exists more than once in the array. That's an error, a redefined label, and we'll want to alert the programmer. Putting $FF into FOUNDFLAG thus allowed us to use BEQ to test for this error.

Checking for the Bottom

But all that comes later. The primary routine in Array starts with STARTLK (100), and oddly enough, the first thing we do is check to see if we're at the bottom of the array. The Equate subprogram always leaves the variable MEMTOP pointing to the bottom of the array. So, by subtracting our current position in the array (PARRAY) from the bottom of the array (MEMTOP), we can tell if we've finished looking through the array. If PARRAY is lower than MEMTOP, the carry will remain set, and we will then BCS down to the all-finished routine, ADONE.

Otherwise, we've got to keep on looking. Remember that Array must look through the entire array each time; even after it finds a match, it must continue looking for another match. This is the only way we can detect duplicated labels.

Array has to accomplish several things at once. It's got to point to the current position in the array, keep track of how large a given label is, and check each letter of each word. The chip registers will all be busy: A holds characters for checking, X keeps count of how large each label is, and Y (working with PARRAY) keeps track of our current position. Here, in line 160, we set X to zero.

Then we lower PARRAY by two to get past the number

Equate and Array: Data Base Management

part of a label stored in array (170-230). We want to get past the 99 in /Label99/. Some of the stored numbers will have their seventh bit set; they'll be larger than 127. So we've got to jump over every stored number since the set seventh bit is our test to see if we've come upon the first character in a label name. We don't want numbers masquerading as label name delimiters.

At last we look at a character (260), and if the seventh bit is set, we BMI down to FOUNDONE. If it's not the start of a label name, we decrement PARR AY by 1 and jump up to LPAR to look at the next letter lower in memory within the array. Notice that we also raise the X (label length) counter (320). By the time we've found a shifted seventh bit indicating the start of a label name, X will hold the correct length of the name.

Double Decrement

Let's pause a minute to look at how a double decrement works (280-310). If, upon loading the low byte of PARRAY, the zero flag is set, we would be forced to lower the high byte of PARRAY (PARRAY+1 in line 300). If the low byte isn't yet lowered to zero, however, we can just lower the low byte and ignore the high byte (310). Note that a zero in the low byte requires lowering both the high and low bytes. Correctly decrementing $8500 would result in $84FF, lowering both bytes, while a correct decrement of $8501 would just lower the low byte: $8500.

Once we have located a set seventh bit, thus locating the start of a label name, we come to the FOUNDONE subroutine (350). Here we must first store PARRAY into the temporary holding variable PT so we can remember exactly where the label name begins. Then we reload A with the first character of the label (390) and compare it against the first character of the label we're looking for. That first character was previously in the variable WORK just before we came to Array from Eval.

If these first characters match, we go to LKMORE to check the rest of the word for a full match. If not, we go to STARTOVER.

In LKMORE, we first raise X to be the correct length of the current array label under examination. Then we save it in the variable WORK+1. We've got to save it at this point because now X will serve as the counter of the source label length. The

Equate and Array: Data Base Management

source label is the word we're looking for, the label from the source code we're trying to find a match to.

The fact that some labels will be like (LABEL),Y or #LABEL (having a ( or # as their first character) is a potential source of confusion to the Array search routine. To eliminate this confusion, whenever a ( or # is encountered during the Eval subprogram, a special flag, BUFLAG, is raised. That makes it easy for us to skip over them here by raising the Y offset (490) if necessary. __

Paradoxically, we simply INY again, right after this. That's because we want to point to the second character in the label (we got this far because the first characters matched). Nevertheless, the combination of INY and DECPAR (490-500) effectively takes care of the ( or # situation and makes this INY point to the second letter of the label proper.

The LKM1 loop compares the entire rest of the source label against the array label (520-600). There are three ways, and only three ways, for us to get out of this loop. We can come upon a zero, which would surely be the end of the label in the buffer (the source label). A zero always means the end of a line of source code. Or we can come upon a character which is lower than 48. That includes things like left parentheses and commas in the ASCII code. Something like the comma in LDA LABEL,X would signal the end of the source label. (Checking for characters lower than 48, however, doesn't exclude numbers. We can still check for such legal labels as: LDA LABEL12.)

The Third Exit

The third way to exit this loop is when we fail to find a character match in the labels. Any point at which this happens, we "fall through" line 600—these characters do not BEQ, they're not equal. If they are equal, we go back up to check the next pair of characters. Notice that X continues to count the length of the words (580). In effect, it is counting the length of the source label (we already know the length of the array label and have it safely stashed away in the variable WORK+1).

If we leave this loop with a match, it will be a zero or a comma or right parenthesis in the source label that causes us to leave. X will then be holding the length of the source label. It's possible that we'll find an apparently "perfect match" which isn't, in fact, a match at all. For example, LABEL (as the array label) and LABE (as the source label) would appear to this

Equate and Array: Data Base Management

LKM1 loop as a perfect match. The only way we have of knowing that they do not really match is to compare their lengths.

If we fail to find a match, STARTOVER (620) just restores the correct array location of PARRAY (pointing at the first character in the label that just failed), and then we lower PARRAY by 1 (660) and jump back up to the STARTLK routine. STARTLK will also lower PARRAY by 1. This double lowering of PARRAY moves it past the number stored in the two bytes at the end of the next label down, thus preparing us to start the comparison process all over again.

On the other hand, if we did find a match, we go to FOUNDIT (950). Right off the bat, we check to see if the current value of X (length of the source label) matches the previously stored value of X (length of the array label). If they don't match, we've got that LABEL LABE situation, and we STARTOVER.

If everything checks out, though, we've got an authentic match. We raise the FOUNDFLAG. If this is the first match, FOUNDFLAG goes up from $FF to $00. That's fine. There should be one match. If, however, FOUNDFLAG is higher than 0, it means we've found more than one match, and we JSR to DUPLAB where the "duplicated label" error message is printed out (1360).

With or without this message, we next compensate for the ( or # symbols which might be at the start of a source label and then load in the low byte of the number stored just above the array label. We put this byte into RESULT and put the high byte into RESULT + 1. When we arrive here at FOUNDIT, the Y Register is pointing just past the end of the label. In other words, Y is pointing at the number stored with the label in the array. This is because we left the LKM1 loop when we got to the end of the label.

Pseudo-op Adjustments

Here's where we make the adjustments for two of our pseudo-ops: > < and +. If BYTFLAG is set, it means that < or > was used to request the low or high byte of a label. LDA #<LABEL requests the low byte (and Eval will only deal with low bytes in the # Immediate addressing mode). The label's low byte is already in the low byte of RESULT, so we need do nothing. But BYTFLAG is a special kind of flag. It has three states rather

Equate and Array: Data Base Management

than the normal two (set or clear, up or down) states. If it contains a 2, this signals that the #>LABEL pseudo-op was used, requesting the high byte of the label. To do this, we need to put the high byte of RESULT into the low byte of RESULT (1140-50). That's it.

PLUSFLAG signals a + pseudo-op like LDA LABEL+ 25. The amount we're supposed to add to LABEL (the 25) is already stored in the variable ADDNUM (by a subroutine in the Indisk subprogram). All we have to do here is add ADDNUM to the value in RESULT (1180-1240).

When these two pseudo-ops have been taken care of, we return to STARTOVER and keep looking for duplicated labels if we're on pass 1. On pass 1, we aren't allowed to leave the Array. On pass 2, however, it's not necessary to repeat this checking or to repeat the error messages, so we RTS, which sends us back to the Eval subprogram.

We've successfully put the value of the source label into RESULT. Now the Eval subprogram can go on to figure out the addressing mode, finish up by POKEing in the opcode and the argument, and then pull in the next line of source code.

But what if we didn't find any match to the source label and we've gone through the entire array? This can mean two things, depending on which pass we're on. On pass 1, it's harmless enough. It could well mean that the label hasn't yet been defined:

100 INY

110 BNE FORWARDLOOP

120 INX

130 FORWARDLOOP LDA 15

On the first pass, the label FORWARDLOOP will not be in the array until line 130. Nevertheless, the Array subprogram will search for it in line 110. And it won't find it. But so what? On pass 1, we can just ignore this failure to find a match and RTS back to Eval.

It would be a serious error, though, if the label could not be found in the array on pass 2. It would be an "undefined label" error.

When a Label Was Never Defined

Both of these possibilities are dealt with in the subroutine ADONE (690-940). If FOUNDFLAG has the seventh bit set,

Equate and Array: Data Base Management

that means that it's still holding the $FF we put there at the very start of Array. We never found the match. We check the PASS, and if it's pass 2, we print the line number and the NOLAB error message "undefined label."

Then, no matter which pass it is, we still want to keep the program counter straight, or all the rest of the assembly will be off. The problem is that an undefined label doesn't give us the answer to the question: Is this a three-byte ordinary address or a two-byte zero page address? Is it LDA 15 or LDA 1500? Should we raise the PC by two or by three? If we raise it the wrong amount, any future reference to address-type labels will be skewed. Here's why:

100 *= 800

110 LDA LABEL; this label is undefined

120 ADDRESS INY; what is the location of ADDRESS here?

If LABEL is in zero page, ADDRESS = 802. If LABEL is not zero page, ADDRESS = 803. We should try to get this right on pass 1. Pass 2 depends on pass 1 for correct label values, including address-type labels. Even if a label is not yet defined, we should still try to raise the program counter by the correct amount.

In Eval there are routines called TWOS and THREES. TWOS raises the PC by two bytes for Zero Page and other two-byte-long addressing modes like LDA #15. THREES handles three-byte-long modes like Absolute addresses, etc. It's here in the Array subprogram, however, that we have to decide which of these routines to jump back to in Eval.

Branches like BNE and BEQ will often be undefined during pass 1 because the program is branching forward. We'll want to go to TWOS if there's an undefined label following a branch instruction. All branches are type 8, and we can easily check for them by LDA TP:CMP #8 (860). The other possible TWOS candidate is one of the > or < pseudo-ops. BYTFLAG signals one of them.

Equate and Array: Data Base Management

The # Immediate addressing mode is not tested for, so this adjustment isn't foolproof. The assumption is that any undefined label is essentially a fatal error and that there will have to be a reassembly. Most undefined labels are considered to be three-byte instructions and we JMP THREES (920).

This clarifies why LADS cannot permit the definition of a Zero Page address within the source code. All Zero Page address labels must be defined at the start of the source code, before any actual assembly takes place. Without this rule, our "yet-undefined-label" routine (690-930) will treat them, incorrectly, as three-byte address modes. It can recognize only branches and > < pseudo-ops as two-byte modes. Any other label that's not defined will be seen as a three-byte type.

Equate and Array: Data Base Management

w

E3

o

a

z

H

H « U O Eh

e-i ca < i

D <N O^

H W

2 Pi < o B

-*.

H H

a. p

<

to a

w w

WHO

j « w

W Q Eh

oq a z

< < H

w u

Eh a.

3 $

>

w ca

Eh

<C Q D J O D

w o

w Eh

OQ « I H <N

£

z I I

Eh

a

CM

o o J

o

Eh

<

Eh 00

a

o

K W IS)

O

Eh

= U Cn

Eh W

l-H [SI M

oq co

S OQ

o

Cn W rsi

O H

Z W H

E J

Eh H

O OQ

a <

Cm O O

J

hQ Eh

W W PQ 03

3

J D

Q <

W

2.

Eh Q

K * 2

W 2

in l in l CN l =tt=

oo

w

w w

cc h

w a.

x « e

o

o w w

H

H U

g <

M Cm

i-Q W

- w

Jh OQ

53

w w

< H

Q K

g W

D DC

O Eh

fc, o

H < O Cm O Q W 2

r-H CN <N

SBSSS8

m »* in vo »• co

O W

>H

g H

OQ

IS

ts ts

(Tl r-H

W

-» CO w <

M

o

6m W OQ S

38

>H

ca a

<

>< 2

O «

w

Eh

Z

H

O Cm

3

s o

CM « O Cm

Eh

2 W

H N

2 H

CO

J

w

m 2

Eh U

I Eh

I OQ

I D

I 00

W I SI I H I

oo a

a

>H

Eh

CO

CJ Cu N Cm

+

Cm

O

Eh

2

Ed 5)

2 =-*

« H

Eh

Eh

W J

Eh

CO

D

w

Eh

Ph

H

35 CO

w 5

o

Eh

oo

M OQ

+

Cm 33

O Eh

Eh I-2

W Eh 5)

S h*

W !S

m oo

£

z

H W

I

w

CQ

3

o

z

H

a;

o

Eh

CO

Cm O

Eh

CQ

6J

u

<:

CM OQ

Ch >H M

H

>H OQ CN

Z < to

M J =t*=

!S Q (S IS Q Q S3

<U«;tn>H<CH 1 <nrt:cM QD3EhWOQOEhOD2 JWW .-jMWWWhHU

BQtsinlssisBtsisiis cowtsSHMn^in'Br-

i-trHCSfNfNrNrMtMCNCNCM

Equate and Array: Data Base Management

o5

<

o

H

2

M «

w

Eh H W

H X

Ed

a

w K

Eh O

W

m

S 5

H

a, u

<C D O

w

o a

H

Em

Eh 2

D O

Oj h

w

W II

«

o Eh

Ed U

p w

•- Z EC

>< H O » H

— Z S

a o o o o z

Eh

s - •-

tN Pd n >h

a s o z a — w h

D

to

Q

2

fci

o

Eh

o o

Ed

D4

Ed

Eh

<

D

a w

En

>H M J •-

Ed Q PQ oo < </>

CD

3

W 2

E

Ed

CO DQ

Z <

H J <

Eh 05

Z Ed

O Eh

U En

w

CO H

J Ed

< EC

D Eh

O O Ed

< Eh

to EC -— O

M

Ed 05 J CQ Eh

ai m

M

05 Ed

< 05 > O

CJ CO

Cm

Ed !*

EC » E-i —

a, o o

CO Eh

s

- Ed

< E C0^

Ed CQ

Q 05 Ed Ed N > >h

£~°3

I

U

Q Ed 05 Ed Ed > 03

to

Ed

Ed

2

« Z Ed

a, cd

£ aa

3 <

X J

Ed

Ed

05 EG

O Eh

Eh

o

>H Z

05 in

Ed H

Em

Em cC

D Q

CQ J

Ed

EC CO

Eh Ed 2

2 O

O CJ

05 Ed

En CQ

Cu Ed

O N

Eh m

m 2 CO

+ Ed CQ

< 2 <

CO--- J

Cd DC

Eh

Em O

Eh

to

Ed 05

Ed

35

Eh

Ed 05 •

<: j

Cm <S

Ed > 05 Ed Cm

>H

O CQ H

>H - J

X J

pJ s

Ed 05

CQ O

< Z

O

to

Eh

2 Ed 2 D CJ 05 rf.

O

2

Q 2 D O

Em

Ed CQ <

Q

Ed

< 2

Ed 2

H J Eh

>H iJ Eh

Ed O

<

to to

Ed 2

05 O 05 05 Ed

Ed DO

<

Q Ed

<c

2

Eh

2

M

05

Cm

Ed CQ

O Z

H

05

m < O Eh Ed to

EdEnSODSEdEdQEH CQtOI-jEdJUCQQHjtO

<<XX>HinO<:x QEHDEdQOEdEHZ

jradQdwffloiH

04 ^f CO 2 a Eh ^ Ed 05

Em O

z

05 H

CO 05

l^ 05 Ed 05

< 05

o CO

2 >r>

05 U

Eh

2 05

Hi

05

to

CO CO Ed S

Eh Z

+ Cm

2

Ed 05 ^ EX,

< < < < 05 Q Eh Q Eh CO J CO J CO ID

SSiSIISISIISSIStaSSBISISCSISIStSlSSllSBISISSinlSlSlaiSS OOi^SrH(Nrn^invor^coo>SiHogro^Lnu3t^oO(rilS)tHCN(Nro'a , irivjOr^

Equate and Array: Data Base Management

Equate and Array: Data Base Management

Equate and Array: Data Base Management

OS

X E-i 2 X C

u

X N

X

ss

Di

+ x x x

X X

b u w

X Q Q 2

J X a <c X

X CO

<

x 3

U X

z <

H X

X

X X

2 2 H l-j

<

x x Eh

2

H

X

z

J w m

s§

H

< H <C

Q U W O

Eh J

a a o 2

J H

Eh

X OS

> <:

- Eh

X W

3

X

tH Eh

< m

a

3 «

< w x oq

2

Q

o

w

o a x x < x

K <

X 2

Eh

a

b W

O Eh Eh

X W

x J

Eh

Z* Eh

M X

J H

Eh X

X H

D

a

o

3

Eh

X

w

B

Q

2

H

x

X

PS >h x

-2

— o

>H U

Q 2

x o

O D 3

o

X O X

06 +

< ^

X W < x X o

< Eh >H S W Eh

05 -O 2

x a

U H

D X

a Eh o H

2

X H

o o ..

J X

X

- >

X O

a; eh o «

2 <

X Eh X X

x a — 2

<: < < x a x

a eh a 2 w 2

j x i-5 u x 1-3

x

X

x

Eh

2

5 c u

X

Eh E-

O H 2

X K

J x

DP X 2 X x 2 H

3

g

2 W

X

Eh

a

X X

X a

X O

X 2

m a

M

Cd X O

E< Eh

z a

H W Q

65 OS O

2

M

X

X >h Eh

65 65 W Oi H

2

2 H

O

X

w

CO

Eh

65

2 a

O 65

U 65

2

+ X

X

O H

2 *

X X

Eh Q X J

X

O

x x

Eh O

65

< W

2 W

Cm

o u

o

E-

X

65

a

2

X X

^<

X 65 3

O J

X

u —

Eh +

< a

2 O

<~

Q IS 2

D H O H X U

w

65 < >

65 § 2 X

Eh 2

65 X X 63 Eh 2

O « J

ts

a «

o

2 X

J Eh

X

X 6j < O J

Q 2 65

X

□ J

X X

I

X X X X X OQ X X

x -

u x

X 3

X CJ X

Eh

Eh

>H ►.

» Eh X

X H O

X X PQ rt J

O J

X X

X X X X

a

m

X X Eh

<

X

X O

X D X 2

X X

X X

2 X X

a x o 3

X

x x x

X

X

X

Eh

X

X X

X Q

X X

a o

<: 3

X

w

X X

Eh

Eh

< o

2

>H M

<C X

« o X o

X X

>* Eh - 2

— O

>H U

ISIS)lSiaiStSISISlSS!SISISIS!SIStSS!SS(SIStSISISISISISISISllS

Equate and Array: Data Base Management

z

2 ° ^

a x

H Q Eh 05 z

O H

3 9

a.

Eh

X O

X E-i

E5 Z

Eh < J

OS

* 8

° 9

o < J

Eh

O OS CO < Eh » CO X CJ w

<"°

E 05

O

O 2

O i-i >

w 05

a,

Eh

D

x

Eh

X

<

Q

X W >

o

Eh X <

Eh

X

3 < >

2 o X X

O

w

X

05

x s o J

x x

05

<

bs5

J

Eh

a

< x

Eh Eh CO

«- z

Q

>h 05,

CQ O

2

05 W 05

Eh X

z x

H Eh

o o Q4 z

S HK

X

Eh

+

>H 05

X 05

+ 05 U <

Eh < X Eh

X X Q CO

< < 05 X DE-MS

to w j co i-i b

X

< &

a Eh

z -

D Z O Q h h

Q

a •-

J H

to

05 — o co

CO CO X CN 05

w a

CO I

H CN

X <

a 05 < <:—-

CO CO

co Q < W M

S Z Eh

X Eh

a < x

X 05 CQ E-*

Eh

u

i» •

X E- J O CQ Z <

E^

Eh

X z

O H H

S E-

c

- Z

CO CO iJ

< x

X CQ CO

2 a

CO Z

CO CO CN M <

a, ..

X

CO H X Eh Q Z

05 < PQ

O

z

H

D5 05 X

05 CO

x

X CJ

Z < 03

H X ft

J CO J

Eh Eh O X

Z Z Z E

05 05 v h

X X =t*= Eh

05 05 < < <

co co a ^ a

w

o

co co

X

E

a z a p

En

E^ O Z

H

aril

J x

CQ

o

z

H 05

CO

CO

r-l K 05

+ E CJ

X Eh Eh

E Z Z

H 05 Q5

Eh X X

Eh

CJ =i

05 Eh

co

z

X CJ

z

o a x x

CO

05

o

x z

< 05 05 O

Eh CO CO Q CO l-j b <

U

X X CJ O v

< .. J -CN X CN

HlOOt-O

X m ^h Q >h Q

o *=«=<: m <c

>< < q x a < x x a z s x a z

X X 3 CJ X X X

sstscscssKisatstatacsKcscsscscsisscscscsjistscscststscs

^(Nro^LrivDi^mCTvtSi'HCNro^Lriver^COcTNS^cNro^tLri^jOr^cXJC^CS'-H ^v0iX>vDvX)^>X)O^i^^^r^t^i^i^^i^t^roc0CtlC0C0C0Ct)XC0X(TiC3>

Equate and Array: Data Base Management

co o 3

CO Eh fa

H fa

« S

E ^

Eh

J H

<

fa

D

J D C 3

Q fa

« fa

2 Eh

z

E- H

fa a

O Oh

^ • E

Eh U

en E-

z <

< a <

E

O 2 63

J

J O

63 ^

co

D fa O

U fa 63 E Eh

cj to

+

fa >H

fa W O E

3 ^

X ~ fa fa CJ Q

2 D O h

CO

w o

< to co

fa

BS

O OS

—. fa E 63 U

< w

S 03

<

to

« 2

H C fa n — Eh

«C O U

fa h w a

N fa D

D —

E-Z

a j «

fa fa

E fa « CJ CO tS

Eh H

rf < 2 £ 05 <C

E

Q - Eh

63 O

fa rf DJ HJK

< fa E fa Q O

Z H -DE

a: o

K fa fa > W

o u

Eh Z •-fa M X

< fa

EhO

" Q fa

Z

d a

o fa

fa 03

C Z <

US

c fa

fa

Eh

< CO

Z fa

fa

+ o fa a

fa

o

3

J >H

fa Q

CO

Eh

D CO

eg a:

fa a

< >

fa

< J

63

J a

<

e-

fa

fa

z

H

fa

Cm O

a

w

CO

fa

fa o

CO

>H + O

< Eh <

2 j j

a d fa

< CO Eh fa 63 >H — DJ 03

fa

Eh

m 3

o o

Eh

z

fa

Eh

>H

fa

E

o

H

E

fa fa o

Eh CO

O >H fa <

DJ Z O Eh CQ H fa CO

>H < < <

2QEQ HjtOJ

O

fa £ <N

u * a a,

63 E 03 U

fa

o

o 0

D 63 CO

fa

z

O

a

Q <

O D

fa

CO

D fa fa

63 CO CO

05 63 63

3 2 fa

63 < <

Z Q Eh

03 J CO U

Eh J

D CO

o

Eh

s

D Z

a

Q

fa w

CO CO 63 63

a a;

a o < u < <;

63 fa Q Q Eh a 03 U J < CO J

IS ts CN m c^ On

S S S S Q S <tf in vd r~ CD o^

0> C^ CT* C^ CT> C^

ts is ts

tS .-H CN

ts ts ts

tstststststststststststsiscsisiscstststs ro^finvot-^cocTNtS'— icNn^irno^cocMSr-icn

SSSBSISSHrHHHrHiHr-IHHHrSINCS i-li-li-li-lp-Hlr-lr-(.-lr-li-H:i-H]i-lt-lt-lr-lr-li-(i-Hi-l,H

Equate and Array: Data Base Management

m

fa

g

8

u

to

Q Z fN

2 O

+ +

H E-

J fa

D D

CO CO

w w

2 as

3

W fa Z H

o u

o w

w

D Q

o

fa

O

o

w w

< fa

a

o

>

w

o

Eh

Q < Z

fa H

fa Z

<: m

a:

fa > O

fa <

iC E-i U CO <

fa fa £

o b 55

x - z co w

& §

fa <

fa fa

fa

Eh

2 H O

fa

< a fa

fa fa

5 o J

fa fa

< fa fa

4

fa

u fa

D O

U H CJ W CO H Z W Q Eh Q 03 Q 2 fa

fa O

<c w

fa

fa as

fa fa fa

fa fa fa

fa z

H

fa

fa

fa

fa

fa

o

z

H

fa

o z

H

fa fa CO

fa < fa fa fa to fa l-i Q

fa

a fa fa

D fa Q

£ S

fa A Eh =«=

< < Eh D CO fa

fa

CO

H fa fa

+ 2 O

fa Eh Eh

s z z

w fa 2

Eh fa fa

z

fa

fa o

fa J

< fa fa CO H Eh CO CO En fa CO l-j »-J fa •

<

i i i . i i i i i i i i i i _ i _j _j _j i t . i i i

Chapter 5

Openl, Findmn, Getsa, and Valdec:

I/O Management and Number Conversions

-

Openl, Findmn, Getsa, and Valdec:

I/O Management and Number Conversions

I/O (Input/Output), a computer's method of communicating with its peripherals, is one of the most machine-specific and potentially complex aspects of machine language programming.

Sending or receiving bytes to or from disk or tape drives and sending bytes to a printer are the most common I/O activities. A large part of a computer's ROM memory is usually devoted to managing I/O.

I/O is machine-specific because each manufacturer invents his own way of managing data, his own variations on the ASCII code, and his own disk or tape operating systems.

And I/O is complex because printers and disk and tape drives differ greatly in such things as how fast they can store bytes, how many bytes they can accept, and esoteric matters like timing, error checking, and special control signals.

ML programmers are frequently advised to perform I/O operations in BASIC and then SYS, CALL, or USR into the ML after the hard part has been accomplished by the computer's operating system. This works well enough with small ML projects. But it can become awkward in a large ML program. LADS itself must open and close disk files pretty often. It would be inefficient to require LADS to fly down into an attached BASIC program for this. Also, large ML programs are easiest to save, load, and use if they are written entirely in ML.

Fortunately, we can access BASIC'S ROM routines from within an ML program. Certain registers and pointers in zero page need to be set up, then we can JSR to open a file to a peripheral. After that, we can send or receive bytes from that file.

Since these routines are so machine-specific, we'll look at the Commodore techniques in this chapter. See Appendix C for an explanation of the Atari and Apple I/O techniques.

Commodore I/O

Some peripherals are intelligent and some are dumb. Commodore disk drives are highly intelligent—they've got large amounts of RAM and ROM memory. One consequence of this is that relatively little I/O computing needs to be done within the computer proper. A Commodore disk drive is a little computer itself. You can just send it a command, and it takes over from there.

The tape drives, though, are dumb. ROM intelligence within the computer must manage I/O to tape. Some printers aren't so dumb, but since you can choose from so many different models and brands, the computer just sends out a sequence of raw bytes when you print to a printer. Your BASIC or operating system makes no effort to control fonts, formatting, or any other special printer functions. You are expected to send any necessary printer control codes via your software. If the printer is equipped to TAB or justify text, that's up to the printer's ROM.

Openl

In the subprogram Openl, there are four Commodore-specific subroutines. In many respects, they are identical subroutines. Each opens a file to an external device in much the same way. Only the specifics differ. The first subroutine, OPEN1, starts communication with a disk file which will be read. That is, the source code will come streaming in from this file so that LADS can assemble it. This file will be referred to as file 1.

The second subroutine, OPEN2, opens file 2 as a write file. If the user includes the .D NAME pseudo-op within his source code, the results of a LADS assembly, the object code, will be stored on disk in a file called NAME. OPEN2 makes the disk create this file.

The third subroutine, OPEN4, creates a simple write file to the printer. It, too, is similar to the others except that there is, of course, no filename.

Looking at OPEN1, the first event is a call to the CLRCHN subroutine within BASIC. All I/O (including that to the screen and from the keyboard) is governed by this opened-files concept in Commodore computers. The normal I/O condition is output to the screen and input from the keyboard. CLRCHN sets the computer to this condition. It is a necessary preliminary before any other opening or closing of files.

106

Openl, Findmn, Getsa, and Valdec: I/O Management

Resetting the Disk Program Counter

Next we close file #1 (50-60). This resets the disk intelligence. As we shift from pass 1 to pass 2, we've been reading through file #1 to bring in our source code. On pass 2, we want to start all over again with the first byte in the disk source file. It is necessary to close, then reopen, file #1 to force the disk intelligence to again point to that first byte in the file.

Next we must prepare some zero page file-manipulation pointers. We store the file number to FNUM, the device number (8 is the disk device number in Commodore computers) to FDEV, and the secondary address to FSECOND. All of this is precisely what we do in opening a file from BASIC with OPEN 1,8,3.

Then we have to point to the location of the filename within RAM. LADS holds filenames in a buffer called FILEN, so we put the low and high bytes of FILEN's address into the FNAMEPTR. Then, at last, we go to OPEN, the BASIC subroutine which opens a disk file.

The four zero page locations and the OPEN routine in ROM are all machine-specific. They are defined in the Defs subprogram. OPEN2 is identical except for a different filename, a different file number, and a different secondary address (which makes it a write file).

OPEN4, too, is identical except that the secondary address is ignored, the device number is 4 (for printers in Commodore computers), and there is no filename.

Line 430 reveals a fifth zero page location which must be POKEd before calling the OPEN subroutine in BASIC ROM. It holds the length of a filename. (Opening to a printer uses no filename, so a zero is put into FNAMELEN [430].)

Both of the other subroutines, OPEN1 and OPEN2, do not need to POKE FNAMELEN. It is POKEd just before LADS JSRs to either of them.

LOAD1, the final I/O subroutine in this subprogram is used with the assemble-from-RAM-memory version of LADS. In this case, the source code files are LOADed into RAM before they are assembled. This means that we need to imitate a typical BASIC LOAD of program files.

The LOAD subroutine within BASIC requires that the LOAD/VERIFY flag be set to LOAD (rather than VERIFY), that 8 be declared the device (disk), and that the name of the program to be loaded be pointed to. Then the machine-specific

LOAD routine within BASIC is called. After that, the program (the source code) is loaded into the normal RAM address for BASIC programs.

Findmn: Table Lookup

This subprogram is similar to the Array subprogram: Both look through an array and find a match to a "source" word. Yet Findmn is simpler than Array. It doesn't need to check for word lengths. Also, the numbers (the values) associated with the words in the array are more simply retrieved. Findmn tries to find a mnemonic like LDA or BCC in a table of all 56 of the 6502 machine language mnemonics.

This table (or array) of mnemonic names is in the subprogram Tables at the very end of LADS source code. The mnemonics table starts off like this:

50 MNEMONICS .BYTE "LDALDYJSRRTSBCSBEQBCCCMP 60 .BYTE "BNELDXJMPSTASTYSTXINYDEY

and continues, listing all of the mnemonics.

This array of mnemonics is simpler and faster to access than our array of labels because it's what's called a lookup table. It has four characteristics which make it both easy to access and very efficient: It's a fixed field array (all items are three bytes long), it's static, it's parallel, and it's turbo-charged.

Charles Brannon, my colleague at COMPUTE! Publications, is a proponent of what he calls "turbo-charged code." He writes an ML program, gets the logic right, and then takes a cold look at things, especially at heavily used loops. Is the first CMP the one most often true in a series of CMPs? Or would it be faster to rearrange these CMPs in order of their probability of use? Should an Indirect Y addressing mode be replaced by an even faster structure such as self-modifying Absolute addressing? Would a lookup table be a possible replacement for some computed value? Sometimes, small changes can result in extraordinary gains in speed. For example, after LADS was finished and thoroughly tested, it took 5 minutes, 40 seconds to assemble itself (5K of object code).

A cold look, about five hours of work, and the resulting few minor changes in the source code brought that time down to its present speed for self-assembly: 3 minutes, 21 seconds. (This speed test was conducted with only the .D name pseudo-op activated, on a Commodore PET/CBM 8032, with a 4040 disk drive, and involving far fewer comments than found with the

108

Openl, Findmn, Getsa, and Valdec: I/O Management

source code as published in this book. The use of additional pseudo-ops, additional comments, or other computer/disk brands and models will result in different assembly speeds. The Apple has a faster disk drive, for example, and the LADS Apple version is even faster than the Commodore version.)

How does this mnemonics lookup table differ from the label array? They're both arrays, but the label array is a dynamic array. It changes each time you reassemble different source code. A lookup table, by contrast, is static: It never changes. It's a place where information is permanent and lends itself, therefore, to a bit of fiddling, a bit of turbo-charging.

A Special Order

First of all, in what order did we put these mnemonics? They're not in alphabetical order. In that case, ADC would be first. They're not in the numeric order of their opcodes either. Using that scheme, BRK would be first, having an opcode of 0. Instead, they're in order of their frequency of use in ML programming. The order wasn't derived from a scientific study—I just looked at them and decided that I used LDA more often than anything else. So I put it first.

The reason for putting them in order of popularity is that every line of source code contains a mnemonic. Every time a mnemonic is detected, it must be looked up. Since this lookup starts with the first three-letter word in the table (all mnemonics are three letters long) and works its way up the table, it makes sense to have the most common ones lowest in the table. They'll be found sooner, and LADS can continue with other things. It turns out that rearranging the order of the mnemonics in the table resulted in an increase in speed of considerably less than 1 percent, but everything helps. The principle is valid, even if it doesn't accomplish much in this case.

The second quality of a lookup table—parallelism— is rather significant to the speed of LADS. Right below the MNEMONICS table in the Tables subprogram are two parallel tables: TYPES and OPS. (See the Tables subprogram at the end of Chapter 9.) TYPES can be numbers from 0 to 9. It is handy to group mnemonics into these ten categories according to the addressing modes they are capable of using. Some mnemonics, like RTS, INY, and DEY, have only one possible addressing mode (they take no argument and have Implied addressing). They are all labeled type 0. The branching instructions, BNE, BEQ, etc., are ob-

Openl, Findmn, Getsa, and Valdec: I/O Management

viously related in their behavior as well: They are type 8. This categorization helps the Eval subprogram calculate addressing modes. This table of TYPES parallels the table of MNEMONICS. That is, the first mnemonic (LDA) is type 1, so the number 1 is the first number in the table of TYPES. The fifth mnemonic in the MNEMONICS tables, BCS, is paralleled by the fifth number in the TYPES table, 8.

The Efficiency of Parallel Tables

What's the value of putting them in parallel? It allows us to use the Y or X Register as an index to quickly pull out the values in any table which is parallel to the primary lookup table, MNEMONICS. Once we've found a match within MNEMONICS, we can simply LDA TYPES,X to get that mnemonic's type. And we can also LDA OPS,X to get the opcode for that mnemonic. All this works because we INX after each failure to match as we work our way up through the MNEMONICS table. X will point to the right item in each of the parallel tables, after we find a match.

But now on to the actual lookup techniques which are used in the Findmn subprogram. As usual, we set our index counters, X and Y, before entering a loop. X gets $FF (40), so it will zero at the first INX at the start of the loop. Y gets 0. You can tell that this was the first subprogram written in LADS. Nowhere else can we achieve the elegant simplicity of calling a loop LOOP and the end of the routine END (390). After using them once, we'll have to come up with other names for loops and exits.

Anyway, we enter LOOP and look at the first character in the MNEMONICS table (60). If it matches the first character in the buffer LABEL (holding something like: LDA 15), we jump down to look for a match to the second, and then the final, character in the mnemonic. Otherwise, if there is no match, we INY INY INY to move up three characters in the MNEMONICS table and prepare to compare the first letter of the second mnemonic against our source mnemonic.

When looking something up, it saves time if you just test first characters before going on to whole-word tests.

Assuming a first characters match, MORE (150) compares the second characters. If they match, we go on to MOREL This time a failure to match results in two INYs because there was one INY at the start of MORE. MORE1 tests the third characters. If it fails, we only need one INY In each case, a failure returns

Openl, Findmn, Getsa, and Valdec: I/O Management

to LOOP. LOOP itself fails when it has exhausted all 56 mnemonics in the table and no match has been found. Since each attempt causes X in INX, we can test for the end of the table of 56 mnemonics by CPX #57 (120).

If we have exhausted the table, we jump back into the Eval subprogram where label definitions are evaluated. Since we didn't find a mnemonic as the first thing on a source code line, it must be a label like:

100 LABEL LDA 15

or

100 LABEL = 75

JMP for JMP

Note that we don't need to PLA PLA the return address of an RTS off the stack before JMPing back to Eval from this subprogram. That's because we JMPed here from Eval. Both possible returns to Eval will be JMPs. That makes it possible for us to JMP directly to Findmn from Eval. For speed, we can JMP back to two different places within Eval, depending on whether we did or did not find a mnemonics match.

Finding a match, however, sends us to the FOUND subroutine (300) where we check to see if there is a blank character or a zero (end of line) following the supposed mnemonic. If there isn't, that means we've got a label which looks like a mnemonic: INYROUTINE or BPLOT or something. We can't let that fool us. If there's a character in the fourth position, such words reveal themselves to be labels. If so, we go back to Eval via NOMATCH.

But let's say that all was well. It's not an address label, it's not an equate label, it's not a label disguised as a mnemonic. We've located a true mnemonic. All we have to do is pick its TYPE and OPCODE out of their tables and store them in their holding places, the variables TP and OP, and JMP back to EVAR in Eval. EVAR is a subroutine in Eval which examines the argument of a mnemonic to determine its addressing mode.

Getsa: The Simplest Routine

This subprogram has only one mission: to point to the starting address in the source code program. Here's what it points to:

10 *= 864

Openl, Findmn, Getsa, and Valdec: I/O Management

Getsa pulls off the first six bytes (in a Commodore disk program file) so that it can check to see if the seventh byte is the * character (120). If so, Getsa returns to the calling routine in Eval (200). If not, it prints the NO START ADDRESS error message and goes to FIN (190), the shutdown (return to BASIC) routine.

Conditional Assembly

There are two fundamentally different versions of LADS. The version presented as object code (to be typed in) in this book assembles from disk-based source code. You create BASIC-like "programs" on disk, and then LADS reads them and assembles them without bringing any source code into RAM memory.

An easy modification to LADS, however, will allow it to assemble directly from source code within RAM memory. A few trivial changes to LADS' own source code and you can assemble a new, memory-based LADS. These changes are described between lines 430 and 640 of the Getsa source code printed at the end of this chapter. The changes are described in greater detail in Chapter 11, "Modifying LADS."

But this Getsa source code illustrates one way that your source code program can conditionally assemble. Notice line 210. The MEMSA and CHARIN routines below it will never be assembled. When LADS sees the .FILE pseudo-op, it will immediately turn its attention to the Valdec source code. .FILE shuts down the current file and switches to the named source file, ignoring any additional source code in the current file.

Thus, to assemble the "conditional" part of this source code, all you have to do is move .FILE below the new source code. See the instruction in line 580 of this Getsa subprogram. That's how you do it to create a memory-based version of LADS.

Another way to conditionally assemble is to insert the .NO pseudo-op, thus turning off object-code-to-memory-storage until the .O pseudo-op turns it back on. You could write your own .ND (no storage to disk) pseudo-op if you want to control assembly which is sending its object program to a disk drive. Another pseudo-op you could write would be something like .NA for No Assembly which would cause LADS to simply search down through source code (taking no actions other than building the label array) until it located a .A pseudo-op, turning all assembly back on. These .ND, .NA, and .A pseudo-ops aren't

Openl, Findmn, Getsa, and Valdec: I/O Management

built into LADS, but would be easy to add if you felt you'd have a use for them.

Valdec: Number Conversion

Numbers such as the 15 in LDA 15 are held in ASCII code format within source programs. In other words, when LADS pulls in the 15, it doesn't get the number 15. It gets 1-5 instead. It gets the ASCII for 1 and the ASCII for 5: 49 and 53 decimal. (As an aside, 1 and 5 are $31 and $35 in hex. It's pretty easy to mentally convert ASCII hex to numeric form. Just drop the leading 3 from any hex ASCII number.)

What Valdec must do is turn 49 53 into the two-byte number OF 00 which the computer can recognize and work with. This is just a bit more complicated than it might seem. The complexity comes from the fact that the 1 in 15 is really 10 times 1. The Valdec subprogram which handles this ASCII-to-integer translation will have to multiply by 10,000 or 1000 or 10 or 1— depending on the position of the ASCII digit. We don't need to worry about numbers higher than 65535 since ML doesn't often need to calculate higher than that. All addresses that the 6502 chip can reach are within that range, and two bytes cannot hold a larger number anyway. Therefore, multiplication by 10,000 will take care of any case we might come across.

And since 10,000 is just 10 X 10 X 10 X 10, we'll really only need a way of multiplying by 10 a maximum of four times. So all that's really needed is a multiply-by-10 routine that we can loop through as often as necessary. Lines 400-550 perform this operation.

But let's start at the start. Anything in LADS which calls upon Valdec for its services will have already set up the TEMP pointer to point to the first ASCII character in the number to be translated. Also, the number will end with a 0 delimiter. (This isn't the ASCII 0, which is $30. It's a true zero.)

Determining Length

After Valdec finishes, it leaves the results in the two-byte register called RESULT.

First Valdec finds the length of the ASCII number (50-90). Our example number, 15, would be two bytes long. Its length is stored in the variable VREND, and we then clean out the RESULT register by storing 0 into it (130-150). Then X (not the reg-

Openl, Findmn, Getsa, and Valdec: I/O Management

ister, the variable) is stuffed with a 1 (170) so it can tell us how many times to loop through the times-ten routine for each digit. As we move from right to left, reading first the 5 then the 1 in 15, X will be raised. Coming upon the 5, X will be 1, and we'll perform no multiplication. The first thing the loop for multiplication does is DEX, so 1 becomes 0 and we exit the loop (250).

Coming upon the 1, X will tell us to go through the times-ten routine once. In other words, we multiply 1 times 10 for a result of 10. This, added to 5, gives the 15 we're after.

But let's back up to where we were, at VALLOOP (180). We can take advantage of the fact that the ASCII code was designed so that the lower four bits in each ASCII numeral byte hold the actual number: $35 stands for 5. How do we extract the number $05 from $35? We could subtract $30. Even simpler is AND #$0F. AND turns bits off. Wherever a bit is off in the mask (the #$0F in this example), the bit will be off in the result:

$35 (ASCII for 5)

AND OF (the four high bits are all off,

the four low bits are on—they have no effect) $05 (the answer we're after)

00110101 ($35, prepared to be stripped of its high bits by)

AND 00001111 ($0F, the mask, turning bits off where the 0's

are)

00000101 ($05, leaving the number we want)

Here we load in the rightmost character, the 5 in 15, the $35 in $31 $35. And strip off the 3, leaving the 5. Then that's stored in two temporary variables: RADD and TSTORE. Next we fill both of the high bytes of these variables with 0 (220-240). That makes them officially correct. Nothing lingers in their high bytes to confuse things later when we perform two-byte addition.

Now that our digit 5 is safely tucked away, we need to multiply it by 10 as many time as necessary. DEX lowers X. With this first character, X becomes 0, and we BEQ to the exit (330). When we come through this loop next time, holding the 1 in 15, X will become 1 and we'll therefore JSR TEN (270) one time, making 1 into 10.

Keeping Track of Position

After the subroutine TEN has multiplied the number in RADD (named for Result of ADDition) by 10, we transfer the result

Openl, Findmn, Getsa, and Valdec: I/O Management

from RADD over to TSTORE (280-310). Why the transfer? Because in the 100's position, a digit would need to be multiplied by 10, twice. The 2 in 215 would have to be 2 times 10 times 10. So TSTORE has to keep a running total of the results achieved by the TEN subroutine. TEN uses RADD during multiplication. Obviously, a second two-byte variable will have to keep track of the total as, more than once, we multiply the larger digits by 10.

Another running total, the result of all Valdec's efforts, is kept in the variable RESULT. That will ultimately hold our final answer. But each time we achieve an interim answer on a single digit, we JSR VALADD (350) to add the results of that digit's multiplication to RESULT (570-640).

Meanwhile, back up at line 360, we DEY to point to the next higher digit, the digit next to the left. And DEC VREND to see if we've reached the end of our ASCII number and cannot RTS. If not, we go back up and load in the next digit, continuing to add to the running total in RESULT.

The multiply-by-ten routine called TEN (410) is worth a brief examination. Let's imagine that we have put a 1 into RADD (200) and we're going through the TEN loop once, multiplying it by 10. We clear the carry. ASL shifts each bit in RADD (the low byte of this two-byte number) to the left by 1. The interesting thing is that the seventh bit goes into the carry. Then we ROL RADD + 1, the high byte, which rotates each bit to the left. This is the same as the ASL shift to the left. The seventh bit pops into the carry. But with ROL, the cany moves into the zeroth bit. A combination of ASL ROL shifts all the bits in a two-byte number to the left by 1:

Carry bit high byte low byte

0 00000000 00000001 (our 1 before ASL low byte,

ROL high byte) 0 00000000 00000010 (after)

You can see that this, in effect, multiplies these bytes by 2. If we ASL/ROL again, we get:

0 00000000 00000100 (the original number, mul-

tiplied by 4)

At this point, our answer is 4. We've multiplied the original 1 by 4 with an ASL/ROL combination, performed twice.

Now we CLC again and add the original number (1) to the current result (4), giving us 5 (460-520). It's easy to see that all

Openl, Findmn, Getsa, and Valdec: I/O Management

we need to do now is one more ASL/ROL, which multiplies the running total by 2 one more time:

Carry bit high byte low byte

0 00000000 00000100 (4) + 0 00000000 00000001 (added to the original 1, gives)

0 00000000 00000101 (5) then, we just ASL the low byte:

0 00000000 00001010 (10) ROL the high byte (which has no effect on this small a number):

0 00000000 00001010 (giving us 10)

That final ASL/ROL multiplies 5 times 2, and we've got the right answer (530-540). This trick—multiply by 4, add the original number, multiply by 2—will work whenever you need to multiply a number by 10. Other combinations will multiply by other numbers. And as Valdec illustrates, you can calculate powers of 10 by just running the result through this TEN subroutine as often as necessary.

Openl, Findmn, Getsa, and Valdec: I/O Management

§

Openl, Findmn, Getsa, and Valdec: I/O Management

o

OS H

CO

Q

a

BSB8SSBSSSSSSSSSSSBSSISSSBSIB1SSIS1S SHNfo^inior-cooMSHiNn^inkOi^cooMSi-icNro^in^Dr^oocTils

Openl, Findmn, Getsa, and Valdec: I/O Management

Openl, Findmn, Getsa, and Valdec: I/O Management

Z Z LU

Ul LL

ll o

o

tfl

LiJ

0"J

LU

.J >-<

-< u.

Ll

h-h- D

IVl LL

a. (-

z 3

'1 Q O

LU

u. tn ll

_j

LU IU U UJ

_J LLl UJ

m tn a ul

LL h-

o a

LU h I- U] H

h- D J

>■ LL ii M it

an- u h ll u t-

D LU i—i LU "1

LU O IX hi X

I U LU D U UJ

K O ll. LU

h UJ ■« h- Z _J "• h-

i- a _i ^ a w m n a

LU LU I" -< Z Z LLZZ

CD H <E u. UJ K S

>■ O Q.LL LUh-CLU.

cn a: i- o i-i (B 3 5 ih

> 3 D LL CE IX Cl LL 3

III LL >'■ LU LU <• (- ■' LU

«-i CC th ~ Z <I CO SS i-t C£ Z <I <t UJ 0) «-i EC <h D <I ^ to

+ I> + Z OI-h rn + > hQIHO 0 + > Z OQUJO

llccuzz a: cnLtLLLCQ-Lr _j ld _j a_ _j a. cc lu jidj

OQQLCITCDd LU 3030Q LU OUOUOQ 0. LU OO

Z Z M 5 a O 0. H- LU V Z A Z Z UJ »h _J v Z A Z Z O O CO CN J V

LlLl«Q.Cl#— wH#Ll#U-Ll OLUG#Ll#LlLl#LL olucj*

LT. > _J UJ _J UJ

<rccii:<r>><itn3CQ<r<i<i<a:cr:tnuoa<i<ra:<ici:<i<rtnLJoac:

t-U)tDHhQOI CtQI-DhUlh JUDHDhtllDI-h J Ul D

cn-nrjcnLn_i_JCC"-3_iixi_iLni-3ir.-. uis_jcn.jixio_jajLt.r. ucqj

O Q O Q Q O O © LT O O O Q O O O Ifi O O OOOO O O o o in o o o cn ntmoNmott-o-* cn n ■*■ in -o -o n co o- o ^(Nn^nooNoo^

CN CN CN IN IN CN CN CN CN ro M M M K) M M KJ K' K> Ki <t*"t<t^-<3-«t<t>t<t<t

Openl, Findmn, Getsa, and Valdec: I/O Management

Q

Q

UJ

tn o _j u

D

LU

K LU h>

LU

r.

a. o

u. 5 U.

Q

u

Q

UJ UJ

z

cn

UJ

u a

iL

w

J.

h-

J

cn

c a.

LU

£

Z h-

LU LU

j m

LL H

I ill D

a. i

CD

3 LU

cn a

0. _i

o u

z a

u. #

+ >

ll it

O Q

z z

Ll Ll

LL

o

LU

En

a <r <r cc a <x cn

hQhCOQhl-

cn _j cn rs _j cn ir

LL

a

E Ll

- LL #

z

UJ

_J

LL

z

3:

a.

H H. H |1

LL Z LL Z

LU A LU

h * P

# # Z

111

_jcn>-i_iLn_icn_Jcn_i_j

LU X X

h- LU

> cn

--' LU

cn <h #

z a.

M u

<x >

t- z

cn m

LU -J UJ Z

i o z z

LL LL

> LU

0. Z

0 fla

O O O O O O O OOOO O O Q p O Q O O O O O O O O O O O Q o ©

wininininHinininij(i*<)^^>o-o«8<J'0'<)NNNh.f«.r>h.NNNDO

Openl, Findmn, Getsa, and Valdec: I/O Management

a J

UJ

CC

UJ

i-UJ

I se z _j

a. a: a: uj

<r x z

a a. u z

P w .. <r

a a ix. z i

CO *— » UJ LU O

a: w 2 3

a en cc > a.

ax. en a. _j z

<i a: uj .j h m

<r m _j uj o z

i- a. =i uj z i- uj ll

cr z z z a m

<r h <x z <x k s

h zj E<ixzn dujh-

to a. I o uj a. u m ui m

uj o ii- uj en x

h- >' J K t- 3 3E CC UJ

UJ> i~< H _i O OO

CO »■ ll -i u- fC CC > H h>

a. i— uj h- u. □

ll o Z 3 fc UJ oS ^ z

a a h- w o Z co ui u

Q Z rt £ h-'HXUJU.

M U. iX (-- Z h K » a > > I m

# "-> mZQ-Z ll CI a: CO CQ UJ U

nUIOLU on: h <h h > > ■>■•

ix ■>* <r ■-> d ec ce <r li_ i— i— z + cewi-ti— h

U) + O £ L0 .» IX X gXMDZQgQ Z CO CO M 3

n z z _j Lr z -o 3 i- 3 •- a a cc 3 a. 3 o o z o

E a o <r co ct o q cj en u z ■» & a. en a co h z»ihi-

QC 5 <t 5 3 fl. i«0 Ki HfelSiL'UttUDh-wxOfcO

> a. a. # co ~ # a. # w HZ i~ zi o # a # ouicc

k z uj^ujo <r<i<ri;cD^x<ia.uj Q<i>>-cc:tx>>-ujxa:Ln(.nyrjiLni:<rxuj<ii-Qt-i ihqzz

Ehf-oat-zizDoii i h- zi-LLZQcn_icnu.»uto_JucQ

LLtoto-idcnMUCQ-irjce ■•■ cj cc — uibuib j

o p o o o o o o o o o o m q o in o q p oo5rt cn nnfpNfl roiiicomiiiii)coLTiiiii>i?'L>i>i>^L>c^i>0'L>a^^rt^THrt^rtrtrtrt

Openl, Findmn, Getsa, and Valdec: I/O Management

LU

z 2 <X

I

u

z

LU LL D

UJ

cc

5

U CD UJ

o cc

HI H > CD Q £

or CO rs

Q

>■ X

a. > x a_

I Q P J

a. J _j a.

3

in o cn

cc u cc

UJ T-l

Z > O

>■

I- H

3 cn a. -h

t- i- <x

HI

.J

Lu

h-3 ll H

o

LU LU

LU

h->

PQ

LU I

\-

LU h-ih IT 3

3 Z

5 i-i cc

» 0.

cn

CN

cn

LU LU

> H

-( >-

I- ■» iB

x *-i cc

# Z <Z 3

0. lu cr at

z z q cn

U CO _J rj

LU Z

M

h 3 O

CC

3 LL

3

o

UJ

0.

I- H

3 cn o

h O

CJ CC fc

0. z

z cc

ra Cl

z

a cr

H

z a:

LU

Q U ffi _J

a

Q

LL LU

CC

a.

D

LU LU 0)

u

LU I

u

a. o

a

<l en

q i-_i cc

CM I-<* X # Z

I- a. in

x Z Z

ZUffl

cc

LU

cc

CL

LT CL

cn

lu o

> cc t z cc

<z a.

<L CC

a cn J n

o o o o o o

O * rH H tH ,h

©oinoooooooooooooooooooooo invO-or^cao-o-trNto^in-o. scoc>0'-i(NM«tin«or>»co

^^^^-H^CNCNNCN tN CN CN CN N N W M M W M M M M M

Openl, Findmn, Getsa, and Valdec: I/O Management

en

_J

uj r ce

Z Z LU UJ

uj 2 j t

in a

S 5

1X1 Z t-

3 I--

O 3 h E UJ o

h 5 UJ 3 N

uj a ir _j o i- #

cq a m en

i u. uj 3 h

h ft 3 35 £ «

En z a. t o

E 3 LU 03 ix D Q F

I- O y Q 111 Z Z ix

3 O -J w H

a a. h clow o — x

Z Z O UJ OU ix UJ Z "* H

hO 03 I- * »H 5) UJUJ

# uj en zoo.- cji cd -h tn

*h j ix <r ui # _j cn a a <r w

H fc 2 D - + Q_l OCX, Uj * LUU34»1-

3COH3 _j o *-> o 9 p a u. M z ■*• en tn i- o a.

OQDODOUJ ZZU.3U.3 >C0 LUUMDlf OlUUlOhh

i-_i#oi-tJizQ.a.»Ln»ui i: uj « en jjijjih »xd o #uuoioa#u#u ui:# ouuuuuunii)Ly*i-o

CM JU LUQ-_JIII <X CD

a.i-<ift:o.ucc<i<i<i<ia:«u)ioa.tnuoiijfl.(NUja.*mQ«Q.L(j

ZXCCtnZ JI-l-Q(-QhHUI-ri jzzjzij uiqzz iZDnn-uuimjaiJina:". uiucc-uanuPQDU-SJUffl

ooooouTooooooooinooomoooooooooooo o>0'H(Nroio*i/]>ONODo>o-*-<cNM<r«j-m>oNa)o>o»HO'H(Nn* K><t*<t<t^*>t«t<}-«i-<j-mLnifiUTinmininininmm~o-oNNNrvN

Openl, Findmn, Getsa, and Valdec: I/O Management

2 LU LU

cc u

LO

o

H!

Ll

I

F

- tn h-

I- I- h m >

►H Q. rr. M W X

xl- Ll x i-i lu *j-

LU x N LU O

h * n * CN

•*»"»' # '■ O O «

+ D O O Z ffl

CZ $ EJEgiZ Z(N LU <X

h- QDh-IZ m 4ft m > Q

Q. «, J Z0.UU-1J ■*o* *H(N H|0 ,H _|

Nht njFh h ro in <r m h o to i- o I s - i- o n i- »- r co o

OXDOitr)tllxxr3*ZD>03n0)Df^DNn3OEO10*if

0 z z

«Q.lU>Kfl.ll|Ua._JUCLOI-0.lU<IQ.UJ<IOLLiJO:Q.lU>CCQ.a«« QZZQXZZZZLnUEODZZQZZQZZQZZQLLZLUO:i-

jucQjzufflMr, H ffluaioL)mjuii)jumJUffljhUfflOM

OOOinO'-tNM^'OOOOOOOOOOOOOOOOOOOOOO in«0Nh«C0C0C0CDC00*O»HNtO^'in>0N03OO»HCNK)^-|n>0NC00>O NNNNNNNNNNIIimODIIIlXlCOOOCOtllCOChO-Cf-O-ChO-O^O^ChO^O

125

Openl, Findmn, Getsa, and Valdec: I/O Management

en Ul

en r.n

£

Q

a

OOOOOQOOOOOO O O O O O O O O O O O O O O O O O O o

■HtNntiD'OMDO-o-iNO^Ln-o n en o> 5 — cn ki t in o I s - on o o <h

0 0 0 0 0 0 0 0 0^^^^^^^^^-.^ CN CN C"J N W N CN W N f'.i t-'J

(N CN CS N CN N CN (N CN CN CN CN CN CN r>i CN CN CN CN CN CM CN CM CN CM CN CN CN CM CM CM

Openl, Findmn, Getsa, and Valdec: I/O Management

en

uj

cj

€ m

■X CD

en q

UJ

9 3

z -a

M + UJ

3 N

O X m

-J H J E

-J CD 'I LU

O Z M £

Ll LU H m

J M I

LU 2

X LU —i !~

LU O Z LU

z z h< <■> en

> >-< LD _J UJ

J i-i CD

en q in -o

IX LL ■' Q LU : th <h

I- > O > > _l 3 &, T4 »H

0. - « - O V £

"1-^ Q -» — x # d <r <r z

+ x en z en en uj = h h z

eneni-auj a a cd a cd en en a

Q Q - I I X LU Q Q ....... Z

i x cd >■■■ en cd en x-jluucjct-m

CD IN CD <X i-i O M LU >• w CN <t m | B) 5 CJ <f « U. N Ll

m o m Q x o x > uj i i'! it' o x > d ffl a ai t* aj » *

X # I _J - # h- Q-#H # CnW#W#W## LU

•w, I- _|

>«<iro<r>a.uJ><J i <iCLa><i<i>>->>->tn>-<<rc:a<i<i<i<renM

hOh^l-ZIZIiliDEIjJZDhZZZZZI-Pt-QHQhQQi-L 1 ll)J(Jll-U)MOfflQHJUODHjU)HMHMH|rUJU)JtOJU)JJir .

o o o © q o o o o o © o o o o o p o o o o o o o o o © o c^ in o o

cn r.i <t m <i I s - ca o- o »h cn n ^fUMuo-oi cn m <* in -c F» co o- o o- 5 <■<

n n K' K' w io NM«t*«*** v***inm m in in yi m in m m ut in -o -o

CN CN C"J CN CN CN N CN CN CN CN CN CN CN CN CN CN CN CN N fN CN CN IN CN CN CN CN CN C"J CN CN

Openl, Findmn, Getsa, and Valdec: I/O Management

ro w to to <t t <r «* <r «* t * t «t in m in in m m in in m w -a o

LU 01 O _J <-i

u *

cc <c en q <-> _j

z

UJ

-J

Q

z

o u

LU S 01 * LU

0)

« Q

J <x

u. o

E J

<r o

ce z

<r <r « ce

h a h- 0)

<x m <i a

QIDU Jffl JDS

Z 0)

lu a

a. a

D H

ce x

UJ UJ

h (-

LL V

<C *

QC 1

01 Q

1 _J

01

<x I-

X

LU

z

M

b

a. cc

IT LU

a u

01 01 M

hilt

cc a ce o

LU H

z

uj a. a. e

O rs

SHWIO<tl(l-OMB(Ma-NM*l(H)MIllMa'HNn"tin MHHi^MrtXHoiNNNMNNNNNN to fO to W (0 to

Openl, Findmn, Getsa, and Valdec: I/O Management

SitSSltSiSSSSiSiSSiSi

Y) <t in -o s s h n w <* m «o

NSr^NNCOCOCOCOCOCOCO

#

a

_j

z

a cc cc en lu a en en i- cl

LL U

i- cn en o

a

z O

u

LU

$ cn cn * ll m

<i <r

Q H

_i cn

tSi SSitSiSiGiintSiSitSitSiSi <l s0-JD s 0"O^0*JD , O , ONNN

Q

w a a,

M

fa

£

a, 2 ►a

o P

EC u Eh

CO

z

o

H

&H

<c u o

CL:

o o J

fa

o

H

a

<

Eh M

< O

a

w

N

o p

o o

o

f5

X fa

2

<c

fa

u

H SZS

i

fa

5 2

Eh fa X

fa W fa SB fa

D CQ

IZ H

fa CO O U Q

Oh(H J 2 O

o s

fa 2 O W fa O

fa fa

fa PS

o <

ca

w u

aa Eh

< w

Eh H

o fa W S; o

N M Eh

J fa Pi

w .» <

M >H fa

< -2

fa w o u u

X H

z -

-O J

X2 H

S3 fa CQ

H 2 <

2 J

CO US

fa fa

Eh X

& & fa

M

Q

IZ fa <N fa

a

w EC

fa fa 2 D O

u o 5

< & a s

ts is <s ts ts f) t)" in k£> r-

II fa w

fa H H 3

PS •- fa fa £ fa E-O O 2

a £

fa SB CQ H

IS ts co c^

> fa

o

Eh fa-

u <

EC CQ

u

Eh O

< o

O

Q

IZ EC

H CJ

fa Eh

O 2

Eh

>H H fa fa Eh

Eh fa -

D SB SB Q

fa fa

Eh

Eh W

a

a .- iz

J CN

CQ fa

< fa J < a fa

fa 2 fa O

M fa U

2

., ^ ..

fa >H

O EC SB O U H J Eh

Eh

o

2

>h >h X 2 2 fa H H U

ts is ts ts is <s ishn m^f m

Openl, Findmn, Getsa, and Valdec: I/O Management

fa a

fa J

< Si

M fa

>

a M v

*g

O K H D 2 E-O fa T. fa fa^-

H fa

< cu

o o

o

H

ss

o

o o

W fa U H

M iH

2 + -

O J H

s w w

HfflK 2 < O S J 2

< fa O >h fa S fa 2

JUIOH

■■ fa E 2 U O

ia fa

>

Eh -- fa

2 2 Q

w E-i

Eh H J

□

S Q

fa

II <"1

w £

a, £

o u

s

fa

D O

a

2 P

O fa

fa >

fa

w

a

<c

w fa w

Eh Eh

fa

fa

O

2 H fa

a « w o m £

(-NJ

+

w

fa CO

id

u

M

2

o

u

Eh

X fa 2

Eh

w

§ s

H

ea o

Eh

w

M

33

Eh

fa

o fa

fa

fa

fa

CQ

w p

fa <

0

x

Eh

fa

33

+

J fa CQ

04 a; s fa

U CO

o X

Sg

w a 2 w

CO m

e

M fa

U

3£

2

> Q

w

2~

fa U

a oi

D Q

a

LZ fa

u fa

h fa

2 S

O M

2

w 2

2 < 2

fa

W CQ

< >o

Eh fa 2

Kfl 2

Q M

fa fa

a fa

o o

Eh

tn a

2

> fa

O

W fa

fa fa

M fa U O Eh

O S O fa * 2

a fa H fa 2 2 mom

w

Q O O fa O

H fa O Eh

w

r-t <

O Eh fa 05

>

fa

fa

2

M H D

§

> fa

O

Eh

fa

2 p

o

Q 2 D

8

fa tj

fa

5 < > W

fa Eh

fa

fa O

2

^ fa fa

a h

2 fa

w •

ssssssssstssssissisisisissssissia

iOl^C0WSHNn H /inior-00(MsiH(MfO^'i/noMD8MS

PL,

<

Openl, Findmn, Getsa, and Valdec: I/O Management

Openl, Findmn, Getsa, and Valdec: I/O Management

SSSHSSISISISISISISISSISISSISISISISISSISISISISISSISISS XtSrHr>irortin^r--Xc^iSirHCNrort , iri>jOt~-m<J\SiHCNro^'irivoor~x<ri rvirorororororororofOrn^^^^Tt^^^^^u-iiniriiriiniriinininir)

Openl, Findmn, Getsa, and Valdec: I/O Management

Openl, Findmn, Getsa, and Valdec: I/O Management

-

Openl, Findmn, Getsa, and Valdec: I/O Management

tstsscaiststacstststamtacatacststacststststscatsisiatscststa ro^vnvcr^C0ONCarHr^rorO'*Ln , i>r~-C0CT\CarHf N Jr r i'H.ir)kDr--00<^tarHrvj

Opertl, Findmn, Getsa, and Valdec: I/O Management

OS

OS

w

o

H

H J 0, M

s

+

Q Q

Q Q

w o

w

fa o

CO

Eh

J D

cn S

a

§

i i

u 3

U Q D

<

J J +

D D Q

W W Q ...

2 2 2k

+ +

EH Eh

P D

w

OS

a

a

< OS OS - >

£g

y < rf q Eh q < w j

H

J CO H Eh fa OS •

u

UJ Q

I I

cn

2

O

h-t t-<I

u

Q O

a a: cn

o IX

o

CO LU 00 UJ

N |f)N

> * >

:- a. u o. cn

a E U £ u

... U IB O III

$

-i cn rc is

•0 <3 <t N

—

-

Indisk:

The Main Input Routine

It's up to the Indisk subprogram to pull in a logical line of source code and set it up so that Eval can evaluate it. What does the word logical mean when used this way? You'll sometimes hear of a "logical" string or a "logical" line versus a "physical" string or line. The logical thing is what the computer will see and compute. The physical thing might well be longer or shorter.

For example, on the Apple, Atari, and Commodore 64, the screen permits a physical line of only 40 characters. And though each screen line can hold only 40 characters, Commodore BASIC can interpret 80-character lines, Apple can interpret 256-character lines, and the Atari can interpret 120-character lines. The logical line length is 80, 256, or 120 characters, but the physical line is 40. To describe Indisk's routines, we'll need to make a similar distinction.

Two physical lines of LADS source code might be:

100 LDA 15: INY:RTS 110 DEC 15

but there are four logical lines in these two physical lines:

LDA 15

INY RTS DEC 15

Put another way, the LADS logical line is sometimes smaller than its physical line. The logical item is the piece that a computer—or in this case, LADS—will work with. Whenever you see a colon, you're at the end of a logical line.

In addition to setting up each logical line for examination by Eval, Indisk also performs some other tasks. It sets flags up in response to several pseudo-ops; it transforms single-byte tokenized BASIC keywords into ASCII words (? becomes PRINT); it transforms ASCII hex numbers like $1500 into two-byte integers (the same thing the Valdec subprogram does for ASCII decimal numbers); and it handles the important .BYTE pseudo-op. Indisk is a busy place. It's the second longest source file in LADS. Eval interprets logical lines of source code; Indisk prepares them for that interpretation.

Indisk: The Main Input Routine

Total Buffer Cleaning

Indisk starts by cleaning out an entire group of buffers: LABEL, BUFFER, BUFM, HEXBUF, FILEN, NUBUFF. That's easy because they are all stuck together (see lines 290-340 in the Tables subprogram). The CLEANLAB subroutine in Eval just sticks 0 into the entire string of buffers.

Then 0 is put into the HEXFLAG (is it a $ type number?), BYTFLAG (is it a < or > pseudo-op?), and PLUSFLAG (is it a + pseudo-op?). These three flags will later be set up, if necessary, by Indisk. We want them down, however, at the start of our analysis of each logical line.

At line 110 LADS sees if the previous logical line ended in a colon. LADS tries to be forgiving. It knows that the programmer might accidentally write source code like:

100 LDA 15: LDX 12

leaving some spaces between a colon and the start of the next logical line. Rather than crash trying to find a label called blank-blank-L-D-X, it ignores leading blanks following colons. Elsewhere, LADS ignores blanks preceding semicolons. This gives the user complete freedom to ignore that potential punctuation problem. Logical lines with extra blank spaces will be correctly analyzed.

If a colon ended the previous logical line, we need to skip over the fetch-and-store-line-number routine (130-160) since there is a line number only at the start of a physical line. In BASIC programs, and consequently in LADS source code, the two bytes just preceding the start of the code proper in each physical line are the line number. They need to be remembered by LADS for printouts and also for error reporting.

The Suction Routine

Lines 170-190 are the suction routine for blanks which might precede a colon. We just loop here until something other than the blank character (#32) is encountered. Notice that this loop is also performed at the start of a physical line, but will have no effect since the computer removes any leading spaces when you first type in a BASIC or LADS line.

Line 210 is the start of the main loop which pulls in each character from the disk, one at a time. We skip over this (200) if we've entered at Indisk and therefore are starting a line rather than just looking at the next character within a line.

Indisk: The Main Input Routine

But let's assume for now that we're trying to get the next character in a line. If it's zero, that means the end of a physical line (230), so we go to the routine which checks to see if we're at the end of the entire program, not just the end of a single line.

If there was no zero, we check for a colon and jump to the routine which handles that (260). Then we check for a semicolon. The next section (290-750) handles semicolons. There are two types of semicolon situations, requiring two different responses.

One type of semicolon defines an entire line as a comment. The semicolon, announcing that a remark follows, appears in this case as the first character in a physical line:

100; THIS ENTIRE LINE IS A REMARK.

This type is relatively simple since there is no source code for Eval to evaluate.

The other type of remark, though, appears at the end of a logical line, and there is something for Eval to assemble on such lines:

100 LDA 75; ONLY PART OF THIS LINE IS A REMARK.

When we first detect a semicolon (270), we store the Y Register in variable A (290). The Y Register is very important in Indisk. It is set to zero at the start of each physical line (60) and will still be zero in line 290 if the semicolon is the first character in a physical line. This is how we can tell which type of comment we're dealing with (at the start of a line or within a line).

If, however, the programmer has not requested a screen printout, there is no point to storing a comment. Comments have no meaning to the assembler; they're just a convenience to the programmer. Line 300 checks to see if PRINTFLAG is set and, if not, skips over the store-the-comment routine.

BABFLAG for Comments

But if the PRINTFLAG was up (contained a 1), we transfer that 1 to force the BABFLAG up as well. BABFLAG tells LADS that there's a comment to be printed after the source and object codes have been printed to screen or printer.

Then that previously stored Y Register is pulled back out, and we see which kind of comment we're dealing with. If Y isn't zero, we've got a within-the-line comment, and we can

Indisk: The Main Input Routine

JSR to the PULLREST subroutine which stores comments in the comment buffer (350). Then we return to Eval to assemble the first part of the line, the source code part (360).

When a semicolon appears at the start of a line, though, we'll just fill LABEL, the main buffer, with the comment and then print out that kind of line right here within Indisk. (Printouts are normally controlled by Eval following the assembly of source code. This type of line, however, contains no source code.)

A little loop (370-440) stuffs the comment line into LABEL. It exits when it finds the end of a physical line (380), and it JSRs when it comes upon a tokenized keyword like PRINT or STOPIT. (STOPIT would appear as three characters in the source code: the token for BASIC'S STOP command, and the letters I and T.) Tokenized words have to be stretched out to their ASCII form, or the comment could contain strange nonprinting characters or graphics characters, etc., when printed out. Any character larger than 127 is not a normal alphabetic character. It's going to be a token.

When we finally come upon the end of this physical comment line, we land at PUX1 (450) and proceed to print the line number, the comment, and a carriage return just as we do for any other line. Then we put 0 into the A variable to let MPULL (the return-to-Eval subroutine) know that there is no source code to assemble in this line. It will send us back to two different places in Eval, depending on whether we should or shouldn't try to assemble the line currently held in the LABEL buffer.

Storage to BABUF

The PULLREST routine (520-600) is similar to the PUX routine

above it, but it stores a comment into the BABUF buffer.

PULLREST cannot use the LABEL buffer because this is one of

those lines where the comment comes after some legitimate

source code. And Eval assembles all legitimate source code from

the LABEL buffer. After Indisk turns the following line over to

Eval:

100 LDX 22; HERE IS A COMMENT.

the two buffers hold their respective pieces of this line:

LABEL LDX 22

BABUF HERE IS A COMMENT.

Indisk: The Main Input Routine

BABFLAG is set up to alert Eval to print a comment after it has assembled and printed out the LDX 22 part of this line (520). Then the semicolon in the Accumulator is saved in the A Register. This is our end-of-line condition. Logical lines can also end with colons and zeros. Different end-of-line conditions require different kinds of exits from Indisk. For example, if we hit a colon, we shouldn't pull in the next two characters and store them as a line number. A colon means we've not yet reached the end of the physical line. Since PULLREST is used as a subroutine in various ways—JSRed to from various places in Indisk—it must save the end-of-line condition.

KEYWAD

Then PULLREST pulls the rest of the line into BABUF (560-650) with a little detour to KEYWAD if the seventh bit is set on one of the characters being pulled in. That signals a tokenized keyword like ? for PRINT. KEYWAD is the same routine as KEYWORD (called above when Indisk is pulling in source code characters). The only difference between them is that KEYWORD extends ? to the word PRINT in LABEL, the source code buffer. KEYWAD extends tokens into BABUF, the comment buffer.

PULLRX (660-680) is quite similar to PULLREST. However, PULLRX is a pure suction routine. It pulls in the rest of a comment line, but doesn't store any of the characters. It is called upon when the PRINTFLAG is down and nothing needs to be printed to screen or printer. All PULLRX does is get us past the comment to the next physical line.

MPULL (690-750) is the exit from Indisk back to Eval after a commented line has been handled. Recall that there are two kinds of comments—those which take up an entire physical line and those which take up only the latter part of a line, those which come after some real source code. MPULL distinguishes between them after first checking to see if we're at the end of the entire program (ENDPRO). It loads in the A variable. If A is holding a zero, that would mean that the semicolon was the first character in the physical line, and consequently, the entire line was a comment and can be ignored. There's nothing to assemble. So we PLA PLA to get rid of the RTS address and JMP directly to STARTLINE in Eval to get a new physical line.

Indisk: The Main Input Routine

Y Is the Pointer

Alternatively, if the semicolon was not at the start of the line, the value in the A variable will be higher than zero. (The Y Register was stored in A when a semicolon was first detected [290].) Y keeps track of which position we are currently looking at within each physical line. In cases where there is some source code on a line for Eval to assemble, we just RTS (750) back to Eval where the evaluation routine begins.

The end of the main Indisk loop is between lines 760 and 950. This section is an extension of the character-testing sequence found between lines 220 and 270. What's happening is that a single character is being drawn in from the source code (on a disk file or within RAM memory, depending on which version of LADS you are using). Each character is tested for a variety of conditions: pseudo-ops, keyword tokenization, hex numbers, end-of-line (220), colon (240), and semicolon (270). If it was a semicolon, we dealt with it before making any further tests. The semicolon (comments) handler is the large section of code we just discussed (between lines 290 and 750). If the character isn't a semicolon, however, there are several other special cases which we should test for before storing the character into LABEL, the source code buffer.

Special Cases

Is it a > pseudo-op? If so, we go to the routine which handles that (770) called HI. Is it the < pseudo-op? Then go to the LO routine. Is it the plus sign, signaling the + pseudo-op? If not, jump over line 820. The + pseudo-op is handled elsewhere in LADS; all we do for now is set up the PLUSFLAG (820). Is it the * = , the Program Counter changing pseudo-op? If so, go to the subroutine which fixes that (850). Is it one of the pseudo-ops which start with a period, like .BYTE or .FILE? If so, go to the springboard to the subroutines which deal with these various pseudo-ops (870). Is the character a $, meaning that the source code number which follows the $ should be translated as a hex number? If so, go to the hex number routine springboard (890). The final test is for tokenized keywords (? for PRINT). Tokens all have a value higher than 127, so their seventh bit will be set. If the character is lower (BCC) than 127, we can finally add the character to the source code line we're building in the LABEL buffer (930). Then we raise the Y Register to point to the next available space in the LABEL buffer, and return to fetch the

Indisk: The Main Input Routine

next available space in the LABEL buffer, and return to fetch the next character of source code from disk or RAM memory (950). This ends the main loop of the Indisk routine. As you see, there are many tests before a character can be placed into the LABEL buffer. We only want to give Eval source code that it can assemble. We can't give it characters like . or + or $ which it cannot evaluate properly. Those, and other special conditions, are worked out and fixed up by Indisk before LADS turns control back to the Eval subprogram.

The Colon Logical End-Of-Line

One special condition is the colon. It is handled at the very start of Indisk as a new physical line is analyzed (110). Not much needs to be done with colons except to ignore them. But we do need to prevent LADS from trying to locate the next physical line number. Colons signify the end of a logical line, not the end of a physical line. COLFLAG tells Indisk not to look for a line number. COLFLAG is set whenever a colon is detected (260). We jump down to COLON (970) and set the flag. We don't need to LDA #1:STA COLFLAG because we wouldn't be here unless the Accumulator was holding a colon character (it's higher than 0). We can just stuff that character into COLFLAG. As long as a flag isn't holding a 0, it's set. When setting flags, it doesn't matter that the number in the flag is higher than 1. Just so it's not 0.

There are two springboards at 990-1020. Recall that branch instructions like BNE cannot go further than 128 bytes in either direction, so you'll get a BRANCH TOO FAR error message from LADS from time to time when you exceed this limit. In such cases, just BNE SPRINGBOARD; just branch to a line you insert, like 990, which just has a JMP to your true target.

Like the . pseudo-op interpreter subroutine, the hex translator is also too far from the branch which tries to reach it. With a hex number, though, we first put the $ into the LABEL buffer so it will be printed when the source code line is sent to the screen or printer. Then we bounce off to the hex translator subroutine (1020).

KEYWORD (1040-1210) translates one of BASIC'S tokens into a proper English word. A BASIC word like PRINT is a word to us programmers, but an action, a command, to the computer. To save space, many versions of BASIC translate the words into a kind of code called "tokens." The token for PRINT might be

Indisk: The Main Input Routine

the number 153, which can fit into a single byte. The word PRINT takes up five bytes.

But BASIC itself must detokenize when it lists a program. It must turn that 153 back into the characters P-R-I-N-T. To do that, it keeps a table of the keywords in ROM. We'll take advantage of that table to do our own detokenization.

The specifics of the example we'll examine here are for Commodore computers. The principle, however, applies to Apple and Atari as well. Only the particular numbers differ. We arrive here at KEYWORD because we picked up a character with a value higher than 127. The first thing we do is subtract 127. That will give us the position of this keyword in the table of keywords. To see how this works, look at how these words are stored in ROM memory:

enDfoRnexTdatA

Notice that BASIC stores words in this table with their last letter shifted, similar to the way LADS stores labels with their first letter shifted. That's how the start of each word can be detected. The code for these words is set up so that END = 128, FOR = 129, NEXT = 130, and so on.

Imagine that we picked up a 129 and came here to the KEYWORD subroutine to get the ASCII form of the word, the readable form. We would subtract: 129 — 127 = 2. Then we would look for the second word in the table. We store the results of our subtraction in the variable KEYNUM (1060) and keep DECing KEYNUM until it's zero and we've thus located the word. We look at the first character in the table of keywords. It will be an e. If it's not a shifted character, we've not yet come to the end of a word, and we keep looking (1120). Otherwise, we go back and DEC KEYNUM. All of this is just a way of counting through the keyword table until we get to the word we're after.

Indisk: The Main Input Routine

When we find it (1140), we store the ASCII characters from the table into LABEL, our main input buffer. Again, a shifted character in the table shows us that we've reached the end of the word (1160), and we can return to the caller (the routine we JSRed here from) after clearing out the seventh bit.

KEYWORD turns this line (in the source code):

100 START? LDA [IT (two embedded keyword tokens, ? and [)

into:

100 STARTPRINT LDA RUNIT (which we can read from screen or

printer)

The HI subroutine (1230) handles the > pseudo-op which gets the high byte of a two-byte label as shown in Listing 6-1.

Indisk: The Main Input Routine

*o

H

X CO

W IS

K W

U W

CO OS

D Eh

Indisk: The Main Input Routine

This sort of thing is fairly common during the initialization phase of an ML program. It prepares for the useful Indirect Y addressing mode (sometimes called Indirect Indexed addressing: LDA (LABEL),Y). The > and < pseudo-ops make it easy to set up the zero page pointers upon which Indirect Y addressing depends.

The adjustments necessary to make these pseudo-ops work are performed in the Equate subprogram. All we do here is set up the BYTFLAG to show which of them -was encountered. BYTFLAG is 0 normally, set to 1 for a < low byte request and 2 for a > high byte request. Then we go back to fetch the next character in the source code. The > and < symbols are not stored in the LABEL buffer.

Don't Drive with Your Legs Crossed

The STAR subroutine (1300) deals with the pseudo-op which changes the Program Counter. This pseudo-op has one primary use: It creates a stable place for tables. Some people like to use it to make room for tables within source code (and consequently within the resulting object code too). That seems both unnecessary and dangerous, like driving with your legs crossed. Most of the time it won't do any damage, but when it does cause problems, it causes a crash.

If you like to live dangerously, go ahead and stick a table or a buffer right in the middle of your code. The *= pseudo-op allows coding as shown in Listing 6-2. When assembled, that risky trick will look like the listing shown in Listing 6-3. This example leaves—between $325 and $357—a 50-byte-long zone to be used for data rather than instructions. You must jump over the table. But what's the point? Why not do the sensible thing and put all your tables, register, buffer, etc.—all your nonprogram stuff—in one place? At the end of the entire program. Not only does that ease your programming task by making it simple to understand what you're trying to do, it also allows the *= pseudo-op to make its true contribution to assembling: a stable table.

When you're assembling a long program, you will often go through a two-step process. You'll assemble, then test. The test fails. You change the source code and try it again. This assemble-test rhythm takes place so often that you'll want to make it as easy on yourself as possible. One of your best debugging techniques will involve running your code and then looking in the

Indisk: The Main Input Routine

buffers, registers, variables, and other temporary storage places to see just exactly what is there. That's usually the best clue to what went wrong. If you are trying to load in the word TEXTFILE from disk and your buffer holds EXTFILEO, that tells you exactly what you need to do to fix up the source code.

In other words, you want to be able to check buffers, variables, etc., often. Where are they located in the object code? Obviously, each time you make a slight change to the source code, everything in the object code above the change in memory shifts. All the addresses beyond the changed source code will go up or down depending on whether you added or subtracted something.

Stabilizing Buffers

This makes for very unstable addresses. You would never know where to PEEK at a particular buffer or variable.

There are two ways to solve this. You could put the data buffers, etc., at the start of your program. That way, they wouldn't shift when you changed the source code beyond them. But that's somewhat clumsy. That means that your program doesn't start with the first byte. The entry to your program is up higher, and you can't just SYS or CALL or USR to the first byte.

An alternative, and likely the best, idea is to put tables at the very end. That way the SYS to the object code start address is also the first byte of the ML program. But how does this solve the shifting tables problem? That's where the *= comes in.

When I first started to write LADS, I decided to start it at $3A00. That left plenty of room below for BASIC-type source files and plenty of room above for "Micromon," an extended debugging monitor program which sits in memory between $5B00 and $7000. (I do all my programming on the venerable, but serviceable, Commodore PET 8032.) LADS was expected to end up using about 4K of memory, so I forced Tables, the final source file, to detach itself from the rest of the program and to assemble at $5000. The Tables subprogram started off like this:

10; TABLES 20 *= $5000 30 MNEMONICS etc.

This kept everything in the Tables unaffected by any changes in the program code below it. The entire source code could be massaged and manipulated without moving the data ta-

bles one byte up or down in memory. A detached table is a stable table.

So, during the weeks while LADS was taking shape, I learned the addresses of important buffers like LABEL and important variables and flags. That makes debugging much faster. Sometimes, I could tell what was wrong by simply PEEKing a single flag after a trial run of the source code.

A program the size of LADS, a complex game, or any other large ML program, will require perhaps hundreds of assemblies. It becomes very useful to have learned the special addresses, like buffers, where the results of a trial run of your object code are revealed. And for this reason, these buffer and flag addresses should stay the same from the day you start programming until the day the entire program is composed.

How is the *= pseudo-op handled? Before anything else, we pull in the rest of the source code line by a JSR to STINDISK, the main loop in Indisk. After that, STAR checks to see if anything should be printed out by looking at PASS. On pass 1, we'll skip over the printout (1320). Otherwise, we print the star and the input line held in the LABEL buffer. We won't check to see if a printout is requested by looking at PRINTFLAG or SFLAG (screen printout). *= is such a radical event that it will be displayed on pass 2 whether or not any printouts were requested.

Then we come to the familiar hex or decimal number question. Hex numbers are translated and put into the RESULT variable as they stream in. Indisk does hex. Decimal ASCII isn't automatically put into RESULT. If the argument following * = was hex, we skip over the next few lines (1380). If not, we look for the blank character (in *= 500, the character between the = and the 5). Finding that (1420), we point the TEMP variable to the ASCII decimal number and JSR VALDEC to give the correct value to RESULT. We'll use RESULT to adjust the PC as requested.

Padding the Disk File

If the programmer wants object code stored to disk, we cannot just change the internal LADS program counter. The disk drive won't notice that. We've got to pad the disk program: We've got to physically send spacer bytes to the disk to move its pointer the correct number of bytes forward. Object code is stored only on pass 2.

151

Indisk: The Main Input Routine

Thus, two questions are asked here. Does the programmer want object code stored? And is the disk drive a recipient of that object code? If the answer to both questions is "yes," we JSR FILLDISK (1590), a padding routine we'll come to later. If not, the whole issue of disk padding doesn't matter and we can proceed to adjust the PC (SA is the variable name of the LADS Program Counter) by transferring RESULT into it (1600-1630). Then we PLA PLA the RTS off the stack and jump back into Eval to get the next physical line.

ENDPRO is a short but essential routine. After each physical line we need to see if we've reached the end of the source code program. Microsoft BASIC signals the end of a BASIC program with three zeros.

But before checking for those telltale zeros, ENDPRO fills the buffers with zeros to clean them (1680-1710).

Then it pulls in the next two characters. If the second one is a zero, we know it's the end of a source file (not necessarily the end of a series of chained source files; that's flagged by the .END pseudo-op). However, if it is the end of a program file, we flip the ENDFLAG up to warn Eval and RTS back to Eval (1790). Even though Indisk has discovered that we're at the same last line in a file, Eval still has that last line to evaluate and assemble. The ENDFLAG won't have any immediate effect when we first return to Eval.

The other possibility is that we won't find the three zeros and that this isn't the last line of a file. If it isn't, we just set the COLFLAG down because at least we're at the end of a physical line. A zero always means that. Then we return to Eval. Indisk just pulls in one line at a time.

Hex Conversions

HEX is an interesting routine. It is called when Indisk detects the $ character. HEX looks at the ASCII form of a number like $0F and turns it into the equivalent two-byte integer 00 OF in RESULT. It's similar to the subprogram Valdec which translates an ASCII decimal number into an integer.

HEX operates like a little Indisk. It pulls in characters from the source code, storing them in its own special buffer, HEXBUF, until it finds either a zero, a colon, a blank, a semicolon, a comma, or a close parenthesis character. Each of these symbols means that we've reached the end of the hex number. Some of them signal the end of a line, some of them don't. Whichever

Indisk: The Main Input Routine

category they fall into, they go to the appropriate routine, DECI or DECIT.

Busy X and Y

If we're not yet at the end of the hex number, however, the character is stored in HEXBUF (1970) for later translation and also stored in LABEL for printout. Notice that both the X and the

Y Registers are kept busy here, indexing their respective buffers.

Y cannot do double duty because it is farther into the LABEL buffer than X; the LABEL buffer is holding the entire logical line, HEXBUF is holding only the ASCII number. The two buffers will look like this when the source line HERE LDA $45 is completely stored:

LABEL HERE LDA $45 HEXBUF 45

LABEL will be analyzed and assembled by Eval. It needs to store the entire logical line. HEXBUF will be analyzed only to extract the integer value of the hex number. Storing anything else in HEXBUF would be confusing.

A hex number which is not at the end of a line goes to DECIT (2020) and, the length of the hex number is stored into the variable HEXLEN (2020) so we'll know how many ASCII characters there are to translate into an integer. Then the final character (a comma or whatever) is put into the LABEL buffer. Then the JSR to STARTHEX (2050) translates the ASCII into an integer in RESULT. A JMP (rather than a JSR) to STINDISK pulls in the rest of the logical line and takes us away from this area of the code. The assembler will not return to this area. It will treat the rest of the line as if it were an ordinary line.

By contrast, a hex number which is at the end of a line goes to DECI (2070), and we store the type of end-of-line condition (colon, semicolon, 0) in the variable A. We put the length of the hex number into the variable HEXLEN (2090), so we'll know how many ASCII characters there are to translate into an integer. And we put a 0 delimiter at the end of the information in the LABEL buffer. Then the JSR to STARTHEX (2110) translates the ASCII into an integer in RESULT. We restore the colon or semicolon or whatever (2120) and jump to the routine which provides a graceful exit (2130).

ASL/ROL Massage

STARTHEX turns a hex number from its ASCII form into a two-

Indisk: The Main Input Routine

byte integer. It does this by rolling the bits to the left, pulling the number into RESULT'S two bytes, and adjusting for alphabetic hex digits (A-F) as necessary.

The variable HEXLEN knows how many characters are in the hex number. It will tell us how many times to go through this loop. Before entering the loop, we clean the RESULT variable by storing zeros into it (2140-2160) and set the X Register to zero.

The loop proper is between lines 2180 and 2350, and is largely an ASL/ROL massage. Each bit in a two-byte number is marched to the left. ASL does the low byte, ROL the high byte. ASL moves the seventh bit of RESULT into the carry. ROL puts the carry into the zeroth bit of RESULT+1, the high byte.

As an example of how this ASCII-to-integer machinery works, let's assume that the number $2F is sitting in the HEXBUF. As ASCII, it would be 2F. But recall that the ASCII code simplifies our job somewhat since the number 2 is coded as $32. To turn an ASCII hex digit into a correct integer, we can get rid of the unneeded 3 by using AND #$0F.

Alphabetic Numbers

What complicates matters, however, is those alphabetic digits in hex numbers: A through F. For them, we'll need to subtract 7 to adjust them to the proper integer value. They, too, will have the high four bits stripped off by AND #$0F.

Let's now follow $2F as it rolls into RESULT. $2F, as two ASCII digits in HEXBUF, is: $32 $46 or, in binary form, 00110010 01000110.

HXLOOP starts off by moving all the zeros in RESULT four places to the left. There are four ASL/ROL pairs. The first time through this loop, just zeros move and there's no effect. Then we load in the leftmost byte from the HEXBUF (2260) and see if it's an alphabetic digit. This time we're loading in the $32 (the ASCII 2), so it isn't alphabetic and we branch (to 2300) for the AND which strips off the four high bits:

00110010 ($32, as ASCII code digit) AND 00001111 ($0F)

00000010 (now a true integer 2)

The ORA command sets a bit in the result if either of the tested bits is set. That's one way of stuffing a new value into RESULT:

Indisk: The Main Input Routine

00000000 (RESULT is all zeros at this point) ORA 00000010 (we're stuffing the integer 2 into it) 00000010 (leaving an integer 2 in RESULT)

Next the X index is raised and compared to the length of the ASCII hex number (in our example $2F, HEXLEN will hold a 2). X goes from 0 to 1 at this point and doesn't yet equal HEXLEN, so we branch back up (2350) to the start of the loop and roll the 2 into RESULT, making room for the next ASCII digit:

Carry bit high byte low byte

0 00000000 00000010 (our 2 before first ASL/ROL)

0 00000000 00000100 (after)

What's happened here is that we've shoved the 2 from the low four bits into the high four bits of RESULT. This makes 2 (decimal) into 32 (decimal), or $20. Why do that? Why make room for the next digit in this way? Because the 2 in $2F is really a hex $20. It's a digit 2, but not number 2. It's not a number 2 any more than the 5 in 50 is a 5. This ASL/ROL adjusts each digit to reflect its position, and position determines the numeric value of any digit.

Alphabetic Adjustment

Now it's time to pick up the F from HEXBUF (2260), and since it has a decimal value of 70, it is higher than 65, so we adjust it by subtracting 7. That leaves us with 63 ($3F). We strip off the 3 with AND $0F:

00111111 ($3F, the adjusted ASCII code digit) AND 00001111 ($0F)

00001111 (now a true integer F)

and then incorporate this F with the $20 we've already got in RESULT from the earlier trip through the loop:

00100000 (RESULT is holding a $20) ORA 00001111 (we stuff the F into it)

00101111 (leaving the integer 2F in RESULT)

Again, X is raised and tested to see if we're finished with our ASCII hex number (2340). This time, we are finished. There's nothing more to roll into RESULT so we set up the HEXFLAG.

lndisk: The Main Input Routine

This alerts all interested parties in LADS that they do not need to evaluate this argument. The value is already determined and has been placed into RESULT, ready to be printed out or POKEd as the need arises. Then we return to whatever routine called on STARTHEX for its services.

Pseudo-op Preliminaries

The important pseudo-op .BYTE is also handled within the lndisk subprogram. Any pseudo-op beginning with . comes here to PSEUDOJ (2410) first. All of these . type pseudo-ops require certain preliminary actions, and the first section of PSEUDOJ accomplishes those things. Then they split up and go to their own specific subroutines. Most of them end up going to the subprogram Pseudo.

PSEUDOJ first tests to see if there is a PC address-type label such as the word OPCODES in:

100 OPCODES .BYTE 161 160 32 96.

The Y Register will still hold a zero if the . character is detected at the very start of a logical line of source code. That would mean that there is no PC-type label and we don't need to bother storing it into the label array for later reference. Likewise, if this isn't pass 1, we can also skip storing such a label in the label array.

But if it is pass 1 and there is one of those labels at the start of the line, we need to save the A and Y Registers (2450-2470) and JSR EQUATE to store the PC label (and its address) into LADS' label array. Then we restore the values of A and Y (2490-2510) and store the . character in the main input buffer LABEL.

If It's Not B

The character following the . will tell us which pseudo-op we're dealing with, so CHARIN pulls it in and stores it into the buffer (2550). If it's not a B, we branch to the springboard PSEUD1 which sends us to the Pseudo subprogram for further tests (3010).

Now we know it's a .BYTE type, but is it the ASCII alphabetic type or the ASCII numeric type? It is .BYTE "ABCDE or .BYTE 25 72 1 6?

There is a flag which distinguishes between alphabetic and numeric .BYTEs: the BNUMFLAG. It is first reset (2600), and we check both the pass and the SFLAG to decide whether we

156

~

Indisk: The Main Input Routine

should print out this line or not. If it's pass 2 and SFLAG is set, we print the line number and the PC address. Then we pull in more of this source code line until we hit a space character. If the character following the space isn't a quote, we know that we're dealing with the numeric type of .BYTE, so we branch down to handle that at BNUMWERK (2810).

Otherwise, we take care of the alphabetic type. This type is easy. We can just pull them in and POKE them. There's nothing to figure out or translate. These bytes are held in the source code as ASCII characters and will be POKEd into the object code as ASCII characters. The main use for this pseudo-op is to store messages which will later be printed to the screen or printer.

End-of-Line Alternatives

The active parts of this loop are the CHARIN (2820) and the JSR INCSA (2990) or JSR POKEIT (3050). The decision whether to simply raise the PC with INCSA or actually POKE the object code is based on the test of PASS (2970). The rest of the loop (2830-2960) is similar to other tests for end-of-line conditions found throughout LADS. We look for a 0 (2830), a colon (2850), a semicolon (2880), and a concluding quote (2940). Any of these characters signal the end of our alphabetic message. And each condition exits in a way appropriate to it. Semicolons, for example, require that the comment be stored in BABUF for possible printout. To do this, we JSR PULLREST (2900).

PSLOOP stores each character into LABEL, the main input buffer. It also JSRs to the POKEIT routine (in the Printops subprogram) which both stores the character in any object code on disk or memory and raises the PC by 1. Then it jumps back up to the start of the loop to fetch another alphabetic character (3080).

Numeric .BYTE

BNUMWERK is more complicated than BY1, the alphabetic .BYTE pseudo-op we just examined. BNUMWERK must not only check for all of those possible end-of-line conditions; it must also translate the numbers following .BYTE from ASCII into one-byte integers before they can be POKEd. It's that same problem we've dealt with before: 253 is stored in the source code as three bytes: $32 $35 $33. We need to turn it into a single value: $FD. (One thing simplifies the numeric type .BYTE pseudo-op. The programmer can use only decimal numbers in the source code for

Indisk: The Main Input Routine

this pseudo-op. .BYTE $55 $FF is forbidden, although you could —

certainly add the option if you wish.)

Like a small version of the Eval subprogram, BNUMWERK has to have a flag which tells it when to close down. We set this —

BFLAG down (3100) and then put the character in the Accumulator into a buffer called NUBUF. In this buffer we'll convert these decimal ASCII numbers into integers. Then we raise X to 1 —

and enter the main BNUMWERK loop (3130).

The BFLAG is tested, and we shut down operations if it is set (3140). Otherwise, we pull in the next character and go through that familiar series of tests for end-of-line conditions: 0, colon, or semicolon. If it is a regular character, we stick it into the special BUFM buffer (3250) and check to see what pass we're on. On pass 1 we don't do any POKEing or printing out, so we can skip that. But on pass 2, we check to see if we've got a space character, indicating that we've reached the end of a particular number, if not yet the end of an entire line (3360). If the number is completely in the buffer, we raise the PC and go back for the next number (3320).

On the second pass, however, we may have to POKE object code and also provide printouts. This means that we have to both calculate each number for POKEing as well as store each number in ASCII form for printouts. We pull the character from the BUFM buffer and store it in the printout buffer, LABEL, the main input buffer (3340). After that we check again for end-of-number or end-of-line conditions (3360-3410) and, not finding one, return for another character (3440) after storing the current character in HEXBUF.

An end-of-line condition lands at BSFLAG (3450), which alerts BNUMWERK that it should exit the loop after the current number in HEXBUR has been analyzed. _

A Huge, and Incorrect, Number

WERK2 (3480) performs the analysis of a single number. It points the TEMP variable to NUBUF where the number is stored and JSRs to VALDEC, leaving the value of the number in RESULT. Then the value is POKEd to the disk or RAM object code (and the PC is raised by 1) (3550).

So that nothing will be left over to confuse VALDEC during its analysis of the next number, NUBUF is now wiped clean with zeros. VALDEC expects to find 0 at the end of an ASCII number that it's turning into an integer. If that 0 isn't there, VALDEC will

keep on looking for it, creating a huge, and incorrect, answer.

Then we return to the main loop and look for another character, the start of another number (3620).

Graceful Exits

There are so many options in LADS that graceful exits from routines like BNUMWERK are rather difficult. We cannot just simply RTS somewhere. We've got to take into account several sometimes conflicting conditions.

LADS can get its source code from two places: disk or RAM memory. The source code can be entirely within a single program file or spread across a chain of linked files. LADS can assemble hex or decimal numbers from source code (except within the .BYTE pseudo-op). The assembler can send its object code to four places: disk, screen, RAM memory, or printer. All or any of these targets can be operative at any given time. And output can be turned on or off at will. No wonder there have to be different exits and some testing before we can leave a pseudo-op. We've got to figure out what's expected, where the object code is going. Finally, the fact that logical lines of source code can end in several ways adds one additional complication to the exit.

BBEND is the start of exit testing for BNUMWERK. On pass 1 we have to raise the PC one final number (3650). If the line ends with a colon, we cannot go to ENDPRO and look for a new line number, since colons end logical, not physical, lines of source code (3680). In either case, we set the COLFLAG up or down, depending on whether or not we've got a colon-type ending to this logical line (3700). We then raise the LOCFLAG to tell Eval to print a PC-type address label and PLA PLA, pulling the RTS off the stack in preparation for a JMP back to Eval. If it's pass 1 or if the printer printout flags are down, we don't need to print anything, and we JMP into Eval at STARTLINE to fetch a new line of source code (3790).

Alternatively, if it's pass 2 or if the PRINTFLAG is up, we go back into Eval at PRMMFIN where comments following semicolons are printed (3780).

FILLDISK (3810) takes care of a problem created by using the *= pseudo-op with disk object code files. Recall that if you wrote source code like:

10 *= 900

100 START INY

159

Indisk: The Main Input Routine

110 * = 950; leave room here 120 INX; continue on

LADS would normally store the INY and follow it immediately on a disk file with INX. The PC variable (SA) within LADS would have changed. The INX object code being POKEd to RAM would be stored correctly at address 950. But the INX would go to disk at address 901. The disk is receiving its object code bytes sequentially and doesn't hear about any PC changes within the computer during assembly.

FTLLDISK subtracts the old PC value from the new adjusted PC value and sends that number of filler bytes to a disk object file. In the example above, 900 would be subtracted from 950, and 50 bytes would be sent as spacers to the disk. This creates a space between INY and INX, a physical space, which will cause the object file to load into the computer with the correct, expected addresses for each opcode.

A secret is revealed here. There are two full passes, but LADS starts to try for a third pass. It is quickly shut down because during this pass the ENDFLAG is up and STARTLINE will detect it. Nevertheless, we cannot store more bytes during this brief condition. Bytes must be stored only on pass 2, not on pass 1 or that temporary attempt at a pass 3 (3840).

Starting the Countdown

If FILLDISK is called upon to act, however, it acts. The disk object file (file #2) is opened (3860), and the old PC is subtracted from the new one (3880-3940). The Accumulator is loaded with a 0 and we start the countdown; the result of our subtraction, in the variable WORK, is decremented for each 0 sent to the disk object file (3960-4000). If WORK hasn't counted down to zero, we continue with this loop (4010 and 4030). Finally, we restore the normal I/O and then return to the caller.

The final subroutine on Indisk is functionally identical to KEYWORD. It turns a token into an ASCII word (turns ? to PRINT), but it sends its results to the BABUF buffer which stores all comments. KEYWORD sends its results to the main buffer LABEL for source code lines. To follow the logic of this subroutine, see the discussion of KEYWORD earlier in this chapter (line 1040 on).

Now we can turn our attention from LADS input to LADS output. The bulk of the next chapter explores the four destinations of assembled code: screen, printer, disk, or memory.

Indisk: The Main Input Routine

Indisk: The Main Input Routine

StStSCSlSCSIStSlcaCSIStSltSltSJQCStStSIStStSISCSSSlStatSISSGl r~C0fflSHNn>tlOkBr~-00miSH(N( r )^iniD^CDOlC3HtNn^'lfllBt^

Indisk: The Main Input Routine

SSSSSISSSISISSBISISISCSIISIISISIISISISSISISISISIISSISSI COO^CSl rHrviro^iri^r^cOiTiS'HCN'^^i^^i^cXJO^'Sr^rsjrO'H/irikDl^CO minvD i>o^ix>ix)^^ou3vDi^i^i^i^i^i^i^i^r^i^cococx)ct)corooococo

Itidisk: The Main Input Routine

§

Indisk: The Main Input Routine

ISBBSSSSSSSBSSinSSSBlaSSSISSSSSSISS STHeN^^iri^i^cnc^Si-HCNojro-H/iriiiJi^cocT.lSirHCNro^t'iri^i^co

HHHi-lrlrliHrlHi-IHrlHHHHHHHHHrlrlHHHrlrlHrl

Indisk: The Main Input Routine

SISSSlSSSSSSSSSSlSSSSSSSSSSSISSSSSS C^SiHMm^incDi^OQc^SMr^oi^in^r^rac^SMrNro'H^irikDi^oocrv

iHiHrHrHiH^Hr— li—li— IrHi-H^HiH^HiHi— IrHiHi-HrHrHf-lrHiH^HrHi— IiHiHiHiH

Indisk: The Main Input Routine

SSGIISSISISISSSQSSISISSSSSC5SSlSIS®SSISSSta

S^tNrn^iri^flr^cowts^c^f^^^vDr^coc^cs^cNm^in^or^cocritsi

CDCOfflOCOfflcDCOfflCOffi^cnoicliC^iyC^WCftSSSISSSlSSStSH

HHHi-IHHHHrlrHH^MHHHHr-lrHHtNMMOIMINWMMNM

Indisk: The Main Input Routine

tstststststststststststststsisisiststststsiststsistststststsis HNn<tiokDi^cooi5!HMro ri /iri\flt--a)i^sHrN( , i'i , iniBi^cooNSH HHHHHHHHHMNMNr>jNN(N'NiMn(»)rf|(<iroc<ir»irrirnfO ri / H /

MOINNNNtNMWMNMMNNNNNNINNMNMMtNfNNfNNN

Indisk: The Main Input Routine

iSCSCSCStSSCSlCSCSlCSStSCSJCSISIS StSCSSISSIS®CSCSCSl(S)iSISlSl

(Nro^invor^cocrvcsiiHrNn^.Lnixii^ cocriCSii-i(Nn'5tinvci^coa^sirH<N

0)tNCNCNCN(NCN(N(N(N<N(NCN<N(NCN CNMOJ(N(NN(N(NCNN(NCNCNCNO)

Indisk: The Main Input Routine

BSHSQISSISISSSSISISSSSISIISSISSIISISSIISSISSISISIS oo^irii^r^cocr>s^(Nro^in^r^coa>csi^fNrn^in^Dr--coc?ie3^Htsiro t^r^t^t^r^r~r^cococricocococococococT\aN^c^<Tvc^c^c^c^c^S)SiS (Nr^<Nr^rv)rNjr\)rs!CNc^cNr>ir^rvi(Nr^r*iMrv!cNCS<NC^r\|oir^

Indisk: The Main Input Routine

tSBSSSSSSBSSSSSSSSCSiSISISISlSISSISSSISSS ^inkflr^coc^S^CNro^invDr^coa^CarH(Nro^t , in^Dr^cO(TiCa^HCNro-5j> SSSSSSHHHHHHHHHHMMMNINNMNNNnnnnn mrorororomrorororororororororornrororo ronnmrororonnnn

Indisk: The Main Input Routine

SSSSSSSSISSISC3SISSSSISISBSISISSIIS1SISB1SISS invor^rocftS^CNro^ir>^r^OTO>S.H(Nro^in^r^CD^K>HCNro^u->

Indisk: The Main Input Routine

SC3BBSSBBSSSSBSQSSSBSISt3C3tSSt3tSt3Cat3tS U3MDWBH(N< r lH/inifli-.rQ(jiiSHNnH/in\O[^C0CTiBHCNd<J' U i^

^vD^^I^I^I^t^[^t^t^t^I^t^C02COOTCOroaDC0O3C0(ri(TiC^<TilTiCI\CIN

Indisk: The Main Input Routine

fa

13

CQ

Si

—•53

W M

53

H En Eh M

O CO Pi Eh

D

O Dj

53-^

M CO

Pi Eh

Eh 53 •

CO W—~

£ fa

hSW H O fa

o cj fa

CO D

o

fa J

o

Eh Eh

a d Pi ffl O

- fa a o

co 2

h o >

O 53 CQ O < H

w

Q Pi Pi W O >

>H < fa

fa CO

M

w

fa CO

CO H

IE

- Eh

u fa -

CO fa

1^

Q </>

<£ =tt=

S U fa CQ tn, CO

D

m s fa

D

53 m U X X

>h in fa H 53

h (N -

fa * fa

< x w a W

Eh O fa H CO

CO fa CO CQ fa

X

X

: x x

. i W 53 Q K H B (O Si H fa fa CO

X

<; jhh

Q fa X fa J CQ CQ fa

Q fa

£ X D

>* h ca

fa fa CQ

fa

H < £ Eh CQ CO

x a

IB 53

fa < fa X

>H fa fa CO

53 S CO Eh

H b fa fa

5!tS!StStStS>K)(StStS!!S!S)(StStStS(S<SS<St3<SiSIStSSltSiStSS>IS

r-CDffiSHNfo^ , inioi , ~(DO>iSr-iNn^'i/nor-co<^siHcsrT*inioi~

<^(3>C^SlSSSSSSSSS'-H'-HiHi— 1 i-H r-H i-H i-H i— I^H<NCN(N<N<NCN<N(N rorom^^^^^^^^-H/'d ^t'^} , rj , ^} , <*^"^t^)'^^-'H/^^"^«*^i"^)'^

Indisk: The Main Input Routine

w

w

U

u

EC

£

W

J H

CO

c

0

I

a

60

C

OJ

C H

"0

c

c

o

Q

Q O

«

EC O

BSl

O

CJ

w u

-^ -o

< I

*5 o

0) &,

8 C

0) .3

0 en CD

W

ro

»

2 U 05 U n CN

{A- tort; =»*= * o

s cu a oss u o u

rsl </> 4fc

S m

U (N

o

S ?H

o a o o

c

0

a

I

o

1

C £

CO _C

2S

o B

^ OJ

2 c°

R J

.5 i-,

5 °

£ -a

O CO

<" fa

° £

C co

.2 fe

e g

% £

■g "o

3 o

^ CN

0J ^ X. I

o . . <U ON x

ra

QJ <«T C

CO

a

i

tf!

<:t iX

U LU

i-i CD

LL E

m id

a z

a lu

E z

M .J

K

£M£

•3 <L cc en

1*3

fs tsj fs ts ta <t vfl co h n r- r- r- CD oo

If) <S Si Si iii CS IS iSi 'ii IS Si ® «-• 0- Si ■-* *h IN -0 m Si to S <-< r-i M >t"f<l<lNhO][OCr>

Indisk: The Main Input Routine

Chapter 7

Math and Printops:

Range Checking and Formatted Output

"1

-

-

Math and Printops:

Range Checking and Formatted Output

Math, a short subprogram, has a rather limited job. It is designed to turn the ASCII number following the + pseudo-op into a two-byte integer and to save it in the variable ADDNUM. Later, when the final RESULT is calculated by the Valdec subprogram, anything in ADDNUM will be added to RESULT. Math responds to a source code line like:

100 SCREEN = $0400

120 LDA SCREEN+ 256; this would assemble as $0500

As with the .BYTE pseudo-op, the + pseudo-op allows only decimal numbers as an argument following the +.

The first loop in the Math subprogram simply looks along the LABEL buffer to locate the +. Thus, it doesn't matter if the + is right next to its label. You could write SCREEN +256 as well as SCREEN+ 256. However, finding the +, the subroutine expects to find no spaces between the + and the number to be added. +256 is correct. + 256 would be incorrect. This allows us to test for a variety of end-of-number conditions. That means that you can use the + pseudo-op within such addressing modes as LDA (SCREEN + 256),Y or LDA 1500 + 25,Y.

Each character following the + is stored in HEXBUF for later translation by Valdec. Each is also tested to see if it is a nonnumber—if it is outside the range from 47 to 58, the ASCII code for the digits 0-9. Anything outside that range ends our storage of the number to be added, and we go down to put the number into ADDNUM.

Range checking is simple enough. Just remember to test against a number which is one lower than the low end and one higher than the high end of the range. For example, to see if a number is lower than $30, you must test against $2F. That's because BCC tests for lower than. $30 wouldn't be lower than $30. The same thing works on the high end. To test for numbers higher than $39, you CMP #$3A.

After the number is set up in HEXBUF, we point TEMP to it, JSR to Valdec, and move the result from RESULT into the

Math and Printops: Range Checking and Formatted Output

variable ADDNUM. It will wait there until, on pass 2, the Array subprogram makes the addition adjustment in line 1160.

Printops: The Output Routine

One important function performed by the Printops subprogram is raising the PC (Program Counter). A subroutine called INCSA (650) increases the PC by one for each object code byte, whether this byte is an opcode or the argument of an opcode. Printops' other main job is to send each byte of object code to one of four places: RAM memory, disk, screen, or printer.

Because each object code byte can go to any one, or all, of these four different destinations, there are a series of tests and parallel routines within Printops. For one thing, Printops has little to do on pass 1—it does raise the PC, but nothing is POKEd anywhere or printed to screen or printer until the second pass.

Also, Printops has three entry points, depending on whether the Eval subprogram has assembled a one-, two-, or three-byte logical line. An INY would only JSR from Eval to FORMAT, right at the start of Printops. FORMAT loads the OP (opcode) and stores it and prints it as required. It's a single-byte event. LDA 15 first JSRs to FORMAT to output the opcode, the numeric equivalent of LDA, then enters at PRINT2. LDA 1500 would JSR FORMAT to send the opcode, then enter at PRINT3. These entry decisions are made by Eval after it has determined whether it's dealing with a one-, two-, or three-byte addressing mode.

FORMAT (20) simply raises the PC by one. It does this with a JSR to INCSA (40) on pass 1. On pass 2, however, it also checks to see if screen printout was requested (60). If so, it restores normal I/O and prints the number (120). As we will see, PRINTNUM also prints to the printer, if that was requested. Then the opcode is POKEd to disk or RAM, if that was requested. The POKEIT subroutine performs POKEs to RAM. POKEIT also leads right into INCSA to raise the PC automatically following each POKE. Finally we RTS back to Eval (160). So much for a single-byte addressing mode.

Two-Byte Addressing Modes

PRINT2 (180) handles LDA 15 and other two-byte addressing modes. Like FORMAT, pass 1 only results in a JSR INCSA (to

raise the PC). Pass 2 follows the same pattern as FORMAT, explained above. The major difference is that the number fetched before the JSR to PRINTNUM comes from the low byte of the RESULT variable (240) rather than OP. This is a single-byte argument addressing mode.

PRINT3 (290) parallels the two previous routines, except that it handles a two-byte argument. On pass 1 it JSRs to INCSA twice to raise the PC by two.

On pass 2, it prints (370) and POKEs (390) the low byte of RESULT if requested and then prints (460) and POKEs (480) the high byte of the argument, RESULT+1. A formatting problem is handled in line 420. HXFLAG shows whether or not output to screen and printer is supposed to be in hex. If this flag is set, we don't need to print a space between the low and high bytes of the argument. The hex printing routine will do that for us. If printout is in decimal, though, we need to print a space (440).

Creating an Object Program

POKEIT (490) stores the byte in the X Register at the current PC address if the POKEFLAG is up. This flag indicates that the programmer used the .O pseudo-op, requesting that object code be stored in RAM memory during assembly. For both PRINTNUM and POKEIT, the X Register is holding the opcode or argument. X is saved in the variable WORK + 1; some of the disk management routines below will change the value of X, so we must preserve it for later use.

Then the DISKFLAG is checked (550). It indicates that the programmer used the ,D pseudo-op, asking that an object code program file be created on disk during assembly. If not, we just go down to raise the PC at INCSA (560).

But if an object program is being created on disk, LADS opens communication to file #2 (the write-to-disk file) and recovers the byte from WORK + 1 (600). The PRINT in 610 will not go to screen or printer. Rather, the current channel is open to the disk object file and PRINT therefore sends the byte in the Accumulator to the disk. Then normal I/O is restored, and file #1 is accessed again. File #1 is the normal input source for LADS, the read-from-disk channel. Finally, we fall through to INCSA (650).

Although it is one of the simplest events in LADS, INCSA is also one of the most important. On both passes, INCSA

181

raises the PC by 1 for each opcode byte and for each argument byte. Much depends on the fact that INCSA keeps the Program Counter accurate during assembly. A single ignored byte would throw off all address-type labels which followed. (The HERE in 100 HERE LDA 15 is an address-type label.) In consequence, the entire assembled object program would be useless. INCSA just adds 1 to SA (the variable which holds the LADS internal Program Counter). Notice lines 690-710. They add 0 to the high byte of SA. What's the point of that?

The 256th Increment

For every 255 increments, INCSA will have nothing to add to the high byte of SA. But on the 256th increment, it must add 1 to the high byte. How does adding 0 to the high byte add 1 to it? The carry flag. ADC means ADd with Carry. If the carry flag is set, the high byte is incremented. If the low byte is holding 255 when we add 1 to it (670), that will set the carry flag.

The rest of the routines in this Printops subprogram handle the printout of a variety of things: messages, spaces, numbers, the PC address, a carriage return, a source code line number, a source code line, or an error message. And each of these print-to-screen routines has a sister routine. There is a parallel series of routines which print the same thing to the printer.

PRNTMESS (740) will print any ASCII message. There are two special requisite preconditions: The message must be pointed to by the variable TEMP, and the message must end with a 0. PRNTMESS is a simple loop, but it can print any message you want. First the Y Register is set to 0 to act as an index to the message within LADS' source code. Then the loop begins (750) by loading in a character from the message (750). If the character is 0, we exit the loop. Otherwise, the character is printed to the screen. Then we JSR to the sister routine, PTP, which will send the same character to the printer, if requested (780). The Y Register is raised, and we go back for the next character (800).

PRINTSPACE (820) simply prints a space character to the screen and then checks with its sister routine, PTP, to see if the space should also be printed on the printer.

Before printing a number, we first put it into the X variable for safekeeping. Then LADS has to make four tests: Is it printout to screen or to printer, and is it in decimal or in hex numbers? PRNTNUM (860) takes advantage of a routine in BASIC ROM if

182

LADS' printout is in decimal (requested with the .NH, no hex, pseudo-op). When you ask BASIC to list a program, it turns integer bytes into printable ASCII numbers to provide line numbers on the screen. On Commodore computers, the high byte of the integer is put into the Accumulator, the low byte into the X Register, and you JSR to within BASIC ROM where this routine resides (950). In LADS, the address of this ROM routine is called OUTNUM. It's defined for each different computer model in the Defs subprogram.

Hex Default

LADS' default, and probably the most common way to print out numbers during an assembly, is hex. LADS itself handles hex printing. If the HXFLAG is up (870), we JSR to HEXPRINT, a subroutine at the end of the Printops subprogram. We'll get to it in a minute. It's the opposite of the HEX subroutine within the Indisk subprogram which changes hex numbers in ASCII format into integers. The HEXPRINT routine will take an integer and turn it into hex ASCII characters for printout.

After the number has been printed to the screen, we JSR to the sister routine PTPNU (910) to also print it to the printer if necessary. Then the number is restored to the X Register from the X variable (920) before returning to the caller.

PRNTSA (990) is similar to PRNTNUM. The main difference is that PRNTNUM always prints the single byte sent to it in the X Register. By contrast, PRNTSA prints the two bytes in SA, the Program Counter variable. The same four possibilities are tested: printer, screen, hex, or decimal. PRNTSA's sister routine, PTPSA, is called upon from both the hex (1050) and the decimal (1100) versions of this routine.

PRNTCR (1120) prints a carriage return; the 13 is the ASCII code for carriage return on both the screen and a printer. PRNTLINE (1160) prints out a line number from the source code. As each physical line is drawn into view by LADS, its line number is stored in the LINEN variable. This routine also uses that OUTNUM routine from BASIC ROM which prints BASIC'S line numbers during a LIST. Line numbers, in BASIC or LADS, are always decimal. PTPLI (1190) is the sister routine for printer printouts.

PRNTINPUT (1210) prints the contents of the main buffer. Those contents will be the most recent logical line of source code as it appeared in the source code. It uses the PRNTMESS routine

183

Math and Printops: Range Checking and Formatted Output

which sends to the screen any ASCII message which is pointed to by the TEMP variable. The line must end in 0. PRNTMESS (740) handles the printer with the PTP, single-character, test. There is no need for a sister routine within PRNTINPUT.

Error Alert

ERRING (1280) performs the preliminaries to an error message printout. Such messages as SYNTAX ERROR or NAKED LABEL are triggered at various places within LADS. But most of them JSR to ERRING before printing out their particular messages. ERRING rings the bell first. The number 7 is the ASCII code which rings any bells attached to computers or printers. (This works on Apple and PET/CBM computers; the 7 is changed to 253 in the Atari version to produce the same result. The VIC and Commodore 64 have no "bell," so the character 7 will have no effect on those computers.) The purpose of the bell is to alert the programmer that an error has been detected. True, the error message will appear onscreen, but during an assembly of a large program, the programmer might well miss silent error messages sliding up the screen.

On Commodore computers, the character 18 reverses the field of all subsequent characters on a line. This, too, highlights errors. Next (1320), the logical line of source code where the error appears is printed, followed by a carriage return.

It would be simple to make error reports more dramatic. You could stop assembly at that point with a key-testing loop that required the programmer to hit any key to continue. You could JSR FIN and exit to BASIC mode, aborting all further assembly. You could JSR PRNTLINE to emphasize the line number in the source code where the error happened. You could ring the bell ten times. As with all other aspects of LADS, you can make it do what's efficient for you, what's responsive to your own style of programming. Add some special effects here if you wish. Then reassemble your customized version of LADS.

Sister Print Routines

The next few routines are the printer routines: Each is a parallel, sister routine to one of the screen routines discussed above. Each tests the PRINTFLAG and returns if the flag is down, indicating that the user did not request a printout on paper. If the PRINTFLAG is up, output is redirected to the printer (1450-1470) by opening a file channel to the printer. On Commodore

Math and Printops: Range Checking and Formatted Output

computers, the printer is device #4. Then OUTNUM or PRINT or HEXPRINT sends the characters or numbers to the printer (1490, 1680, 1720, 1900, 1960, 2130). After that, normal I/O is restored (1500) and a channel is reopened to file #1, the input-source-code-from-disk mode.

To follow the logic of PTP (1380), PTPNU (1560), PTPSA (1780), or PTPLI (2020), just look at the parallel routines which JSR to them. The purpose, the tests, and the logic are the same. The only difference is that the sister routines described above route their characters to the screen. These routines send characters to a printer.

Printing Hex Numbers

The subprogram Printops concludes with HEXPRINT, an interesting routine which converts a one-byte integer into an ASCII hex string that can be printed to screen or printer.

HEXPRINT operates on a single byte at a time. The byte is first stored temporarily on the stack with PHA (2200). Let's use $4A as an example. The four high bits are stripped off with AND #$0F, leaving $0A. That's one of the characters we need to print. Then we can use a short, simple lookup table to extract the character by its position in the table. In the Tables subprogram is a minitable called HEXA (270). It looks like this:

270 HEXA .BYTE "0123456789ABCDEF

Since the number $0A (10 decimal) is also the tenth character in this table, we can just move the ANDed $0A over to the Y Register (2220) and load HEXA,Y to fetch the ASCII character for $0A, which would be 65 (the letter A). We can stick this character into the X Register; X isn't being used elsewhere in this routine, so it can save the character for us while we look at the highbits.

this time we move the four high bits right over on top of the four low bits. This takes four logical shifts right (2270-2300). After LSRing $4A we get $04. Again, we TAY and load the character 4 from the table (it's 52 decimal). We print this. In $4A, the 4 comes first. Then we recover the A character from the X Register and print it right after the 4 (2350).

Math and Printops: Range Checking and Formatted Output

Math and Printops: Range Checking and Formatted Output

Math and Printops: Range Checking and Formatted Output

cscscscstscscsscscscscscscscscacscsKcscscscscscsiscststacs i^C0CftcS)Mr\iro^in^r~c0CTicSliHCNrn^tinvor--00(TicSiH<Nro^t'incO

HHHNtNNNtNNNNNMfOtneironnnnrifO't^'l'rt'H'rt.H;

Math and Printops: Range Checking and Formatted Output

KSKQISStStSSKlSISlSSIStStSlIStSISIStSiStSlSSltStSISIStS

r~ooffvSrH<Nro^'inix>r--co<TiSi-HCN< v '"H<in^or~-coc^SrHCNro^ , inkDr-~ 'l*'rt''3 , inininininininLriinin^v0kD , ^> l i>u3 kO kD^Du : >r^^

t~~ r— r~ i— t—^ i— i~~

Math and Printops: Range Checking and Formatted Output

o x

CO

Ex H

M

S w o a

S3 O W Eh

O

G H

5s

H

PS

«

w w

Eh 03 O

*fi o

os co

s d

u

a

M Eh

z

H PS

OS

w

Eh fa

S3

w

Eh U

u

w u

<

Dm

en

Eh Z H «

a.

ix

Eh

a

co

id

Cb CO ° £

o os J co w

CO z

w o

S Q CO

Pm co

2 W i-d 2

<C Q

H

u

<

a. w

Eh

w

Eh Z M

K

u S

Eh

O Eh

O

o

O CO

Q

J D O EC CO

Eh H

w H co

Eh

Z -

M (X

05 Eh

os os co z <;

CO CO Eh « D hj ^ OS (X J

CDC^SiHCNOI'^iniXIt^

r~t"-cocococoaococoao

SSB SS

in cn n •* in

C?i CT» (^ CX* CT\

IS <s s <s vfl r~ co <Ti

CT* Ci CT> CT»

SHrnnrf/inio>

rHfHiHi-li-HiHiH^H

Math and Printops: Range Checking and Formatted Output

SQSSSSSSSlSBSlSSSlSISISSISISISSISStSlSiaSS COC^cSrHtNro^LnkOi^coc^SiHr^rn^invDr^COc^cat-icNrOTH-in^ot^

SSHHHHHiHHHHHcMNtNMMNNCNMrscincimmnnn

i—1,-Hi-t'—I i— IHHHr Mr-li-Hi-li—IHrtHrtrli—I H H H H r-IrHiHrHrHrH

Math and Printops: Range Checking and Formatted Output

03

u

u

o

H

£

D Eh

PS

w

Eh

2 H 03

a, o

0

SB H Eh S3 H

Cm O

53

8

CO

CO

<

a, o

en

en <

Cm

X Q J

Cm H Cm

O 53 H

Eh

g

O

a

u

<c J

lit Eh Z H

03 Cm

< J

Pm

Eh

S3

H

K Cm

X Cm

Q H

3 Cm

2

O

u

<

o to

Eh

53

W

Eh

85

O

a

w

>

en

W CO Cm 13 Eh Eh

own.

03 W

Eh 53

H

a; a.

o

Eh < Eh

53 03 H

w 03

> Cm

Z EC U B3

J <* U *

Oh X

CO Q

03

g

w +

H IS 53 in H ro

K rH Cm CO

gg H CO J OS

w w a <

s

2 °

53 H O

o

03 < 03 CO Q CO b J 1-3

53

« 53

O H

03 u;

J H S3

o =*= u

03 X 03

CO Q CO

t-3 J 1-3

• 53

< 03 D

< H Q W J S

Eh ..

Eh CO

W Eh

03 03

a

Cm

X

a

03

Cm

X 53

CI Dm 3 Eh Cu

53

E u

03

U

03 CO

Eh D

o

>* EC =*»= O

X 05 Q CO

Q

53 Cm Eh Cm

S X SB

< O < 03 Q W Q CO

J m i-i b

H

03

Cm

X H

Ct)^K!iH<Nro^in^t^cO(3^SiHcs)rO'<ti^ivor--co<TiSi-icsro^tirivi)r--co

MiH^Hr-lr-Hf-H^HMiHf-HrHiHi-lrHrHi-liHnHrHliHfHi-HrH^Hf-li-li-li-liHf-li-l

Math and Printops: Range Checking and Formatted Output

+ ts in

CO

os w

W 12

Eh H

Z J H

os w

cu <;

u

H

O

o J

fa X < to

< fa

Z M OS

Cm

H X CO

CO D Cm

a J Eh

Eh Cu

Oh HS

co

fa CO Oi fa CO Z Eh Eh Z Eh 03 OS Cm CQ OS

OS Eh

CO D

D O

S*S

<*«

co * u cu

Eh X OS Cu Q CO

< a

+

co

<

CO

Cm Eh Ch

h a <

fa co co

Cu

Cu Eh X

S 0. O

>D 2 J

SI

os

CO

id

<

;*s

Cu rH S3 Eh* O Cu

Z X OS CO H Q CO

id fa J id

OS

ISSSISBISISSSSSISCS11SBISSISISSISISISISSISS IS sss <TiSi-HtNrO'^ir)vi)r~cO(yiiSr-i(Nrn>H/invDr~-00(TiiSi'-irNf r i'* ir ) *£> r-~ 00 CTi ^r^r^r^c^t^r^r^i^r^r^cDcooocorocococDCDCDcricTvcricriO^CTi <y\ o\ (?> d

Math and Printops: Range Checking and Formatted Output

-

CSCSCSIStSISISISCSISISISISISCSISCStStSCSCSISISCSCSCSCSISlSISCS !S^cNro^Ln^r^rocT^cs^(Nro^iri^!^coc^lSiHCNrn'*iri^or--coo , iCS

SSSISSISSISISSlrH'HHHrHHHHHHM(N(N(N(NNCMMt > )(Nn

o)rviMoqo)MOjcNrgcNMC^r^o)r)r^cNCN(NCNCNi(NicNCNCNCNrsio)CNCNCN

Math and Printops: Range Checking and Formatted Output

5)

CD CM CM

a

a z

Z H H J

<

X

x a

Q = Z

M S O

cc

D

< H

> rj

o a-

D

a, cm

a

w

%

a

D

>

o

H DC

W

Q

O < ts

^-» M

CN

^ CN

W H

a Z

H M fa J

w w

Z >H

<

a,

< z z

X M X M

>h < a < < a w x

D W

w a. a «

& m

at, w h

CO E-i fa

IS IS IS IS IS is ts

h Mm <t in ifir^

n m (*i n pi n m

CN CN CN CN CN CN CN

|

0

2

£

01

_c

OJ

C

s

u

a?

o C

"3

1 o

< .2

a o

CO

o u

c o

01 . . > CN

■g IN

!T3 _

<l

u a:

U'J O

r j a

# 3

UJ

1 </)

Q LL

e2^

CC Z UJ

O M _J

it; i-

£r 0:: u.

<5: CO r-

'-i n K'

•0 -i IN

Chapter 8

Pseudo:

I/O and Linked Files

1 1 1 1

"1

1

1

-~

Pseudo:

I/O and Linked Files

All pseudo-ops except .BYTE (and in-line ones like #< or +) are handled by the Pseudo subprogram. Eight pseudo-ops are tested for at the start of Pseudo (50-300). They are: .FILE, .END, ,D, .P, .N, .0, .S, and .H. These tests and the associated JMPs are identical to an ON-GOTO multiple branch structure in a BASIC program. The rest of the Pseudo subprogram is a collection of subroutines which service these various pseudo-ops.

If an unrecognized pseudo-op appears within the source code, an error message is printed out (340-460). If something like .X or .MAP appears, the line number, the start address, and the source code line are printed (350-390). The variable TEMP is set to point to the SYNTAX ERROR message in the Tables subprogram, and that message is sent to screen, and possibly printer, via the PRNTMESS subroutine (440). A carriage return is printed (450), and we return to the Eval subprogram after pulling all the characters of the current source code line. The subroutine PULLINE does this (460).

Assuming, however, that LADS came upon the legitimate pseudo-op .FILE during an assembly, lines 480-830 take the necessary action. .FILE appears at the end of a subprogram. It tells LADS that another subprogram is linked to the one just assembled and that the source code within this next subprogram is to be assembled next, as an extension of the current subprogram. The current source code file will need to be shut down, and the next linked file will need to be opened for business. The next linked file is the one called NAME, for example, in .FILE NAME.

Linking with .FILE

The FILE subroutine starts off by looking for a blank character following the .FILE pseudo-op word (480-510). Locating a blank, it can now store the name of the next file of source code. It pulls in the name, one character at a time, looking for an end-of-line 0 (540) or a byte with the seventh bit set (a tokenized keyword which needs to be stretched out into a full ASCII word). Then each character in NAME is stored in the main buffer (590) as it comes in from the source code.

Pseudo: I/O and Linked Files

When an end-of-line 0 is encountered, the whole filename has been stored in LABEL, the input buffer. And—since Y was counting the number of characters and helping store them in the right place in the buffer—Y now holds the number of characters in the filename, its length. We store Y in the FNAMELEN variable which will be needed by the DOS (Disk Operating System) when the OPEN1 subroutine tries to open or load a program file on the disk.

Now the filename is moved from the LABEL buffer to the FILEN buffer (630-680). Why not just store the name in the FILEN buffer in the first place? First, because the printout routines get their characters and words from LABEL, the main buffer. Second, because there might be a keyword, a tokenized, abbreviated BASIC command within a filename. The filename might be END or IFNOT. And KEYWORD, our detokenization subroutine, acts upon words in LABEL, the main buffer. So, rather than make a separate KEYWORD detokenization subroutine for each buffer, it's easier to bring words into the main buffer first, detokenizing them on the fly. Then move them.

But why, then, not have the OPEN1 subroutine look to the main buffer for its filenames? That way, the names wouldn't need to be moved to FILEN, a separate buffer. True enough, but it helps me and, I suspect, many other programmers to keep things separated by function.

It takes only 14 bytes in LADS to move the filename from the main buffer to the filename buffer. It adds only a few microseconds during assembly time since .FILE is a relatively rare event. It won't happen more than a few times during an entire assembly. It's nowhere near the heavy action of the innermost loops of LADS where every event counts, where every improvement in logic results in a noticeable improvement in speed. So memory use or speed efficiency is not really worth bothering with here. If it's easier for you to visualize the actions of a program (and make sure there are no unwanted interactions), use as many buffers and variables as you want.

Printing Addresses

The next section of this FILE subroutine prints out to screen or printer (690-740). Pass 2 doesn't print the starting address of each linked file. That's one way to tell which pass is currently being assembled. Change the LDA PASS in line 690 to LDA

Pseudo: I/O and Linked Files

#0 if you want the address printed on both passes. The PRNTSA subroutine (from Printops) prints the address in RAM memory where the first byte in the new file will be assembled. PRNTINPUT prints the filename from the main buffer. Then a carriage return prepares for the next screen (or printer) line (740). The whole thing looks like this on the screen:

470A NAME 49FF NEXTNAME

If the .S and .P pseudo-ops are turned off, nothing will be printed to the screen during an assembly except for this list of linked files and their object code addresses. That's the fastest way to assemble any source code. Printing during assembly takes up a considerable amount of time.

The OPEN1 closes the old source code file and opens the new one. OPEN1 is found in the subprogram of the same name. Next, the computer's input channel is switched to file #1, the input-from-disk channel, and two bytes are pulled off the newly opened source code program file. (These first two bytes are, in the Commodore DOS system, ignorable.) Then ENDPRO gets us in position to analyze the first line in this new source code file (800). Finally, the ENDFLAG is set down because there's obviously more code to assemble. We return to line 80 where the RTS (back to the Indisk subprogram) is pulled off the stack, and we JMP directly back into the Eval subprogram to pull in the first source code line of the newly opened file.

The .END Link

The .END pseudo-op is quite like the .FILE pseudo-op. It serves to link the last file in a chain to the first file:

PROG1 (ends with .FILE PROG2)

PROG2 (ends with .FILE PROG3)

PROG3 (ends with .END PROG1, pointing back to the original

file)

This way, the assembler can go through two passes.

.END starts off by printing the word .END (850-940). Then it borrows a good section of the FILE subroutine above the JSRing to line 520. Most of the events in FILE now take place: The name of the new program file is stored in the two buffers, the file is opened, ENDPRO puts us in the right spot to look for

Pseudo: I/O and Linked Files

a new line, and so on. When we return to the END subroutine (970), .END's most important work is now performed: On pass 1, the ENDFLAG is left down (980). But on pass 2, the ENDFLAG is sent up, and that will quickly cause the Eval subprogram to shut the entire LADS engine down.

But if this is pass 1, another very important thing happens: Pass 1 is changed into pass 2. The PASS flag itself is set up (1000).

The original starting address is now retrieved from the TA variable and restored into SA, the main Program Counter variable. This starts us off on the second pass with the correct, original starting address for assembling the object code. The JSR to INDISK gets us pointed to the first true line of source code in that first program file (past the * = symbol), and we RTS back up to line 140 which exits us from this subprogram the same way that the .FILE pseudo-op exits.

Assembly to Disk Object File

The .DISK pseudo-op is an important one: It makes it possible to store the object code, the results of an assembly, as a program on disk. In a way, it's the opposite of .FILE. .FILE pulls in source code from a program file already on the disk; .DISK sends out object code to a new program file being actively created during the assembly process.

On pass 1, nothing is stored to a disk object file, so we branch to PULLJ which is a springboard to PULLINE. PULLINE pulls in the rest of a logical line and prepares us to look at the next logical line.

On pass 2, however, all object code is stored to a disk object file if the .D NAME pseudo-op has been invoked. This storage happens character by character, just the way that object code is sent to the screen or printer. But before these bytes can go into a disk object code file, the file must be opened for writing on disk.

One character is pulled off the source code, moving us past the space character in ,D NAME and pointing to the N in NAME. A little loop (1130-1210) stores the NAME of the object file into the main buffer (for printouts) and into the filename buffer, FILEN, simultaneously. Meanwhile, if any tokenized keywords are detected (seventh bit set), we're directed to translate them to ASCII characters via a JSR KEYWORD (1170). This accomplished, we add ",P,W" onto the end of the filename.

202

Pseudo: I/O and Linked Files

That's Commodore-specific; it tells the DOS that this file is to be a Program/Write file.

At this point, Y holds the length of the filename, and it's then stored in the proper zero page location (1350) for use by the DOS in opening this write file. Now the main input line, the filename, is printed out, and the DISKFLAG is set up (1380). That tells LADS to always send object code bytes to this object file on pass 2 when it has finished assembling each logical line.

An Abnormal Program

The routine OPEN2 in the Openl subprogram will now open the write file on disk (1390), and the channel to that file is made the main output channel at this point (1400-1410). Whatever is PRINTed will now go to the disk write file. And the first two bytes of a program file tell the computer where in RAM memory to load a program file. Normally, for a BASIC program, this load address would be the start of RAM, the start of BASIC'S storage area for programs. But this is an abnormal program. It's machine language; it could go anywhere in RAM. We therefore need to tell the computer what the starting address of this particular program is.

At the very beginning of LADS, the start address is pulled from just beyond the source code's *= symbol. That symbol must be the first item in any source code. The start address is then put into several variables. SA, the Program Counter, gets it, but will keep raising it as each logical line is assembled. SA is a dynamic, changing variable. TA also gets the start address. TA is a "variable," but never changes. Its job is to remember the starting address all through the assembly process. Perhaps TA should be called a constant rather than a variable, but the term variable is generally used in computing to refer to both types of "remember this" storage places.

TA Remembers

In any event, TA will always know where we started assembling. So TA is sent to the disk object file as the first two bytes (1420-1450) and then normal I/O (input from disk source file, output to screen) is restored (1460-1470). Now a disk error is checked for, and we prepare to look at the next logical line via JSR ENDPRO (1500). The RTS is pulled off the stack (it would want to send us back to INDISK), we set the ENDFLAG down

Pseudo: I/O and Linked Files

and JMP back to Eval to analyze the next line of source code (1550).

The PRINTER subroutine responds to a .P pseudo-op. It is ignored on pass 1, but on pass 2 the file to the printer is opened (1590), and the PRINTFLAG is raised. Normal I/O is restored, and we "fall through" to PULLINE, the subroutine which keeps sucking bytes off the current logical line until the end of that line is reached. These bytes are ignored. That's why pseudo-ops should be the only thing on any physical line. Anything following a pseudo-op is sucked in and ignored.

The PULLINE routine finishes when a colon or a 0 is detected. The exit back to STARTLINE in Eval is prepared for by the PLA PLA which throws away the RTS (caused by JSRing to Pseudo from within Indisk). The only difference between a 0 (end-of-physical-line) and a colon (end-of-logical-line) condition is that a 0 requires that we skip over some link bytes in the source code. 0 requires that we first clean off these link bytes by a JSR to ENDPRO (1700). ENDPRO is also necessary in the event that the end of a physical line is also the end of the source code file itself. ENDPRO would detect that.

The .O pseudo-op notifies LADS that you want object code stored into RAM memory during assembly beginning at the start address * = . This is relatively simple: We just print out the .O (1770-1800) and set up the POKEFLAG. (Elsewhere in LADS, the POKEFLAG is queried to determine if object code should be sent to RAM.) Then we exit via PULLINE.

Turning Things Off

The .N pseudo-op turns things off. It can turn four things off: printer printout, RAM object code storage, screen printout, and hexadecimal printout. If .N is detected in the ON-GOTO section of Pseudo above (110-320), we are sent here for another ON-GOTO series of tests (1880-1960). Of course, none of these forms of output are triggered on pass 1, so they don't need to be turned off on pass 1 either. But on pass 2, we are sent to one of the four turn-it-off routines below.

Pseudo: I/O and Linked Files

NIXPRINT (1980) first notifies us that the .NP pseudo-op has been detected in the source code by printing the .NP. Then the PRINTFLAG is lowered (2050), and a carriage return is sent to the printer. This is in case you should want the printer turned on again further along in the source code. (You would turn it on with the .P pseudo-op.) The first line of a reactivated printout must appear on a new line, not as an extension of the previous printout.

Then the printer is turned off with JSR CLOSE (this close-down-a-file routine is in the Openl subprogram), and we exit via PULLINE (2160).

The next three turn-it-off pseudo-ops are simple, and virtually identical. NIXOP prints .NO and sets down the POKEFLAG. NIXHEX prints .NH and sets down the HXFLAG (causing decimal to become the number base for opcode printouts to printer and screen). NIXSCREEN prints .NS and sets down the SFLAG. Each routine exits via PULLINE described above.

Disk Error Trapping

DISERR (2510) checks for an error in disk operation. It could be JSRed to from any place in LADS where you suspect that things aren't likely to go well with the disk. Disk drives differ considerably in their reliability: An unabused Commodore 4040 drive is usually good for years of error-free performance; many of the Commodore 1541 single-drive units, especially the earlier ones, are perhaps best described as sensitive. In any case, how often you feel the need to JSR DISERR for a report on the disk's success in completing an operation will depend on how often your drive is the cause of problems during your other programming experience.

For Commodore computers, a simple check of the ST (status) byte in zero page will reveal many kinds of disk errors. If one is detected, an error message is printed and LADS is shut down (2650) by jumping to FIN within Eval.

Pseudo: I/O and Linked Files

The .S (screen printout on) and .H (hexadecimal number printout) pseudo-ops are the final items to assemble as part of the LADS source code program. The subprogram Table follows, but it's data, not programming.

There's no particular reason why these two pseudo-ops should be the last thing in LADS. They just are.

Also, they're very simple. They each print their names to announce themselves, .S or .H; set up their flags, SFLAG or HXFLAG; and exit through PULLINE. The only notable thing about .S is that it must not set its flag until pass 2.

The .H is a default condition of this assembler. LADS assumes that you want hex output unless you use the .NH to turn off hex and turn decimal on. Of course, you can set up other default conditions which are more harmonic with your own programming needs.

Pseudo: I/O and Linked Files

Pseudo: I/O and Linked Files

a,

ssssisstssistssssiissisissiastssstsssisssss ^SrHCNrnTtinvor~cxic^CSrHrv)m^invDr^ci)CJ\tSiHtNro'*invDr~ooiTi

Pseudo: I/O and Linked Files

w

CQ

fa w h

fa D

ffi

CJ fa

2 SC Eh

H Z

w

M J

2 u

w <

H H

fa J

M

O fc

5 w

« B

E-i H CO W

W

1 £

H H S

[^ CO =tt=

Q, (N >H

2 h a

►3 fa J

J W

CO

<

t

<c

Q J

O J

fa fa

fa m co m

o

H

fa

fa

1-3

H

fa fa

z

o

a

to to

< fa

2 O

w

CO

fa <

Q

W

z

fa

H fa

Eh Eh

z

H

fa fa

W

D fa

Eh Z

ta h fa J

fa

<

CO

in Z

H fa fa fa

Eh

D

fa z

fa M CJ Eh

< z

fa K CO fa Eh

Z « fa CO fa »D

U fa O

fa »«

Eh Z

Z M

fa

CO

fa

Eh CQ

o

3

Eh

Eh X

fa z

fa fa p fa

z z o

SB M M fa H fa fa fa

fa 2 < a

fa fa K Z 0<O#UUUM

O

fa

Q

S Z

Hfafainfafaxfao-'O-PSXXoo ZCOCOMCOCOQCOWCOCOQEhEh

Q

z fa

Eh

D

o

Eh

z

M fa fa

>J3

Eh Eh Eh

< Z Z Z

Q M CTi H CO M

►q fa vD fa r- fa

fa =M= fa

a

z fa < fa

w co Q co

fa 1-3 .J 1-3

=*«= fa

«c fa

Q CO

SK5>CSS)5)CSKC35>5)KC3KKSCSl5>5>KS>SIISCSSKlS)tSCS(S<S S h <^ ci •<* i/i \o r» co en csii-i(Nn'tinkDr-coc3 > iiSrHCN( , o , <tinii)r~cocritS) vo^^^vo^^vovo^t^r^t^i^r^r^r^r~[^r^cococococococococooocri

Pseudo: I/O and Linked Files

cr»cr»cy^cT>c^o><y*c5i o**

St3tSSBIStSSSC3SISSSISISt5SISISSlSl

csi'HCNm^ir)vot^coaNtai-HCNro'S , ioiDr~-oocriSiH

SSBBtSBSSSBHHHHHHHHHHNM H HHH-li— IrHi-liH^Hi-lr-liHi-Hr-liHr-liHrHrHi-lrH

Pseudo: I/O and Linked Files

w

i cm

Cm =#=

s

Q 4 J

S

Dm

>< r »i

z a

W Q

Q Eh Cm CO

IS

co

>H < <

z a e-i

H J CO

2 H

<* H

2

CO M =tfc &M

CO

o Eh

o o

Q

J — D O O Eh EC CO O

z

CO H

W H

!*I H

O OS

Cm S

Eh

O H

Z Z

U H

w

z M

H h

W

OS

O

CO

W OS D

B

Eh

<

K

E^

O SB co

o

E-

O

<

E

CO

Eh K

Z O

M < OS

Cm Om

&M

M H <

OS PS Cm Cm

«;<>h<<>k>hOSPSCJPSX QEhZOEhZEhCOCOZcOQ

w J

H

&M

6<

CO HI

Q

o

z

w

Cm <N O =*

>H J

Eh

O

w

PS

as o 0

z H

Cm O

X OS PS PS < < D CO CO to J J

J b 1-3 b 0- Cm

SISSSISSSSISSSSSISSISSSSSISISSISISSSISSISIS (Nro^^kDt^ctJffNis-HCviro^Lnvot^coc^SrHCNro^Ln^i-^oocTiiSli-HtN

Pseudo: I/O and Linked Files

SSSSSBSSSSSSBSSSSSSSSSSSSSSSSSS ro^LnvOt^cOCT>Kl^rviro^iri^i^cDaNSi-HCsiro^ir)ii)r--COcrilSi--l(Nro inLnLn^irii/iiri^v£^u5vx)^^vDvD^Dr~r~i^t^r^r^t-~r-~i-~r--oocococo

Pseudo: I/O and Linked Files

to

2 W W K U co

o F

H 2 H

w

a,

En O 2

O

tfl

2

a

s

H

CJ w

Q

O Eh

EC CJ Eh H 3 tO

to

2 EC Eh

X W

EC

Eh

D O Eh 2 i-i « ft,

Eh

O 2

o

Eh

EC

2

ft, CJ

Eh CO Z CO CO M HH H M

Oh ft,

.-ft, .. O ..

CD m r- m oo * z * Z *

a, o ft, a ft,

SKSHS

ucoumu

2 H

w co

Eh

D ft,

Eh

D O

K

w

Eh Z

M

a z n, w w t, os t, cj o co

z o a; e-i D

E-i = ft,

2

Eh

2

X

CJ H CO ~ EC X <N X m r- h Z =t*= Z

a ft, o

HEW

fflUffl

04

D

s

J

E*

2 H

U CO

i o

Eh I

E-i Z

Z

5

p

E-<

W Pi K

a; ft, ft,

W « 6h < £ O

o

J z

Eh _ Z •-Z H CD HQJN « ft, * ft,

x « <:

M CO Q

ZbJ

a;

a

u o

- ^

OS Pm

CJ Eh

Eh Z

Z Eh

EC S Eh

CJ O z x n m

Vj'ECrHrt^JJ^I

=tt= CJ =tt= ft, =tt= CJ CJ *

H tS H Z H Oh

a co as « « j

Cu =H= ft, ft, ft, CJ

COQCOCOWCoacOQCGQWCOQ

^•inii)r^coiTisiHMn<tuiio>cooiSHMro*ifnot^cowSHMn^'

COCOCO00Ctl00^(^C^C^CTi^^CriC^CTilSCalSlSSSSSl!S)tSiHiHrHiHi-H

HHHHHHHHrHHiHrHHHHHtMMMNMMlNNNMNNMNN

Pseudo: I/O and Linked Files

os a,

o w

< S3

J H

W J

a «o

3 OS O

3 D CU E-i

E-i CO

W WW

OS fa OS

h O

3 WW

O a OS

IS = = < OS O

U a O M D S3

= = OS Eh U

OS M

o 2

t cj o •«

*= < w

OS J 2 < Eh En H U W H

a a - a •- a eh wj

iHtBHIUHZ tlSJ

PSr-PSr-OSoSlsOD l4Pj=*D)=«=aiDj=B=0-iDj

<os<os<ososrt;<ca<

HCOQWQCOCOQEhE

•»ahjiJ^iJ^h3jaiio

X

£3 OS D

En

W OS

w

<

H

OS DS <C

u

> w en

Eh O D fr> O

a a

w

a

H

a

s w o o

Q

Eh

o co

< w

J OS

E

X w

W OS

X o a

Eh O P M

OS

E- Eh U

-a - a eh

co h tN h a

r> os r~ es os is

* Oi *t= Cu Cu =«=

< OS < OS OS <

Q co q to co a

OS &

a w w

DS

tj

to

a, o

Eh :

m co

a

Eh

a

M OS

a,

ID

< a

CO

a Eh Eh Eh

w a -a -a

W M 03 m n M

OS OS P- OS CO os

U 0j * Dj =*t= Oj

co

X OS < OS < OS

M 10 Q CO Q CO

2bJhJb

SSSBSSSSSISSlSlSSSISSSlSlSSSBSiSSSISSSSl in^r~cocr\iSiHrv)m^Lnvor~-coo>lSiH<Nro^tir)^Dr-co<riSl'-i<Nro>H'in HHHHHMNNCSNiMtNN(NiNmrnmnnnnni , iri>l'H;^^^ I j

CVlrvir^01(NCNCSCNCVI<NCNCNCNCN<N(N(N<N(N<N(N(N(NCN(NCNC\lCNCNCNCN

Pseudo: I/O and Linked Files

2

52

D Eh

3

H

< H

a

u o

fe h

M

u «

w en

04 H

co Q

2

E

D H H K K O Q DS 2 5 < H

"" w

w co

2 H

h a

H Dm 2

E-<

U

w

E-i

H Q

O

a

P

a,

u o

k <

2 J

Bh S fe

a< * co

« < <

co Q E-i

1-3 J oo

W

2 H

J

D

a,

fe 2 1-3 .,

w

m

a

H

>

co

a

< Eh w

CO

Eh CO

X

Q

2

H CO

o cc w tsi

Eh

O 2

fa

Ed O < CO CO

w

o

BJ B, W

Eh

a

o

Eh

2 H

fe

< 2

Q 2

J Eh

Pi K

W Cm

W

o <

CO

CO

H

B<

o Bh

fe CO

o

Eh

Eh

2

H H

o a; o a;

Cm 1/1

CO H

Eh a

2 2

a v

Dm =N=

to

.. M

Dm Q 2 2

W a

a a <t

CO

CO

fe

g

a: o « «

Pi u w <

Eh Eh CO

2

H Ph O ttlh 2 Cm O

M

a; .»co co Eh - co Pi o w 2 2 J

HhJ

a 2 a

W Dm

w ca

D

g

2 H K

Cm

2 W

O 00

§

mooaaiPi<c<<;

2EhOWWQEhQ

< pi pi ,< <

Eh 00 CO |J J CO to h) C« Cu

SI

w

Cm

o

CO

D

a

w

K

H

Eh

2

w

2 S O

Q

Eh

D

sa

CO

Cm O I

o

Q

a w

CO

Dj

oo ;

CO

Dm

=tt=

2

E

D

Eh

w

s

w

o

H £ U

Eh

a

o

Eh 2 H fe

Cm 2

w

w

Pi u

CO

o

2

Q

Bh

Eh Eh CJ 2 - 2 Eh

h m h 2 Si oi co Pi E

M Dm =* Cm Cm

W

Pi Ci < 0i Pi

U CO Q 00 00

- CO 1^3 J 1-3 ^

CO

CO

<c

Ph

2 O

6h

O 2

M Eh CO

H

Eh

D

o

Eh

2

2 W W Pi O CO

fe

co

H

>

U

O Eh

D

Eh fe

Pi

W

2

H J

Cm

< C5 K H

fe

O

Eh CO

w Pi

fe

o

2 O

.. x

00 H

00 Pi

< U Cm 00 =**=

< a <

D W Q

JO J

w

2 H

D

Dm O Cm

< 2

►J b fe

co x w

< Pi

Eh U

co oo

SSIStStSBISISSISlSISS'SlSISBISISISlBlSISSBISiaiSISISIS vOt^rocriSrMM^^^^l^O3<TilS)i-HCNro^J , invDr-~C0criS'-HtNroT) l irivO

r^rv|Ojr^CN<NCNCNCN(N(NCNCNO0CN(N(NCN(NCN(NCN(Nf\)<N(NJCNtNCNlCN(N

Pseudo: I/O and Linked Files

8 g

H PS fa

U

in

PS D Q

s

EC

Oh o I

O

Q

B

co

• EC fa =

<t M

IS

D H H

s

W CD

<!

H PS

a

6

i <

I Q

I J

I

I E-i

I H

I X

I W

o

S PS

fa H

Cm O

CO

a ps

o

IZ

o

OS o

Eh Eh O <

2 •- & Eh J

H tN H 2 fa

a, # dj Dj *= W

PS < PS PS <c <

CO Q CO W Q Eh

b J hj ho J to

•> to

H fa

h en J <:

J H

D

fa fa ►J pj H S fa b •

oooooooooo

r-cowsHtNn^irnD r-r-i^cocococococooo

CNCNtNCNOJCN(N(NCMtN

d

o

rt U

♦ i-l

o

Ji

I

Q

-d

Ph

N

00

|

& o u

O QJ

-* «S

O w>

<"■■> 5

CM JS

*-"§

.S c

1*T

O OO

& B

QJ t>0 in O

^h J1

Uh Pi

O

c S o o

i-l

> m

QJ \D

« c.s £ So

CO

u o

Q

I

o

Q D 6J CO

Ch

Cd J

HI fa

to

«:

Eh

to

D 1-3

U

Eh

w

a

fa Q

Eh <Z H H

CO H H Cu

•- Eh

(^ H ^O CD *

Cm H S J CJ H

fa

H

fa PS

CO CO fa hO

IS IS

.-I KD

fa Q

fa % O

Q CC PS Eh H

O cc

13 Eh

w J OS

o

W fa

>

CO <

co

>H

OS >H

la

H Q Cu fflHO

Eh I fa < O OHQ

PS D CO U fa H co

Eh - Cu >H OS

m B •«

fa ■- 2 < fa

Eh J

+ + < ft

CO Eh

2

HJ

fa

os la

Eh fa fa

a >h

fa Eh ►J CO

fa

o

X Eh

o w

la j

H H

J fa

W >H

Eh OS

H <

os a

03 rH +

OS -PS

Eh Eh Eh

fa la fa 2 h a

H OS H J fa J

HQeQEHQcQEHDOCOQ COJCOcOtJCOCOfaJhoiJ

DClH/imcMBlsii/nflis 130013131301/1101010 tSOOOOOOm^'sj'^

Pseudo: I/O and Linked Files

s

1

"

Chapter 9

Tables:

Data, Messages, Variables

~

Tables:

Data, Messages, Variables

Computers are information processors. Data is another word for information. This points up the difference between the two distinct sections of any computer program: code and data. The code, or program proper, is a list of actions for the computer to take. The data is the information upon which those actions are based.

Data is usually separated from the code; it might even be outside the computer. Sometimes data is on a disk file, sometimes on tape, sometimes in the user's brain, as when a program halts and asks for input from a keyboard. In all of these cases, though, the code is segregated from the data which it processes.

An Odd Duck

LADS processes source code, turning it into runnable object code. It takes a list of actions like LDA #75:STA SCREEN and turns them into computer-understandable machine language object programs.

LADS gets its data from two sources, a disk source code file (or source code in RAM) and also from the Tables subprogram. Tables isn't really a subprogram, of course. We're forced to call it that because there isn't a better word. It's really an odd duck. There are no commands to the computer within Tables. It's pure information. Essential information, true, but there are no ML instructions in Tables. Just definitions, messages, pointers, buffers, flags, and registers. LADS couldn't operate without them, but they're not active programming instructions—they're for reference.

Three Parallel Tables

Tables starts out, appropriately enough, with three parallel tables: MNEMONICS, TYPES, and OPS. Each table contains 56 pieces of information. MNEMONICS holds the names of all the 6502 mnemonics like LDA and INY. TYPES identifies the category of each mnemonic (we'll get to this in a minute). And OPS provides an opcode for each category. To see how these three tables work together, let's look at the first item in the first table, the mnemonic LDA.

Tables: Data, Messages, Variables

In your machine language programming, you might want to load the Accumulator with the number 1. You would write:

100 LDA #1

The computer wouldn't grasp the meaning of the ASCII characters L-D-A-#-l at all. They're for our convenience, not its.

We think alphabetically or alphanumerically. It thinks binarily. It wants pure numbers. The CPU, the "thinking" part of the 6502 chip, takes action according to a code of its own, but this code isn't the ASCII code. It's an opcode, an operations code. The CPU will place a number into the Accumulator, the A Register, if it comes across any of the following numbers: 161, 165, 169, 173, 177, 181, 185, or 189. Each of these numbers is an opcode for LDA. But each one loads from a different place. The different numbers represent the opcodes for the eight different addressing modes available to LDA. They are:

Most of the mnemonics can use a variety of addressing modes. LDA can be addressed these eight ways, LDY can be addressed five ways, and so on. That's where TYPES comes in. There are ten TYPES, and each opcode falls into one of the ten categories. Mnemonics are grouped according to their addressing mode's similarities. The mnemonics cluster into TYPES according to the way that they can be addressed:

Type 0:

RTS, INY, DEY, DEX, INX, SEC,

CLC, TAX, TAY, TXA, TYA, PHA,

PLA, BRK, CLD, CLI, PHP, PLP,

RTI, SED, SEI, TSX, TXS, CLV NOP

Tables: Data, Messages, Variables

(Each of these mnemonics takes up only one byte in memory; each is only capable of Implied addressing—they have no argument, no address.)

Type 1:

LDA, CMP, STA, SBC, ADC, AND, ORA, EOR

(Type 1 mnemonics have the largest number of possible addressing modes, eight. See the list for LDA above.)

Type 2:

STY, STX, DEC, INC

(These are fairly restricted in their addressing options. STY has only three possibilities: Absolute, Zero Page, and Zero Page,X. STX can perform only Absolute, Zero Page, and Zero Page,Y [it's the only one which can use this Zero Y mode]. DEC and INC can do Absolute; Zero Page; Zero Page,X; and Absolute,X.)

Type 3:

ROL, ROR, LSR, ASL

(These are the bit-shifting, "logical" instructions. They can be addressed in the following modes: Absolute; Zero Page; Zero Page,X; Absolute,X; and one which is reserved for them alone, Accumulator mode. In that mode, the number held in the Accumulator is acted upon.)

Type 4:

CPY, CPX

(The compare X or Y can use Immediate, Absolute, or Zero Page modes.)

Type 5:

LDY, LDX

(These loads are more restricted in their addressing possibilities than LDA. LDX can use Immediate; Absolute; Zero Page; Absolute,Y; and Zero Page,Y. LDY can use Immediate; Absolute; Zero Page; Zero Page,X; and Absolute,X. Notice that they cannot index themselves; ,X modes are possible only with LDY and vice versa.)

Type 6:

JMP

(This is a special case; it stands alone. It has two ways of addressing: the extremely common Absolute mode and the ex-

Tables: Data, Messages, Variables

tremely rare Indirect mode, JMP (via this). Because most programming contains many JMPs, it should have its own category. Also, the only other mnemonic which is essentially limited to Absolute addressing is JSR, and it gets a category all to itself as well.)

Type 7: BIT

(This one is also an oddity. It too needs a category all its own. BIT can use only Absolute or Zero Page addressing.)

Type 8:

BCS, BEQ, BCC, BNE, BMI, BPL,

BVC, BVS

(All the branch instructions collect together as type 8. They have only one addressing mode, Relative, and they are the only instructions which can use this mode.)

Type 9:

JSR

(It can only Absolute address.)

Each of these groups derives from the arrangement of the opcodes. The patterns are more easily visualized if you look at the opcodes laid out in a table according to their numeric values.

Tables: Data, Messages, Variables

cj 13 0 y a

O _ o

I

Tables: Data, Messages, Variables

Notice the relationship between LDA (15,X) and LDA #15. The former has an opcode of 161; the latter, 169. As the Eval subprogram goes through the source code line, it is looking for clues to the addressing mode: Is there a #, a comma, a parenthesis, an X, or a Y?

Each of these things, combined with the TYPE, tells Eval when to raise the value of the original opcode (let's call it the base opcode) assigned to the mnemonic from the OPS table. If Eval finds a # symbol, it adds 8 to the base opcode and goes right to the TWOS exit. It knows then that this opcode should be 169 (161 + 8) and that there will be two bytes to assemble: Immediate mode addressing uses two bytes. (All the other mnemonics grouped with LDA as type 1 will also add 8 to their base opcodes to signify their Immediate addressing modes.)

The base opcodes are in that third table called OPS (190). The Eval subprogram looks up each mnemonic in the MNEMONICS table, and then the numbers extracted from the TYPES and OPS tables are stored in the variables TYPE and OP for future reference. Finally, Eval starts looking for those # and ) clues within the source code line. These clues cause Eval to add 4 or 8 or 16 or sometimes even 24 to the base opcode. This adjusts the base opcode upward so it will eventually become the correct opcode for the addressing mode being used.

CMP is grouped with LDA as a type 1 mnemonic. That's because a # will add 8 to either of their base opcodes and result in the correct, final opcode for Immediate addressing. The base opcode for CMP is 193, which, unadjusted, would stand for CMP (15,X). If we come upon a # following the CMP, however, 8 is added to the 193, giving 201, the correct opcode for CMP #15. Then Eval would JMP to TWOS and conclude assembly of that line of source code.

In each case, the base opcode in the OPS table is the lowest possible opcode number from among the addressing mode options available to each mnemonic. As the evaluation process proceeds throughout the Eval subprogram, the discovery of the various addressing modes triggers additions to the base opcode. In the end, when Eval finally releases a source code line, the right opcode has been achieved.

Returning to the data within the Tables subprogram, we next come upon the little HEXA table (270). It lists all the digits found in hexadecimal numbers. It's used as a lookup table

Tables: Data, Messages, Variables

when LADS translates an internal two-byte integer into a printable, readable ASCII hexadecimal number like F-F-D-2.

The Six Bufferettes

Here are the buffers (290-340). They are constantly being filled with a source code line, evaluated, and then cleaned off by being filled with zeros. They are separated into six different bufferettes primarily for the programmer's benefit. It's easier to visualize different actions if the buffers have different names.

LABEL is the main buffer—every source code line comes into it. BUFFER is where arguments are sent for further study. The rest of them are used for special-purpose analysis. Things like hex numbers are moved up to HEXBUF, for example, so they will be isolated from other characters and can be translated.

One other buffer, distant from the rest, is needed. LADS stores comments (remarks following semicolons in the source code) into a buffer normally used by BASIC to hold program lines. The location of this buffer depends on each computer's memory organization and so it is defined in the Defs subprogram.

The computer's Accumulator and Y and X are called registers. They're like hypervariables inside the 6502 chip—they are constantly changing. Calling them registers serves to distinguish them from program-created variables or other special locations within the computer. The three variables RADD, VREND, and TSTORE are called registers in LADS. That's largely the result of whimsy. There are as yet no established conventions concerning how to describe storage areas in ML programming. In this book we're variously referring to these set-aside bytes as flags, variables, registers, pointers, vectors, etc. (See Chapter 1).

In reality, they're all pretty much the same thing: Just some RAM memory space we've allocated with the .BYTE pseudo-op (or identified in zero page by definition using the = pseudo-op like STATUS = $FD). But it's nice to use various terms. It helps to remember things and, sometimes, it even helps to describe the purpose or function of a particular variable. Pointers, for example, are always associated with the Indirect Y addressing mode—LDA (POINTER),Y. They point to some address in RAM.

Tables: Data, Messages, Variables

Registers Used by Valdec

Anyway, these three variables are described (350) as registers. RADD holds numbers being added to other numbers. VREND holds the length of the ASCII version of a number while it's being turned into an integer. TSTORE holds the interim results of multiplication. All three "registers" are used by the Valdec subprogram.

Lines 400-460 contain the various error messages. Note that each one ends with .BYTE 0 to stick a delimiting 0 in after the message itself. This 0 tells PRNTMESS (the subroutine in the Printops subprogram which prints messages) where to stop.

The rest of Tables contains variables, pointers, and registers. Notice that there are no zero page variables here. Zero page variables, pointers especially, are most useful for Indirect Y addressing, but you won't need too many of them. In fact, you won't be allowed to use much of zero page because it is so popular with your computer's operating systems and languages. But the most important thing to remember about any zero page space that you do use is: Zero page variables must be defined at the start of your assembler source code. They are unique in this. Any other equates can be defined anywhere in the source code. And, of course, the address-type PC variables or labels can be defined anywhere.

OP and TYPE are variables which hold information about the mnemonic currently under investigation during assembly. After a mnemonic is located in the MNEMONIC table, the matching TYPE and base opcode are pulled out of their tables and stored into the variables OP and TP for later reference (480-490). TA is the permanent storage area for the start address of assembly, the original * =.

Source Code Line Numbers

LINEN holds the source code line number of whatever physical line is currently being assembled. ENDFLAG tells Eval when to shut down assembly. It is incremented by the .END pseudo-op. WORK is used by several routines within LADS as a convenient place to temporarily leave two-byte values.

RESULT is an important variable. It holds the argument of each opcode. When an argument (expression-type) label like STA HERE is encountered, the label HERE is looked up by the subprogram Array and the integer value of the word HERE is placed into RESULT. When a hex argument like STA $1500

Tables: Data, Messages, Variables

comes in from the source code, the subprogram Indisk translates the characters $1500 into an integer value and stores that value in RESULT. Likewise, a decimal argument like STA 5376 is sent to RESULT after it's evaluated in the Eval subprogram. For every addressing mode which has an argument, the argument is stored in RESULT after it's been evaluated.

ARGSIZE holds the length of each argument, how many characters long it is. For example, ARGSIZE would hold a 7 for the argument in LDA (155),Y since (155),Y is seven characters long. It is used in the Eval subprogram in lines 1670, 2250, 2750, and 3020.

EXPRESSF is a flag which shows whether or not there is a label being used as an argument. LDA 15 would leave EXPRESSF down. LDA NAME would set it up. It is used in the Eval subprogram at lines 740, 1470, 1510, 1590, and 1700.

HEXFLAG tells the Eval subprogram whether or not it must calculate a decimal argument. Hex arguments are calculated (and left in RESULT) by the Indisk subprogram. Decimal arguments, however, need to be worked out by Eval. HEXFLAG is used in lines 550 and 1680 in Eval.

HEXLEN holds the length of a hex number. It is used in Indisk in lines 2170, 2240, and 2490.

KEYNUM holds the position of a keyword (a BASIC command) in the table of keywords in ROM BASIC. It is used in Indisk in 1060, 1080, 4260, and 4280.

LABSIZE is used in the Equate subprogram to hold the number of characters in an equate-type label (such as NAME = 22). It is used in lines 120, 160, and 410.

LABPTR is also used by Equate. It points to the position in the label array where the integer value of a label should be stored. It is found in lines 600 and 750.

ARRAYTOP points to the highest byte in the label array. It is where we start any search through the labels. Identical to TA, ARRAYTOP also represents the start of the LADS assembler in memory, minus one. It is used in Equate in lines 110 and 150 and in Array in lines 30 and 50.

A List of Flags and Variables

BUFLAG goes up when a line of source code contains # or (. These symbols are important when determining addressing mode, but must be ignored in evaluating arguments (the numeric value of the expression). This flag is used in lines 470

Tables: Data, Messages, Variables

and 1020 in Array and in lines 750 and 1400 in Eval.

PASS is used frequently throughout the entire LADS program—it shows which pass we're currently on during assembly. A 0 in PASS signifies pass 1; a 1 represents pass 2.

The three variables A, X, and Y are often called upon to temporarily hold the values in the 6502 registers after which they were named. They are temporary storage areas.

PT is a temporary storage area to hold the PARRAY dynamic pointer in the Array subprogram.

BNUMFLAG and BFLAG are used in the evaluation of the .BYTE pseudo-op in the Indisk subprogram.

ADDNUM holds the value of the number following the + pseudo-op. For example, it would hold 78 if this were the source code: LDA LABEL + 78.

The PLUSFLAG shows that there is something in the ADDNUM variable which must be added to the label in an argument. It shows that the + pseudo-op appears in the current source code line.

BYTEFLAG shows that the < or > pseudo-op appears in the current source code line. It is an odd flag in that it has more than two states. It can be 0 indicating no < or >. And it can be 1 or 2 to distinguish between < and >.

DISKFLAG means the .D NAME pseudo-op was activated and so object code should be sent to a disk object file to create a runnable ML program.

PRINTFLAG means the .P pseudo-op was activated and a listing should go to the printer for a hard copy record of assembly.

POKEFLAG means the .0 pseudo-op was activated and all object code generated by assembly should be POKEd into RAM memory.

COLFLAG is used in the Indisk subprogram to show that the previously assembled line of source code ended with a colon rather than a 0 (end of physical line). It tells Indisk not to look for a new source code line number.

FOUNDFLAG goes up when the same word is found more than once within the label array, proving that a label has been redefined. That's illegal and results in an error message. This flag is used in the Array subprogram.

Tables: Data, Messages, Variables

SFLAG means the .S pseudo-op is being used and a visible listing of source and object code should appear on the screen during assembly.

HXFLAG responds to the .H pseudo-op. If set (that's the default, the normal start-up condition in LADS), all opcodes and arguments are printed (to screen or printer) in hexadecimal. HXFLAG is turned off by the .NH (no hex) pseudo-op and causes opcodes and arguments to be printed as decimal numbers.

LOCFLAG, when set, tells the printout routines within the Eval subprogram that they need to print a PC address-type label. For example, a line like:

100 START LDA #GREEN

requires special handling so that the address-type label START will be printed on screen or printer in the correct format (or that it will be printed at all). LOCFLAG is used in Eval in lines 790, 1210, and 3510.

BABFLAG shows that there is a semicolon on a line of source code. It signifies that a REMark, a comment, appears on this line. It tells the printout routines that there is a comment which must also be printed on the screen or the printer following the printout of the business part of a line.

Tables: Data, Messages, Variables

"

a. a.

EC O

Cu 53

J >

W ij rH

< u

H W CO

J X

a x

JWCDSSISCOIS U Eh

w w

JMCMCN^-tSrHlS

O Eh tfaiHvoistscom

>j>iLn(Nscom pq cu ea

s : -(OS HQCO

HWWHWHWttl WEhEhWEhEhEhEhEh

>nCQCQ>HCQCQeqcaca

m • • e-i

ro

<?•

10

■* H

rH

ts is ts

•rj- CM CM

r-f

CN

ix> n cm .H <s cn cm

SPIN

ts ts

VO >X>

ts .h r~

t3 rH CN

SHH

ts a co E-i ts

ts >H CN

m

W • H

E Eh

S>h W >h

CQ Cm cq

• o •

*x> in

in ts

CN rH

CN CN

CN

CM (N (S in co

fN

CM

>X>

co co ro

ts H in

CO IX)

CN CO

IX) ^

(N rH

n co

CN ts co

CO HlB

m

<*

CO CO rH

CN <h.

in

CO rH

CO >X> CO

rH rH (N

<s

*S> CN >X>

w

CQ

<

Eh

s

Eh

D W O C<

OS i H

x w E

H Q D

E u m ea <

I CM

I CO

i r-

I VO

I in

co co cm •*

CN

co ^>

CN

rH CS CN CM tS rH •*

rH I ■*

I n

I CN

CN CO

H H W W H

pH PH >H ^H >H

cq CQ CQ CQ CQ

I S)

I

I H I Eh I >H I CD I I I I

< X I w

X

tstststststststs tststststSrHCNro*tin>x>r-

iflMBIJlHrlHrlrlHHH

tststststststststststs cocMtSrHCNf r )'S , inix>r-~co

rHrHCNCNCNCNCNCNCNCNCN

Tables: Data, Messages, Variables

s

KISISK>IS<SIS!StS<S!S(St3KltStS!SIStSISISIS!S<SISISS)tStS!SIS ^iSrHCNrn^in^or~ooc^tS'-H(Nro^tir)i l Di^co<TiiS'H(NrO'<a , invDr~oo<ri

Tables: Data, Messages, Variables

fa

tsisisisisistststsistsistststsisiststststsisisisisisis SHtNnTtu liD |va)OM3Hoin^irncrs(B(^QHCMn<ti/ito l £vOk0i£>i£vai£i£i£i'J3t^r^r^r-t^t--^r~.r^r^cDO3faaDcoct)co

Tables: Data, Messages, Variables

so c

s

IS

ts ts

IS

ts

tS IS IS tS IS IS IS IS IS

0

u

is

IS IS

IS IS IS

IS IS IS

IS

is ts

IS

IS ts

IS IS ts

IS

is ts

ts ts IS

IS

ts ts

(S

ts ts

ts ts ts

ts ts ts

IS

IS ts

IS

ts ts ts ts

IS

H W E-i tS E-i

>H >H

pa ts m

in

IS

ts

ts ts

ts

IS

ts ts

ts ts

ts ts

ts ts

IS

ts ts

IS

IS IS

IS IS

IS

ts

ts ts

ts ts

IS IS IS IS

ts

ts ts

ts

IS IS

ts is ts

IS

ts ts

IS

ts ts

ts ts ts

ts ts ts

ts ts IS

ts ts ts

ts ts ts

IS

ts ts ts

ts IS

IS IS IS

IS IS ts

IS

is ts

ts ts ts

IS

ts ts

ca

tS E-i IS

>H

tarns

IS IS IS

ts

w

tS E-i

>-<

IS CQ

ts 05

IS E-i

a,

tS 2

a

IS

ro CO

W W J J H

H En

fa

Eh D

a, Z

Eh E-i

2 g

H Cd

o5 2

05 05

D D

u u

Cd Cd

nc DC

E-i H

co co

Q Q

o o

33 DC

M

w

E-<

>H

CQ

o

05

Eh

o

05 rH

w

o ts

<

2 IS

2 H

I

CO IS

o .» a h

IS IS

ts IS

M H

Eh Eh

>H >H

CQ CQ

2 2

W W

Dj Oj

o o

fa s

< a B

co

IS <*

<? i-H

•tf vfl ro

.-H CO

ts a\

IS <*

r- .-i t ts

■H ro

rH CO IS

IS ts

IS

rH tS

ts

H CT5

1^ Eh 't

^f >H ,-H

iH CQ

• CO

in cm

^t Eh .H M

W 05 W

Eh 3 Eh

>i 2 !*

CQ 0< CQ

■ o •

^'

i-H LT5

M CO

IS CO

l£> .-H

.-n ro co

IS

r» ■<*

r- Tt in

i-h ro

is co

ro

co is is

co is ts

i-H tS tS

ro ts IS

co

IS IS

IS ■* IS IS

IS is ts

in ro ts ■* IS CO IS IS

i-H

IS ts IS is ts

ts ts ts IS is ts

^o

tS ^ IS K> IS IS

i-H r-H H IS(NM

Cd

w u

U H

M >

> w

Cd Q Q

Eh Eh D

D a,

Cu Eh 2 D H O

Eh Eh

2 2

bJ H

05 05

05 05

D D

u u w la

Eh Eh

o o u <; * =«=

ta

&q H o

H H W fa Pn o

ca ia S K ffi O

Eh Eh Eh

co W to

Q Q Pj J J S

o o ca

O

ca

05

I

>H

fa

o

ca o

cS o

Eh CO

cu

S I ca i

Eh I

ca ca ts is

Eh

>> >■

m cq

CQ IS CQ

a

ca

Eh >H

cq 05

ca ca ts £h >h ca

CQ CQ Eh

05 s

ca ca

CO CO M o

O O 2 2

JJHOi

u u o o

1 CO

I fa

i ca

I Q

I Q

I 2

i ca

< N

IS IS in IS ts is ts ^f in in \o (*« co ctn

CO CO CO CO CO CO CO

iststststsisiststs is^HcNoo^finkor^co

CT5CJ5CT5Cr\CJ\C5CT5CT5(T5

is ts

IS tS H

CT5 IS IS

ON i-< iH

Tables: Data, Messages, Variables

s

60

c

1

o

CD

to

2

a .S n

•rt c J

rt - •■ O

™ tn ,-h -

O CD i '-L

eg

> a u

*c .2 w '■-'' ■"•"

„£ I .5 LL ft

H oi ra o > u.

. £-% c«g

fO «« -7.

1 Si "g

J1 S IN -C

P-t -t CO CO

236

c u _ u » "C .2 w '■-■• en

■ Chapter 10

6502 Instruction

1 ~i

n

"I

6502 Instruction Set

Here are the 56 mnemonics, the 56 instructions you can give the 6502 (or 6510) chip. Each of them is described in several ways: what it does, what major uses it has in ML programming, what addressing modes it can use, what flags it affects, its opcode (hex/decimal), and the number of bytes it uses up.

ADC

What it does: Adds byte in memory to the byte in the Accumulator, plus the carry flag if set. Sets the carry flag if result exceeds 255. The result is left in the Accumulator.

Major uses: Adds two numbers together. If the carry flag is set prior to an ADC, the resulting number will be one greater than the total of the two numbers being added (the carry is added to the result). Thus, one always clears the carry (CLC) before beginning any addition operation. Following an ADC, a set (up) carry flag indicates that the result exceeded one byte's capacity (was greater than 255), so you can chain-add bytes by subsequent ADCs without any further CLCs (see "Multi-Byte Addition" in Appendix D).

Other flags affected by addition include the V (overflow) flag. This flag is rarely of any interest to the programmer. It merely indicates that a result became larger than could be held within bits 0-6. In other words, the result "overflowed" into bit 7, the highest bit in a byte. Of greater importance is the fact that the Z is set if the result of an addition is zero. Also the N flag is set if bit 7 is set. This N flag is called the "negative" flag because you can manipulate bytes thinking of the seventh bit as a sign (+ or —) to accomplish "signed arithmetic" if you want to. In this mode, each byte can hold a maximum value of 127 (since the seventh bit is used to reveal the number's sign). The B branching instruction's Relative addressing mode uses this kind of arithmetic.

ADC can be used following an SED which puts the 6502 into "decimal mode." Here's an example. Note that the number 75 is decimal after you SED:

6502 Instruction Set

SED CLC

LDA #75

ADC #$05 (this will result in 80)

CLD (always get rid of decimal mode as soon as you've

finished)

Attractive as it sounds, the decimal mode isn't of much real value to the programmer. LADS will let you work in decimal if you want to without requiring that you enter the 6502's mode. Just leave off the $ and LADS will handle the decimal numbers for you.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Immediate ADC #15 $69/105 2

Zero Page ADC 15 $65/101 2

ZeroPage,X ADC 15,X $75/117 2

Absolute ADC 1500 $6D/109 3

Absolute,X ADC 1500,X $7D/125 3

Absolute,Y ADC 1500,Y $79/121 3

Indirect,X ADC (15,X) $61/97 2

Indirect^ ADC (15),Y $71/113 2

Affected flags: N Z C V

AND

What it does: Logical ANDs the byte in memory with the byte in the Accumulator. The result is left in the Accumulator. All bits in both bytes are compared, and if both bits are 1, the result is 1. If either or both bits are 0, the result is 0.

Major uses: Most of the time, AND is used to turn bits off. Let's say that you are pulling in numbers higher than 128 (10000000 and higher) and you want to "unshift" them and print them as lowercase letters. You can then put a zero into the seventh bit of your "mask" and then AND the mask with the number being unshifted:

LDA ? (test number)

AND #$7F (01111111)

6502 Instruction Set

(If either bit is 0, the result will be 0. So the seventh bit of the test number is turned off here and all the other bits in the test number are unaffected.)

Addressing Modes:

ASL

What it does: Shifts the bits in a byte to the left by 1. This byte can be in the Accumulator or in memory, depending on the addressing mode. The shift moves the seventh bit into the carry flag and shoves a 0 into the zeroth bit.

nnAAn/)/)

Bit Bit Bit Bit Bit Bit Bit Bit 7 6 5 4 3 2 10

Major uses: Allows you to multiply a number by 2. Numbers bigger than 255 can be manipulated using ASL with ROL (see "Multiplication" in Appendix D).

A secondary use is to move the lower four bits in a byte (a four-bit unit is often called a nybble) into the higher four bits. The lower bits are replaced by zeros, since ASL stuffs zeros into the zeroth bit of a byte. You move the lower to the higher nybble of a byte by: ASL ASL ASL ASL.

6502 Instruction Set

Addressing Modes:

Name Format

Opcode

Number of Bytes Used

1 2 2 3 3

BCC

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the carry flag is clear. In effect, it branches if the second item is lower than the first, as in: LDA #150: CMP #149 or LDA #22: SBC #15. These actions would clear the carry and, triggering BCC, a branch would take place.

Major uses: For testing the results of CMP or ADC or other operations which affect the carry flag. IF-THEN or ON-GOTO type structures in ML can involve the BCC test. It is similar to BASIC'S > instruction.

Addressing Modes:

Name Format

Relative BCC addr.

Affected flags: none of them.

Opcode

$90/144

Number of Bytes Used

2

BCS

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the carry flag is set. In effect, it branches if the second item is higher than the first, as in: LDA #150: CMP #249 or LDA #22: SBC #85. These actions would set the carry and, triggering BCS, a branch would take place.

Major uses: For testing the results of LDA or ADC or other operations which affect the carry flag. IF-THEN or ON-

6502 Instruction Set

GOTO type structures in ML can involve the BCC test. It is similar to BASIC'S < instruction.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BCS addr. $B0/176 2

Affected flags: none of them.

BED

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the zero flag (Z) is set. In other words, it branches if an action on two bytes results in a 0, as in: LDA #150: CMP #150 or LDA #22: SBC #22. These actions would set the zero flag, so the branch would take place.

Major uses: For testing the results of LDA or ADC or other operations which affect the carry flag. IF-THEN or ON-GOTO type structures in ML can involve the BEQ test. It is similar to BASIC'S = instruction. Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BEQ addr. $F0/240 2

Affected flags: none of them.

_ BIT

What it does: Tests the bits in the byte in memory against the bits in the byte held in the Accumulator. The bytes (memory and Accumulator) are unaffected. BIT merely sets flags. The Z flag is set as if an Accumulator AND memory had been performed. The V flag and the N flag receive copies of the .^ sixth and seventh bits of the tested number.

Major uses: Although BIT has the advantage of not having any effect on the tested numbers, it is infrequently used mm because you cannot employ the Immediate addressing mode with it. Other tests (CMP and AND, for example) can be used instead.

6502 Instruction Set

Addressing Modes:

BMI

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the negative (N) flag is set. In effect, it branches if the seventh bit has been set by the most recent event: LDA #150 or LDA #128 would set the seventh bit. These actions would set the N flag, signifying that a minus number is present if you are using signed arithmetic or that there is a shifted character (or a BASIC keyword) if you are thinking of a byte in terms of the ASCII code.

Major uses: Testing for BASIC keywords, shifted ASCII, or graphics symbols. Testing for + or — in signed arithmetic.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BMI addr. $30/48 2

Affected flags: none of them.

BNE

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the zero flag is clear. In other words, it branches if the result of the most recent event is not zero, as in: LDA #150: SBC #120 or LDA #128: CMP #125. These actions would clear the Z flag, signifying that a result was not 0.

Major uses: The reverse of BEQ. BNE means Branch if Not Equal. Since a CMP subtracts one number from another to perform its comparison, a 0 result means that they are equal. Any other result will trigger a BNE (not equal). Like the other B branch instructions, it has uses in IF-THEN, ON-GOTO type structures and is used as a way to exit loops (for

6502 Instruction Set

example, BNE will branch back to the start of a loop until a 0 delimiter is encountered at the end of a text message). BNE is like BASIC'S <> instruction.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BNE addr. $D0/208 2

Affected flags: none of them.

BPL

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the N flag is clear. In effect, it branches if the seventh bit is clear in the most recent event, as in: LDA #12 or LDA #127. These actions would clear the N flag, signifying that a plus number (or zero) is present in signed arithmetic mode.

Major uses: For testing the results of LDA or ADC or other operations which affect the negative (N) flag. IF-THEN or ON-GOTO type structures in ML can involve the BCC test. It is the opposite of the BMI instruction. BPL can be used for tests of "unshifted" ASCII characters and other bytes which have the seventh bit off and so are lower than 128 (OXXXXXXX).

Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BPL addr. $10/16 2

Affected flags: none of them.

BRK

What it does: Causes a forced interrupt. This interrupt cannot be masked (prevented) by setting the I (interrupt) flag within the Status Register. If there is a Break Interrupt Vector (a vector is like a pointer) in the computer, it may point to a resident monitor if the computer has one. The PC and the Sta-

6502 Instruction Set

tus Register are saved on the stack. The PC points to the location of the BRK + 2.

Major uses: Debugging an ML program can often start with a sprinkling of BRKs into suspicious locations within the code. The ML is executed, a BRK stops execution and drops you into the monitor, you examine registers or tables or variables to see if they are as they should be at this point in the execution, and then you restart execution from the breakpoint. This instruction is essentially identical to the actions and uses of the STOP command in BASIC.

Addressing Modes:

BVC

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the V (overflow) flag is clear.

Major uses: None. In practice, few programmers use "signed" arithmetic where the seventh bit is devoted to indicating a positive or negative number (a set seventh bit means a negative number). The V flag has the job of notifying you when you've added, say 120 + 30, and have therefore set the seventh bit via an "overflow" (a result greater than 127). The result of your addition of two positive numbers should not be seen as a negative number, but the seventh bit is set. The V flag can be tested and will then reveal that your answer is still positive, but an overflow took place.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BVC addr. $50/80 2

Affected flags: none of them.

6502 Instruction Set

BVS

What it does: Branches up to 127 bytes forward or 128 bytes backward from its own address if the V (overflow) flag is set).

Major uses: None. See BVC above. Addressing Modes:

Number of Name Format Opcode Bytes Used

Relative BVS addr. $70/112 2

Affected flags: none of them.

CLC

What it does: Clears the carry flag. (Puts a 0 into it.) Major uses: Always used before any addition (ADC). If there are to be a series of additions (multiple-byte addition), only the first ADC is preceded by CLC since the carry feature is necessary. There might be a carry, and the result will be incorrect if it is not taken into account.

The 6502 does not offer an addition instruction without the carry feature. Thus, you must always clear it before the first ADC so a carry won't be accidentally added.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied CLC $18/24 1

Affected flags: Carry (C) flag is set to zero.

CLD

What it does: Clears the decimal mode flag. (Puts a 0 into it.)

Major uses: Commodore computers execute a CLD when first turned on as well as upon entry to monitor modes (PET/CBM models) and when the SYS command occurs. Apple and Atari, however, can arrive in an ML environment with the D flag in an indeterminant state. An attempt to execute

6502 Instruction Set

ML with this flag set would cause disaster—all mathematics would be performed in "decimal mode." It is therefore suggested that owners of Apple and Atari computers CLD during the early phase, the initialization phase, of their programs. Though this is an unlikely bug, it would be a difficult one to recognize should it occur.

For further detail about the 6502's decimal mode, see SED below.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied CLD $D8/216 1

Affected flags: Decimal (D) flag is set to zero.

CLI

What it does: Clears the interrupt-disable flag. All interrupts will therefore be serviced (including maskable ones).

Major uses: To restore normal interrupt routine processing following a temporary suspension of interrupts for the purpose of redirecting the interrupt vector. For more detail, see SEI below.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied CLI $58/88 1

Affected flags: Interrupt (I) flag is set to zero.

CLV

What it does: Clears the overflow flag. (Puts a 0 into it.) Major uses: None. (See BVC above.)

6502 Instruction Set

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied CLV $B8/184 1

Affected flags: Overflow (V) flag is set to zero.

CMP

What it does: Compares the byte in memory to the byte in the Accumulator. Three flags are affected, but the bytes in memory and in the Accumulator are undisturbed. A CMP is actually a subtraction of the byte in memory from the byte in the Accumulator. Therefore, if you LDA #15:CMP #15—the result (of the subtraction) will be zero, and BEQ would be triggered since the CMP would have set the Z flag.

Major uses: This is an important instruction in ML. It is central to IF-THEN and ON-GOTO type structures. In combination with the B branching instructions like BEQ, CMP allows the 6502 chip to make decisions, to take alternative pathways depending on comparisons. CMP throws the N, Z, or C flags up or down. Then a B instruction can branch, depending on the condition of a flag.

Often, an action will affect flags by itself, and a CMP will not be necessary. For example, LDA #15 will put a 0 into the N flag (seventh bit not set) and will put a 0 into the Z flag (the result was not 0). LDA does not affect the C flag. In any event, you could LDA #15: BPL TARGET, and the branch would take effect. However, if you LDA $20 and need to know if the byte loaded is precisely $0D, you must CMP #$0D:BEQ TARGET. So, while CMP is sometimes not absolutely necessary, it will never hurt to include it prior to branching.

Another important branch decision is based on > or < situations. In this case, you use BCC and BCS to test the C (carry) flag. And you've got to keep in mind the order of the numbers being compared. The memory byte is compared to the byte sitting in the Accumulator. The structure is: memory is less than or equal to the Accumulator (BCC is triggered because the carry flag was cleared). Or memory is more than — Accumulator (BCS is triggered because the carry flag was set). Here's an example. If you want to find out if the number in the Accumulator is less than $40, just CMP #$41:BCC

6502 Instruction Set

LESSTHAN (be sure to remember that the carry flag is cleared if a number is less than or equal; that's why we test for less than $40 by comparing with a $41):

LDA #75

CMP #$41; IS IT LESS THAN $40?

BCC LESSTHAN

One final comment about the useful BCC/BCS tests following CMP: It's easy to remember that BCC means less than or equal and BCS means more than if you notice that C is less than S in the alphabet.

The other flag affected by CMPs is the N flag. Its uses are limited since it merely reports the status of the seventh bit; BPL triggers if that bit is clear, BMI triggers if it's set. However, that seventh bit does show whether the number is greater than (or equal to) or less than 128, and you can apply this information to the ASCII code or to look for BASIC keywords or to search data bases (BPL and BMI are used by LADS' data base search routines in the Array subprogram). Nevertheless, since LDA and many other instructions affect the N flag, you can often directly BPL or BMI without any need to CMP first.

Addressing Modes:

CPX

What it does: Compares the byte in memory to the byte in the X Register. Three flags are affected, but the bytes in memory and in the X Register are undisturbed. A CPX is actually a subtraction of the byte in memory from the byte in

6502 Instruction Set

s

the X Register. Therefore, if you LDA #15:CPX #15—the result (of the subtraction) will be zero and BEQ would be triggered since the CPX would have set the Z flag.

Major uses: X is generally used as an index, a counter within loops. Though the Y Register is often preferred as an index since it can serve for the very useful Indirect Y addressing mode (LDA (15),Y)—the X Register is nevertheless pressed into service when more than one index is necessary or when Y is busy with other tasks.

In any case, the flags, conditions, and purposes of CPX are quite similar to CMP (the equivalent comparison instruction for the Accumulator). For further information on the various possible comparisons (greater than, equal, less than, not equal), see CMP above.

Addressing Modes:

CPY

What it does: Compares the byte in memory to the byte in the Y Register. Three flags are affected, but the bytes in memory and in the Y Register are undisturbed. A CPX is actually a subtraction of the byte in memory from the byte in the Y Register. Therefore, if you LDA #15: CPY #15—the result (of the subtraction) will be zero, and BEQ would be triggered since the CPY would have set the Z flag.

Major uses: Y is the most popular index, the most heavily used counter within loops since it can serve two purposes: It permits the very useful Indirect Y addressing mode (LDA (15),Y) and can simultaneously maintain a count of loop events.

See CMP above for a detailed discussion of the various branch comparisons which CPY can implement.

6502 Instruction Set

to reverse the current state of the sixth bit in a given byte: LDA BYTE:EOR #$40:STA BYTE. This will set bit 6 in BYTE if it was 0 (and clear it if it was 1). This selective bit toggling could be used to "shift" an unshifted ASCII character via EOR #$80 (1000000). Or if the character were shifted, EOR #$80 would make it lowercase. EOR toggles.

Addressing Modes:

INC

What it does: Increases the value of a byte in memory by 1.

Major uses: Used exactly as DEC (see DEC above), except it counts up instead of down. For raising address pointers or supplementing the X and Y Registers as loop indexes.

Addressing Modes:

Name

Zero Page Zero Page,X Absolute Absolute,X

Affected flags: N Z

Format

INC 15 INC 15,X INC 1500 INC 1500,X

Opcode

$E6/230 $F6/246 $EE/238 $FE/254

Number of Bytes Used

2 2 3

3

INX

What it does: Increases the X Register by 1.

6502 Instruction Set

■

the X Register. Therefore, if you LDA #15:CPX #15—the result (of the subtraction) will be zero and BEQ would be triggered since the CPX would have set the Z flag.

Major uses: X is generally used as an index, a counter within loops. Though the Y Register is often preferred as an index since it can serve for the very useful Indirect Y addressing mode (LDA (15),Y)—the X Register is nevertheless pressed into service when more than one index is necessary or when Y is busy with other tasks.

In any case, the flags, conditions, and purposes of CPX are quite similar to CMP (the equivalent comparison instruction for the Accumulator). For further information on the various possible comparisons (greater than, equal, less than, not equal), see CMP above.

Addressing Modes:

CPY

What it does: Compares the byte in memory to the byte in the Y Register. Three flags are affected, but the bytes in memory and in the Y Register are undisturbed. A CPX is actually a subtraction of the byte in memory from the byte in the Y Register. Therefore, if you LDA #15: CPY #15—the result (of the subtraction) will be zero, and BEQ would be triggered since the CPY would have set the Z flag.

Major uses: Y is the most popular index, the most heavily used counter within loops since it can serve two purposes: It permits the very useful Indirect Y addressing mode (LDA (15),Y) and can simultaneously maintain a count of loop events.

See CMP above for a detailed discussion of the various branch comparisons which CPY can implement.

6502 Instruction Set

INC

What it does: Increases the value of a byte in memory by 1.

Major uses: Used exactly as DEC (see DEC above), except it counts up instead of down. For raising address pointers or supplementing the X and Y Registers as loop indexes.

Addressing Modes:

Name

Zero Page Zero Page,X Absolute Absolute,X

Affected flags: N Z

Format

INC 15 INC 15,X INC 1500 INC 1500,X

Opcode

$E6/230 $F6/246 $EE/238 $FE/254

Number of Bytes Used

2 2 3 3

INX

What it does: Increases the X Register by 1.

Major uses: Used exactly as DEX (see DEX above), except it counts up instead of down. For loop indexing.

Addressing Modes:

INY

What it does: Increases the Y Register by 1.

Major uses: Used exactly as DEY (see DEY above), except it counts up instead of down. For loop indexing and working with the Indirect Y addressing mode (LDA (15),Y).

Addressing Modes:

JMP

What it does: Jumps to any location in memory.

Major uses: Branching long range. It is the equivalent of BASIC'S GOTO instruction. The bytes in the Program Counter are replaced with the address (the argument) following the JMP instruction and, therefore, program execution continues from this new address.

Indirect jumping—JMP (1500)—is not recommended, although some programmers find it useful. It allows you to set up a table of jump targets and bounce off them indirectly. For example, if you had placed the numbers $00 $04 in addresses $88 and $89, a JMP ($0088) instruction would send the program to whatever ML routine was located in address $0400. Unfortunately, if you should locate one of your pointers on the edge of a page (for example, $00FF or $17FF), this Indirect JMP addressing mode reveals its great weakness. There is a bug which causes the jump to travel to the wrong place—JMP

255

6502 Instruction Set

($00FF) picks up the first byte of the pointer from $00FF, but the second byte of the pointer will be incorrectly taken from $0000. With JMP ($17FF), the second byte of the pointer would come from what's in address $1700.

Since there is this bug, and since there are no compelling reasons to set up JMP tables, you might want to forget you ever heard of Indirect jumping.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Absolute JMP 1500 $4C/76 3

Indirect JMP (1500) $6C/108 3

Affected flags: none of them.

JSR

What it does: Jumps to a subroutine anywhere in memory. Saves the PC (Program Counter) address, plus three, of the JSR instruction by pushing it onto the stack. The next RTS in the program will then pull that address off the stack and return to the instruction following the JSR.

Major uses: As the direct equivalent of BASIC'S GOSUB command, JSR is heavily used in ML programming to send control to a subroutine and then (via RTS) to return and pick up where you left off. The larger and more sophisticated a program becomes, the more often JSR will be invoked. In LADS, whenever something is printed to screen or printer, you'll often see a chain of JSRs performing necessary tasks: JSR PRNTCR: JSR PRNTSA:JSR PRNTSPACEJSR PRNTNUMJSR PRNTSPACE. This JSR chain prints a carriage return, the current assembly address, a space, a number, and another space.

Another thing you might notice in LADS and other ML programs is a PLA:PLA pair. Since JSR stuffs the correct return address onto the stack before leaving for a subroutine, you need to do something about that return address if you later decide not to RTS back to the position of the JSR in the program. This might be the case if you usually want to RTS, but in some particular cases, you don't. For those cases, you can take control of program flow by removing the return address

6502 Instruction Set

from the stack (PLA:PLA will clean off the two-byte address) and then performing a direct JMP to wherever you want to go.

If you JMP out of a subroutine without PLA:PLA, you could easily overflow the stack and crash the program.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Absolute JSR 1500 $20/32 3

Affected flags: none of them.

LDA

What it does: Loads the Accumulator with a byte from memory. Copy might be a better word than load, since the byte in memory is unaffected by the transfer.

Major uses: The busiest place in the computer. Bytes coming in from disk, tape, or keyboard all flow through the Accumulator, as do bytes on their way to screen or peripherals. Also, because the Accumulator differs in some important ways from the X and Y Registers, the Accumulator is used by ML programmers in a different way from the other registers.

Since INY/DEY and INX/DEX make those registers useful as counters for loops (the Accumulator couldn't be conveniently employed as an index; there is no INA instruction), the Accumulator is the main temporary storage register for bytes during their manipulation in an ML program. ML programming, in fact, can be defined as essentially the rapid, organized maneuvering of single bytes in memory. And it is the Accumulator where these bytes often briefly rest before being sent elsewhere.

Addressing Modes:

LDX

What it does: Loads the X Register with a byte from memory.

Major uses: The X Register can perform many of the tasks that the Accumulator performs, but it is generally used as an index for loops. In preparation for its role as an index, LDX puts a value into the register.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Immediate LDX #15 $A2/162 2

Zero Page LDX 15 $A6/166 2

Zero Page,Y LDX 15,Y $B6/182 2

Absolute LDX 1500 $AE/174 3

Absolute,Y LDX 1500,Y $BE/190 3

Affected flags: N Z

LDY

What it does: Loads the Y Register with a byte from memory.

Major uses: The Y Register can perform many of the tasks that the Accumulator performs, but it is generally used as an index for loops. In preparation for its role as an index, LDY puts a value into the register.

258

6502 Instruction Set

Addressing Modes:

Name

Immediate Zero Page Zero Page,X Absolute Absolute,X

Affected flags: N Z

Format

LDY #15 LDY 15 LDY 15,X LDY 1500 LDY 1500,X

Opcode

$A0/160

$A4/164 $B4/180 $AC/172 $BC/188

Number of Bytes Used

2 2 2 3 3

LSR

What it does: Shifts the bits in the Accumulator or in a byte in memory to the right, by one bit. A zero is stuffed into bit 7, and bit 0 is put into the carry flag.

Major uses: To divide a byte by 2. In combination with the ROR instruction, LSR can divide a two-byte or larger number (see Appendix D).

LSR:LSR:LSR:LSR will put the high four bits (the high nybble) into the low nybble (with the high nybble replaced by the zeros being stuffed into the seventh bit and then shifted to the right).

Addressing Modes:

Name Format

Opcode

Number of Bytes Used

2 2 2 3 3

Affected flags: N Z C

6502 Instruction Set

NOP _

What it does: Nothing. No operation.

Major uses: Debugging. When setting breakpoints with BRK, you will often discover that a breakpoint, when examined, passes the test. That is, there is nothing wrong at that place in the program. So, to allow the program to execute to the next breakpoint, you cover the BRK with a NOP. Then, —

when you run the program, the computer will slide over the NOP with no effect on the program. Three NOPs could cover a JSR XXXX, and you could see the effect on the program when that particular JSR is eliminated.

Addressing Modes:

ORA

What it does: Logically ORs a byte in memory with the byte in the Accumulator. The result is in the Accumulator. An OR results in a 1 if either the bit in memory or the bit in the Accumulator is 1.

Major uses: Like an AND mask which turns bits off, ORA masks can be used to turn bits on. For example, if you wanted to "shift" an ASCII character by setting the seventh bit, you could LDA CHARACTER:ORA #$80. The number $80 in binary is 10000000, so all the bits in CHARACTER which are ORed with zeros here will be left unchanged. (If a bit in —

CHARACTER is a 1, it stays a 1. If it is a zero, it stays 0.) But the 1 in the seventh bit of $80 will cause a 0 in the CHARACTER to turn into a 1. (If CHARACTER already has a 1 in its seventh bit, it will remain a 1.)

6502 Instruction Set

Addressing Modes:

PHA

What it does: Pushes the Accumulator onto the stack.

Major uses: To temporarily (very temporarily) save the byte in the Accumulator. If you are within a particular subroutine and you need to save a value for a brief time, you can PHA it. But beware that you must PLA it back into the Accumulator before any RTS so that it won't misdirect the computer to the wrong RTS address. All RTS addresses are saved on the stack. Probably a safer way to temporarily save a value (a number) would be to STA TEMP or put it in some other temporary variable that you've set aside to hold things. Also, the values of A, X, and Y need to be temporarily saved, and the programmer will combine TYA and TXA with several PHAs to stuff all three registers onto the stack. But, again, matching PLAs must restore the stack as soon as possible and certainly prior to any RTS.

Addressing Modes:

6502 Instruction Set

PHP

What it does: Pushes the "processor status" onto the top of the stack. This byte is the Status Register, the byte which holds all the flags: N Z C I D V.

Major uses: To temporarily (very temporarily) save the state of the flags. If you need to preserve the all current conditions for a minute (see description of PHA above), you may also want to preserve the Status Register as well. You must, however, restore the Status Register byte and clean up the stack by using a PLP before the next RTS.

Addressing Modes:

PLA

What it does: Pulls the top byte off the stack and puts it into the Accumulator.

Major uses: To restore a number which was temporarily stored on top of the stack (with the PHA instruction). It is the opposite action of PHA (see above). Note that PLA does affect the N and Z flags. Each PHA must be matched by a corresponding PLA if the stack is to correctly maintain RTS addresses, which is the main purpose of the stack.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied PLA $68/104 1

Affected flags: N Z

PLP

What it does: Pulls the top byte off the stack and puts it into the Status Register (where the flags are). PLP is a mnemonic for PuLl Processor status.

6502 Instruction Set

Major uses: To restore the condition of the flags after the Status Register has been temporarily stored on top of the stack (with the PHP instruction). It is the opposite action of PHP (see above). PLP, of course, affects all the flags. Any PHP must be matched by a corresponding PLP if the stack is to correctly maintain RTS addresses, which is the main purpose of the stack.

Addressing Modes:

ROL

What it does: Rotates the bits in the Accumulator or in a byte in memory to the left, by one bit. A rotate left (as opposed to an ASL, Arithmetic Shift Left) moves bit 7 to the carry, moves the carry into bit 0, and every other bit moves one position to its left. (ASL operates quite similarly, except it always puts a 0 into bit 0.)

/iYiW/ryjW/n

Bit Bit Bit Bit Bit Bit Bit Bit 7 6 5 4 3 2 10

Major uses: To multiply a byte by 2. ROL can be used with ASL to multiply multiple-byte numbers since ROL pulls any carry into bit 0. If an ASL resulted in a carry, it would be thus taken into account in the next higher byte in a multiple-byte number. (See Appendix D.)

Notice how the act of moving columns of binary numbers to the left has the effect of multiplying by 2:

0010 (the number 2 in binary)

0100 (the number 4)

6502 Instruction Set

This same effect can be observed with decimal numbers, except the columns represent powers of 10:

0010 (the number 10 in decimal)

0100 (the number 100)

Addressing Modes:

ROR

What it does: Rotates the bits in the Accumulator or in a byte in memory to the right, by one bit. A rotate right (as opposed to a LSR, Logical Shift Right) moves bit 0 into the carry, moves the carry into bit 7, and every other bit moves one position to its right. (LSR operates quite similarly, except it always puts a 0 into bit 7.)

_

' TV?*'T* *?*'?»^-^

Bit Bit Bit Bit Bit Bit Bit Bit 7 6 5 4 3 2 10

Major uses: To divide a byte by 2. ROR can be used with LSR to divide multiple-byte numbers since ROR puts any carry into bit 7. If an LSR resulted in a carry, it would be thus taken into account in the next lower byte in a multiple-byte number. (See Appendix D.)

Notice how the act of moving columns of binary numbers to the right has the effect of dividing by 2:

1000 (the number 8 in binary)

0100 (the number 4)

6502 Instruction Set

This same effect can be observed with decimal numbers, except the columns represent powers of 10:

1000 (the number 1000 in decimal)

0100 (the number 100)

Addressing Modes:

Name

Accumulator Zero Page Zero Page,X Absolute Absolute,X

Format

ROR

ROR 15 ROR 15,X ROR 1500 ROR 1500,X

Opcode

$6A/106 $66/102 $76/118 $6E/110 $7E/126

Affected flags: N Z C

Number of Bytes Used

1 2 2

3 3

RTI

What it does: Returns from an interrupt.

Major uses: None. You might want to add your own routines to your machine's normal interrupt routines (see SEI below), but you won't be generating actual interrupts of your own. Consequently, you cannot ReTurn from Interrupts you never create.

Addressing Modes:

Number of Bytes Used

1

Name Format Opcode

Implied RTI $40/64

Affected flags: all of them (Status Register is retrieved from the stack).

RTS

What it does: Returns from a subroutine jump (caused by JSR).

Major uses: Automatically picks off the two top bytes on the stack and places them into the Program Counter. This reverses the actions taken by JSR (which put the Program Counter bytes onto the stack just before leaving for a subroutine). When RTS puts the return bytes into the Program

6502 Instruction Set

Counter, the next event in the computer's world will be the instruction following the ]SR which stuffed the return address onto the stack in the first place.

Addressing Modes:

SBC

What it does: Subtracts a byte in memory from the byte in the Accumulator, and "borrows" if necessary. If a "borrow" takes place, the carry flag is cleared (set to 0). Thus, you always SEC (set the carry flag) before an SBC operation so you can tell if you need a "borrow." In other words, when an SBC operation clears the carry flag, it means that the byte in memory was larger than the byte in the Accumulator. And since memory is subtracted from the Accumulator in an SBC operation, if memory is the larger number, we must "borrow."

Major uses: Subtracts one number from another.

Addressing Modes:

SEC

What it does: Sets the carry (C) flag (in the processor Status Register byte).

"

6502 Instruction Set

Major uses: This instruction is always used before any SBC operation to show if the result of the subtraction was negative (if the Accumulator contained a smaller number than the byte in memory being subtracted from it). See SBC above.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied SEC $38/56 1

Affected flags: C

SED

What it does: Sets the decimal (D) flag (in the processor Status Register byte).

Major uses: Setting this flag puts the 6502 into decimal arithmetic mode. This mode can be easier to use when you are inputting or outputting decimal numbers (from the user of a program or to the screen). Simple addition and subtraction can be performed in decimal mode, but most programmers ignore this feature since more complicated math requires that you remain in the normal binary state of the 6502.

Note: Commodore computers automatically clear this mode when entering ML via SYS. However, Apple and Atari computers can enter ML in an indeterminant state. Since there is a possibility that the D flag might be set (causing havoc) on entry to an ML routine, it is sometimes suggested that owners of these two computers use the CLD instruction at the start of any ML program they write. Any ML programmer must CLD following any deliberate use of the decimal mode.

Addressing Modes:

6502 Instruction Set

SEI

What it does: Sets the interrupt disable flag (the I flag) in the processor status byte. When this flag is up, the 6502 will not acknowledge or act upon interrupt attempts (except a few nonmaskable interrupts which can take control in spite of this flag, like a reset of the entire computer). The operating systems of most computers will regularly interrupt the activities of the chip for necessary, high-priority tasks such as updating an internal clock, displaying things on the TV, receiving signals from the keyboard, etc. These interruptions of whatever the chip is doing normally occur 60 times every second. To find out what housekeeping routines your computer interrupts the chip to accomplish, look at the pointer in $FFFE/FFFF. It gives the starting address of the maskable interrupt routines.

Major uses: You can alter a RAM pointer so that it sends these interrupts to your own ML routine, and your routine then would conclude by pointing to the normal interrupt routines. In this way, you can add something you want (a click sound for each keystroke? the time of day on the screen?) to the normal actions of your operating system. The advantage of this method over normal SYSing is that your interrupt-driven routine is essentially transparent to whatever else you are doing (in whatever language). Your customization appears to have become part of the computer's ordinary habits.

However, if you try to alter the RAM pointer while the other interrupts are active, you will point away from the normal housekeeping routines in ROM, crashing the computer. This is where SEI comes in. You disable the interrupts while you LDA STA LDA STA the new pointer. Then CLI turns the interrupt back on and nothing is disturbed.

Interrupt processing is a whole subcategory of ML programming and has been widely discussed in magazine articles. Look there if you need more detail.

Addressing Modes:

6502 Instruction Set

STA

What it does: Stores the byte in the Accumulator into memory.

Major uses: Can serve many purposes and is among the most used instructions. Many other instructions leave their results in the Accumulator (ADC/SBC and logical operations like ORA), after which they are stored in memory with STA.

Addressing Modes:

STX

What it does: Stores the byte in the X Register into memory.

Major uses: Copies the byte in X into a byte in memory.

Addressing Modes:

STY

What it does: Stores the byte in the Y Register into memory.

Major uses: Copies the byte in Y into a byte in memory.

6502 Instruction Set

Addressing Modes:

TAX

What it does: Transfers the byte in the Accumulator to the X Register.

Major uses: Sometimes you can copy the byte in the Accumulator into the X Register as a way of briefly storing the byte until it's needed again by the Accumulator. If X is currently unused, TAX is a convenient alternative to PHA (another temporary storage method).

However, since X is often employed as a loop counter, TAX is a relatively rarely used instruction.

Addressing Modes:

TAY

What it does: Transfers the byte in the Accumulator to the Y Register.

Major uses: Sometimes you can copy the byte in the Accumulator into the Y Register as a way of briefly storing the byte until it's needed again by the Accumulator. If Y is currently unused, TAY is a convenient alternative to PHA (another temporary storage method).

However, since Y is quite often employed as a loop counter, TAY is a relatively rarely used instruction.

6502 Instruction Set

Addressing Modes:

TSX

What it does: Transfers the Stack Pointer to the X Register.

Major uses: The Stack Pointer is a byte in the 6502 chip which points to where a new value (number) can be added to the stack. The Stack Pointer would be "raised" by two, for example, when you JSR and the two bytes of the Program Counter are pushed onto the stack. The next available space on the stack thus becomes two higher than it was previously. By contrast, an RTS will pull a two-byte return address off the stack, freeing up some space, and the Stack Pointer would then be "lowered" by two.

The Stack Pointer is always added to $0100 since the stack is located between addresses $0100 and $01FF.

Addressing Modes:

Number of Name Format Opcode Bytes Used

Implied TSX $BA/186 1

Affected flags: N Z

TXA

What it does: Transfers the byte in the X Register to the Accumulator.

Major uses: There are times, after X has been used as a counter, when you'll want to compute something using the value of the counter. And you'll therefore need to transfer the byte in X to the Accumulator. For example, if you search the screen for character $75:

6502 Instruction Set

CHARACTER = $75:SCREEN =

$0400

LDX#0

LOOP LDA SCREEN,X:CMP

#CHARACTER:BEQ MORE:INX

BEQ NOTFOUND ; (this prevents an endless loop

MORE TXA ; (you now know the charac-

NOTFOUND BRK

ter's location)

In this example, we want to perform some action based on the location of the character. Perhaps we want to remember the location in a variable for later reference. This will require that we transfer the value of X to the Accumulator so it can be added to the SCREEN start address.

Addressing Modes:

TXS

What it does: Transfers the byte in X Register into the Stack Pointer.

Major uses: Alters where, in the stack, the current "here's storage space" is pointed to. There are no common uses for this instruction.

Addressing Modes:

6502 Instruction Set

TYA

What it does: Transfers the byte in the Y Register to the Accumulator.

Major uses: See TXA. Addressing Modes:

Chapter 11

Modifying LADS:

Adding Error Traps, RAM-Based Assembly, and a Disassembler

1 1

"

Modifying LADS:

Adding Error Traps, RAM'Based Assembly, and a Disassembler

Special Notes on the Construction of Atari and Apple LADS

Imagine how nice it would be if you could add any additional commands to BASIC that you desired. You wouldn't just temporarily wedge the new commands into a frozen ROM BASIC. Instead, you would simply define the new commands, and they would then become a permanent part of your programming language.

This freedom to change a language is called extensibility. It's one of the best features of Forth and a few other languages. Extensibility opens up a language. It gives the programmer easy access to all aspects of his programming tool. LADS, too, is extensible since the internals of the assembler are thoroughly mapped, documented, and explained in this book. You can customize it at will, building in any features that you would find useful.

After exploring the details of the LADS assembler and using LADS to write your own machine language, you may have thought of some features or pseudo-ops that you would like to add. In this chapter, we'll show how to make several different kinds of modifications. These examples, even if they're not features of use to you, will demonstrate how to extend and customize the language. We'll add some new error traps, create a disassembler, and make a fundamental change to the Commodore and Apple LADS—the capability of assembling directly from RAM. (The Atari version has this feature built-in already.)

At the end of this chapter we'll cover the details of the Atari and Apple LADS source code where they differ from the

Modifying LADS: Special Notes on Atari and Apple LADS

general LADS source listings (printed at the end of each chapter). The three versions—Commodore, Atari, and Apple—are functionally identical, so the descriptions throughout the book apply to each version. However, a few adjustments had to be made: input/output variations, a special source code editor for the Atari, etc. All these will be discussed below. But first, let's see some examples of how to customize LADS.

A Naked Mnemonic Error Trap

The original version of LADS notifies you of most serious errors: branch out of range, duplicated or undefined labels, naked labels (labels without arguments), invalid pseudo-ops, no starting address, file not found on disk, and various syntax errors. Other kinds of errors are forgiven by LADS since it can interpret what you meant to type in your source code. For example, LADS can interpret what you meant when you type errors like these:

100 INY #77; (adding an argument to a one-byte opcode) 100 INY : LDA #15:INY:INX;(extra spaces before or after

colons)

The source code in these examples will be correctly assembled. Also, if you forget to leave a space between a mnemonic and its argument (like: LDA#15), that sort of error will be trapped and announced.

But the original LADS didn't have a built-in trap for naked mnemonics. If you wrote:

100 INC:INY:LDA #15 ; (that "INC" requires an argument)

the assembler would have crashed. No error message, no warning, just a crash.

Programmers who tested the early versions of LADS asked that this error be trapped. That is, if this mistake was made during the typing of an ML program's source code, it shouldn't cause the assembler to go insane. The following two error-trap modifications have been made a permanent part of LADS (and are already in the object code version you typed in from this book or received on disk).

To expose naked mnemonic errors, a special trap was inserted into the Eval subprogram (see Listing 11.1)

Modifying LADS: Special Notes on Atari and Apple LADS

After Eval has determined (line 930 of Program 3-1) that the mnemonic under evaluation does require an argument (it's not like INY, which uses Implied addressing and never has an argument), Eval then goes down to check to see if the argument is a label or a number (1460).

Here's where we can check to see if the programmer forgot to give an argument. If the mnemonic is followed by a colon or a 0 (end of logical line), that's a sure signal that the argument has been left out. We can load in the character just after the mnemonic (see line 1474, Listing 11.1). If there is a space character (#32), all is well and we can continue (1480) with our assembly. If not, we jump to L700, the error-reporting routine which will print the error and ring the bell.

A Trap for Impossible Instructions

Another programmer who tested LADS was just starting to learn machine language. Unfamiliar with some of the mnemonics and addressing modes, he once tried to assemble a line like this:

100 LDA 15,Y

not knowing that Zero Page,Y addressing is a rare addressing mode, exclusively reserved for only two mnemonics: LDX and STX. But LADS didn't crash on this. Instead, it assembled an LDA 15,X (the correct addressing mode, but fatal to his particular program since he was trying to use the Y Register as an index).

The trap was inserted into LADS (Listing 11.2) to make a harmless substitution, to assemble an Absolute,Y (at a zero page address). Thus, the programmer's intent is preserved, but the illegal addressing mode is replaced.

By the time Eval reaches this point, it has already filtered out many other possible addressing modes. Eval knows that the addressing mode is some form of ,X or ,Y and that it's Zero Page. Eval first checks to see if we are dealing with an attempted ,Y addressing mode (CMP #89, the Y character). If not, we continue with the assembly (5271) by a BNE to line 5274.

Modifying LADS: Special Notes on Atari and Apple LADS

But if it is a ,Y, we check the opcode to see if it is LDX, the only correct opcode for this addressing mode. If so, we continue.

However, if it is some other mnemonic like LDA or STY, this ,Y addressing mode is illegal and we make the adjustment to Absolute,Y by a JMP to the area of Eval where that addressing mode is accomplished.

Most illegal addressing will be reported by LADS. Nevertheless, if there's a peculiar error that you often make when programming and LADS doesn't alert you, just add an error-reporting trap or have the assembler automatically correct the problem.

A final minor modification to the PDISK routine in the Pseudo subprogram will permit embedded keywords in filenames when using the .D pseudo-op to save object code to disk. (The Atari version will not need this modification.) As printed in this book, LADS will correctly extend and print a filename following the .D pseudo-op which contains a keyword. For example, .D OLDSTOP will look correct onscreen. However, LADS will send the tokenized keyword to the disk as the filename. This will result in unpredictable filenames when you use BASIC commands as part of a filename. To correct this, remove line 1190 of Program 8-1 and adjust the following lines in the Pseudo subprogram. Then reassemble a new version of LADS:

1230 PD1 LDY #0

1231 PDLO LDA LABEL,Y:BEQ PDEN:STA FILEN,Y:INY:JMP PDLO; MOVE NAME

1239 PDEN LDA #44; PUT ,P,W (PROGRAM, WRITE) SIGNAL S ONTO FILENAME

Modifying LADS: Special Notes on Atari and Apple LADS

Modifying LADS: Special Notes on Atari and Apple LADS

A Remarkably Simple, Yet Radical, Change

Since LADS uses symbols instead of numbers, it's fairly easy to change, to make it what you want it to be. What's more, all the programs you write with LADS will also be symbolic and easily changed. Let's make a radical change to LADS and see how easy it is to profoundly alter the nature of the assembler.

As designed, LADS reads source code off a disk program file. Let's make it read its source code from within the computer's RAM memory, instead of from disk. This makes two things possible: 1. You can change source code, then test it by a simple SYS to LADS. 2. Tape drive users can use LADS. This version of LADS isn't functionally different from the normal version since long, linked assembly will still be coming from disk files. However, it can be a more convenient way to write and debug smaller ML programs or subroutines. Everything works the same when you assemble, except that the first (or only) source code program resides in RAM instead of on disk. Commodore and Atari RAM-LADS versions can use linked files, but the Apple RAM-based version cannot link files as it can in the normal Apple LADS.

You make a radical change whenever you change *= 864 to *= 5000. You are making a small change at the beginning, the root, of your source code. After making this change, the entire program is assembled at address 5000 instead of address 864. The effect—in the usual sense of the term—is quite radical. The effort on your part, however, is rather minor. Likewise, we can drastically alter the way that LADS works by making a few minor changes to the symbols in LADS.

Our goal is to make LADS read source code from memory instead of from disk files. First, we need to add two new pointers to the LADS zero page equates (in the Defs file). We create PMEM. It will serve as a dynamic pointer. It will always keep track of our current position in memory as we assemble source code.

The intelligence in the disk drive keeps track of where we are in a file; whenever we call CHARIN, it increments a pointer so that the next CHARIN call will pull a new byte into A, the Accumulator. But we're going to be reading from memory so we'll need to update our own dynamic pointer. To create this pointer, just type in a new line in Defs: PMEM = $xx (whatever zero page, two-byte space is safe in your computer).

Modifying LADS: Special Notes on Atari and Apple LADS

The other new pointer we need to define in zero page will tell LADS where your BASIC RAM memory starts, where a program in BASIC starts. To create this register, just look at a map of the zero page of your particular computer and define: RAMSTART = $xx (whatever it is).

Note: These definitions have already been added to the Commodore versions of the Defs subprogram in this book. If you are creating a RAM-based version of LADS for the Apple, add the following two lines to the Apple Defs file:

135 RAMSTART = $67; POINTER TO START OF RAM

MEMORY 157 PMEM = $E2

The Apple version of the RAM-based LADS requires the same changes to the Eval subprogram as Commodore machines require. However, no changes are needed in the Pseudo or Openl subprograms. The one difference between Commodore and Apple versions in the Getsa subprogram is that Apple requires #$2A in line 300 instead of the #172.

A New CHARIN

Next, we need to change the CHARIN subroutine itself. As LADS normally runs, it goes to BASIC'S get-a-byte subroutine whenever CHARIN is invoked. This won't work for memory-based source code. BASIC RAM cannot, alas, be OPENed as if it were a file. So, since LADS is peppered with references to CHARIN, we can just undefine CHARIN in the Defs subprogram by putting a semicolon in front of it (Listing 11.3).

Similarly, CHKIN is scattered throughout LADS to reopen file #1, the read-code-from-disk file. We're not using file #1 in this version of LADS, so we add a semicolon to its definition too (Listing 11.4).

But throughout LADS there are references to these two subroutines. We need to write a new CHARIN and CHKIN to replace the ones we just obliterated. LADS will then have somewhere to go, something to do, as it comes upon CHARINs or CHKINs throughout the code. We do this by adding to the Getsa subprogram (Listing 11.5).

Modifying LADS: Special Notes on Atari and Apple LADS

m as

co H

M Eh

a to

a

w z

o

z

H CO

D

w

Cm

CO

3 W

o

u

fa fa

cm u

,-1 ^

3

CN

IT)

DjD

to

H Q

a;

X

o

W S

o X

Ph

w

Q

o u fa u a P o co

w

OQ

£ W CO

to <

es

o s

H

2

— O

« os

CO fa

<

X = CJ

2 1-1 CO

os W

< Eh

W H

2~

z a

H W

os os <

X

u

J 3j

a

o

M

Eh

2

w co

2 -

o co

U Eh

OS

co ••

w o<

j ••

Pu >h W

OS >H

Q

•« J

.H ••

+ 04

S B

W O.

S ••

&, >-U^

2 2

M H

04

w z

M Eh

O OS

'«

CO

m =»=

2 >h

fa Q

a, «

<

q a

•• CO S> fa

>H CO

Eh Eh

co os

M ^H 2

os n m

< u «

X 2 X

U M CJ

5j tsssscasss • >- ■H/invor^cocriQ^H t-J t T )nnmnn^>H/

Modifying LADS: Special Notes on Atari and Apple LADS

Line 410 is just an RTS. It's a placebo. We never want to reopen file #1 (CHKIN's normal job), so whenever LADS tries to do that, we JSR/RTS and nothing happens. Something does have to happen with CHARIN, however. CHARIN's job is to fetch the next byte in the source code and give it to the Accumulator. So this new version of CHARIN (390-400) increments PMEM, our new RAM memory pointer, saves Y, loads the byte, saves the Status Register, restores Y, restores the Status Register, and returns. This effectively imitates the actions of the normal disk CHARIN, except it draws upon RAM for source code.

Here you can see one of those rare uses for PHP and PLP. There are times when it's not enough to save the A, Y, and X Registers. This is one of those times. INDISK returns to Eval only when it finds a colon (end of source instruction), a semicolon (end of instruction, start of comment), or a zero (end of BASIC program line, hence end of source instruction). When we get a zero when we LDA, the zero flag will be set. But the LDY instruction will reset the zero flag. So, to preserve the effect of LDA on the zero flag, we PHP to store the flags on the stack. Then, after the LDY, we restore the status of the flags, using PLP before we return to the Indisk file. This way, whatever effect the LDA had on the flags will be intact. Indisk can thus expect to find the zero flag properly set if a particular LDA is pulling in the final 0 which signifies the end of a line in the BASIC RAM source code.

After making these substitutions to LADS, we need to remove the two references to Openl (the routine which opens a disk file for source code reading) in the Eval subprogram. These references are at lines 350 and 4350. We can simply remove them from assembly by putting a semicolon in front of them (Listing 11.6).

Early in Eval, we have a JSR GETSA. This is the GET-Start-Address-from-disk routine. We want to change this to: JSR MEMSA. GETSA isn't needed. MEMSA will perform the same job, but for memory-based source code instead of disk-based source code. MEMSA is found in the Getsa subprogram (Listing 11.7).

The first thing that MEMSA does is to put the start-of-BASIC-RAM pointer into PMEM (our dynamic pointer). This positions us to the first byte in the source code. Then it pulls

Modifying LADS: Special Notes on Atari and Apple LADS

off enough bytes to point to the * in the start address definition in the source code. This is just what Getsa does for a disk file. The rest of MEMSA is identical to Getsa.

Second Generation LADS

That's it. These few substitutions and LADS will read a source file from RAM memory. You can still use .D NAME to create a disk object code file. You can still send the object code disassembly to a printer with .P. All the other pseudo-ops still work fine. A radical change in ten minutes.

The Getsa subprogram contains a complete, step-by-step description of this disk-to-RAM modification of LADS. After you've made the changes to the source code (and saved them to disk), just load in the normal disk version of LADS, enter Defs as the starting file for assembly, and SYS to LADS. It will grind out a brand new, RAM-based assembler for you.

As always, when making a new version of your LADS assembler, be sure to direct object code to the disk (use the .D pseudo-op) so that you won't overwrite the working LADS in the computer. Also be sure you've given the new version a filename that doesn't already exist on the disk.

Modifying LADS: Special Notes on Atari and Apple LADS

Modifying LADS: Special Notes on Atari and Apple LADS

A Disassembler

In a perfectly symmetrical universe, with a right hand for every left, and a north pole for every south, you could transform an assembler into a disassembler by just making it run backwards.

Unfortunately, ours is not such a universe. Since LADS turns source code into object code, it would seem possible to tinker with it and adjust it a bit and make it turn object code back into source code, to disassemble. Not so. We have to link two new files onto LADS to add a disassembler function: Dis and Dtables.

Personal Programming Style

Dis is an example of how a fairly complex ML program can be constructed using LADS. The relatively few comments reflect my personal style of programming. I find many of the variable names are meaningful enough to make the code understandable, especially since the purpose of the lookup tables in Dtables is fairly easy to see.

The relatively few comments in the compressed code in Dis also allow you to look at more instructions at the same time on the screen. This can help during debugging since you might be able to more quickly locate a fault in the overall logic of a program. Nevertheless, many programmers find such dense code hard to read, hard to debug, and generally inefficient.

Obviously, you should write the kind of source code that works for you. The degree of compression is a matter of programming style and personal preference. Some programming teachers insist on heavy commenting and airy, decompressed coding. Perhaps this emphasis is appropriate for students who are just starting out with computing for the same reasons that penmanship is stressed when students are just starting to learn how to write. But you needn't feel that there is only one programming style. There are many paths, many styles.

How to Use the Disassembler

For convenience, Dis is set to start at 17000. That's an easy number to remember when you want to SYS, CALL, or USR to see a disassembly. The version at the end of this chapter is fully functional, but you might want to make modifications. As

Modifying LADS: Special Notes on Atari and Apple LADS

printed, it will ask for the start address location in RAM of the object code you want to see listed. Notice that the object code must be residing in RAM to be disassembled. (It would be simple, though, to make a disassembler which operated on disk or tape code.) Then it will disassemble until you hit the STOP or BREAK key. You might want to adjust it— you could have it assemble 20 instructions and then halt until a key was pressed. Or you might want to make it print disassemblies to the printer. Or it could ask for both starting and ending addresses before it begins. To have the disassembler you prefer, just modify the code.

The disassembler is included in this book because it demonstrates compressed LADS source code and it also shows how LADS itself can be expanded while borrowing from existing LADS subroutines like STOPKEY and PRNTNUM.

The source code in other parts of the book is somewhat artificial: Each line contains only one mnemonic followed by a description, a comment about the purpose of that line. Normally, such extensive commentary will not be necessary, and many lines can contain multiple statements separated by colons. Dis is an example of LADS source code as many programmers will probably write it.

To add the disassembler to LADS, change the .END DEFS at the end of the Tables subprogram in LADS to .FILE DIS. This will cause the file for Dis to be assembled along with LADS. Dis will link to Dtables, which ends with .END DEFS to permit the second pass through the combined LADS/Dis code.

Keyboard Input

Let's briefly outline the structure and functions of the disassembler. It starts off by printing its prompt message called DISMESS (30). The actual message is located in line 710. PRNTMESS is a subroutine within LADS which prints any message pointed to by the variable TEMP.

Then $3F, the ? symbol, is printed and STARTDIS (50) sets the hexflag up so that number printouts will be in hexadecimal. If you prefer decimal, LDA #0 and store it in HXFLAG.

Now there's an input loop to let the user input a decimal start address, character by character. If a carriage return is detected (90), we leave the loop to process the number. The

Modifying LADS: Special Notes on Atari and Apple LADS

number's characters are stored in the LABEL buffer and are also printed to the screen as they are entered (100).

When we finish getting the input, the LADS Valdec routine changes the ASCII numbers into a two-byte integer in the variable RESULT. We pick up the two-byte number and store it in the variable SA which will be printed to the screen as the address of each disassembled mnemonic.

Line 150 is a bit obscure. It wasn't originally written this way, but testing revealed that the JSR GB in line 190 would increment the start address right off the bat (before anything was disassembled or printed). At the same time, putting that increment lower in the main loop was inconvenient. So the easiest thing was to simply accept a start address from the user, then decrement it. The disassembler will start off with a start address that is one lower than the user intends, but that early increment will fix things up. Thus, the variable PMEM will hold a number which is one lower than the variable SA. Both these variables are keeping track of where in memory we are currently disassembling. But we've got to distinguish in this way between SA which prints to the screen and PMEM which tells the computer the current location.

Battling Insects

This is a good place to observe that programming is never a smooth trip from the original concept to the final product. No programmer is so well-prepared or knowledgeable that he or she simply sits down and calmly creates a workable program. If you find yourself scratching your head, circling around a bug and not trapping it, spending hours or days trying to see what could possibly be wrong—you're in good company. I've worked with some very experienced, very talented people and have yet to see someone fashion a program without snags. And the more significant and sophisticated the program, the more snags it has.

All that can be done, when you hit a snag, is to single-step through the offending area of your program, or set BRK traps, or puzzle over the source code, or try making some tentative reassemblies (not knowing for sure if your changes will have any salutary effect), or sometimes even toss out an entire subroutine and start over. For example, I wrote the rough draft, the first draft of this disassembler, in about two hours. I didn't have the final version working until I'd spent two full

Modifying LADS: Special Notes on Atari and Apple LADS

days battling bugs. Some were easy to fix, some were monsters. It took about ten minutes to cure that problem with the start address being one too high. But it took hours to locate an error in the disassembler tables, Dtables.

After the user has input the start address, TEMP is made to point to the LABEL buffer and VALDEC is invoked. VALDEC leaves the result of an ASCII-to-integer conversion in the RESULT variable. That number is stored in PMEM and SA (140-150). One final adjustment restores SA to the original number input by the user. SA will only print addresses onscreen; PMEM is the real pointer to the current address during disassembly. The decrementing of PMEM, made necessary by that JSR GB early in the main loop, is not necessary for SA. (SA is not incremented by the GB subroutine.)

GETBYTE: The Main Loop

Now we arrive at the main loop. GETBYTE (190) first tests to see if the user wants to stop disassembly via the STOPKEY subroutine (in the Eval subprogram within LADS). Then the GB subroutine (690) raises the memory pointer PMEM and fetches a byte from memory. This byte is saved in the FILEN buffer and will act as an index, a pointer to the various tables in the Dtables subprogram. For purposes of illustration, let's assume that the byte we picked up held the number 1. One is the opcode for ORA (Indirect,X). We can trace through the main loop of Dis and see what happens when Dis picks up a 1.

The 1 is transferred to the Y Register (200), and we then load whatever value is in MTABLE + 1 since we LDA MTABLE,Y and Y holds a 1. This turns out to be the number 2, signifying that we've come upon the second opcode (if the opcodes are arranged in ascending order). Notice that BNE will make us skip over the next couple of lines. Anytime we pull a 0 out of MTABLE it means that there is no valid opcode for that number, and we just print the address, the number, and a question mark ($3F). Then we raise the printout address pointer with INCSA and return to fetch the next byte (210-220).

Modifying LADS: Special Notes on Atari and Apple LADS

However, in our example, we did find something other than a 0 in MTABLE. We've got a valid opcode. Now we have to find out its addressing mode and print a one- or two-byte argument, depending on that addressing mode. Is it Immediate addressing like LDA #15 (one-byte argument) or Absolute addressing like LDA 1500 (two-byte argument)?

Having found a valid opcode, we now extract the mnemonic from WORDTABLE and print it out (240-330). First we multiply our number from MTABLE by 3 since each mnemonic has three letters. The number we found in MTABLE was a 2, so we have a 6 after the multiplication. That means that our mnemonic will start in the sixth position within WORDTABLE. We add 6 to the address of WORDTABLE (280-290) and leave the variable PARRAY pointing at the first letter O in WORDTABLE.

Now the SA (current disassembly address) is printed onscreen with PRNTSA and a space is printed (300). We then print ORA onscreen, one letter at a time (310-330), and print another space. Now we're ready to figure out the addressing mode.

Addressing Type

We had previously saved our original byte (the number 1 in our example) in FILEN (190). We now retrieve it, pull out the position value from MTABLE (getting the number 2), and load in the addressing mode type from TYPETABLE (see lines 360-410 in the Dtables subroutine listing at the end of this chapter). It turns out that the number 2 we're using in our example will pull out a number 4 from TYPETABLE. The number 4 identifies this as an Indirect X addressing mode.

Between lines 380 and 410 we have a simple decision structure, much like BASIC'S ON-GOTO structure. In our example, the CMP #4 in line 390 will now send us to a routine called DINDX which handles Indirect X addressing.

DINDX (460) takes advantage of several routines which print symbols to the screen for us: LEPAR prints a left parenthesis; DOONE fetches and prints the next number in RAM memory (the argument for the current mnemonic); COMX prints a comma and an X; and RIPAR finishes things off with a right parenthesis. Now we have something like this onscreen:

Modifying LADS: Special Notes on Atari and Apple LADS

0360 ORA (12,X)

so our disassembly of this particular instruction is complete. We JMP to ALLDONE (600) and print a carriage return and start the main loop over again to disassemble the next mnemonic.

Other mnemonics and other addressing modes follow a similar path through Dis as they are looked up in Dtables and then printed out.

By the way, if you look at lines 650-680 on page 296, you'll see a peculiar #" pseudo-op. It allows you to specify a character instead of a number for immediate addressing. In line 650 we need to print a comma to the screen. You could LDA #44 (the ASCII code for a comma) and JSR PRINT.

But if you don't want to look up the ASCII code, LADS will do it for you. Just use a quote after the # symbol: LDA #", (followed by the character you're after; in this case, the comma). The correct value for the character will be inserted into your object code. You can see that we used this pseudo-op to load the value for X, Y, ), and ( symbols as well, in lines 650-680.

Modifying LADS: Special Notes on Atari and Apple LADS

!

s

5

6

CO

a S

2

2

cm

a

ce

+ cm

H

<

CO

CO 02 W 2 CO H Q

H CO

IS)

-8= >H

H P

2 J

M ••

os o

Hi <C

CO X

Pm < ro E-i CO- CO

u

H

Q

CO

co

w «

□

Q 2!

a!

H CO

e-

H

a

H 2

E B

CO CO

H W

a s

CO I 53 H

i <a a co r» *

Q

J <

•• Q

OS J U

Eh co

2 H

OS

O

II Q

'•* J

S Q tS

H cm m

os < X CJ

os q

CO 2

b Eh Q

s a

Eh cj

q m o r-

2

3

D Eh

s

w o

H

DS

a

<c o

Eh S3

H

CM

« CO

CJ

H

a > co

H

O >H

a w

o

Eh

D +

co S

H W

« 2

CM

u

co H

Q

Eh Ph

CO CQ

OS *

co

■• Q

>H J

J cm

w a

D3 w

< Eh

+ +

Eh Eh

pj J

D D

CO CO 01

CQ W

OS OS

+ E-

D co W

< Eh

Eh CO

CO ••

" J

s> w

* m

<

< j

J Q 2

.. .. pq

< em S

CO CQ CM

< H < Eh 2 Eh CO CQ CO

Eh Eh Eh

J J J

D D D

CO CO CO

WWW

os os os

HOQ S U J CJ CQ

S $ 53 CO CTi .H

CJ

a o o

CM

O

<—-

> x

a

< Q 2

CO H M

co

Eh < M

» fH >

M <

co

w —

w -

CO 2 H

Q J 2 H

< fa

W < Eh Eh >h CO CQ •• CQ

< O

2 OS H CO

a

a o

CJ

cm o

w

CJ

<c

CM

CO

Eh

2 OS CM

• «

os cj

CO <

1-3 CM

• ■ CO

< Eh

co 2

ir< OS

2 CM

os

CM OS

CO

OS i-J

CO ••

1-3

+

< os os

<

J

o

os

>H

>H O < < < <

2 £ Q Q Q O

H Q J J J >J -

Q Q S Q S S G3

H (N ro -^ u"i y3 t^-

W OS

a co

CQ 1-3 >H s

w

Q

o

CM CJ S CM 1-3 O

< Q

CO H

CJ J

2 <

H >

os <

Cm

CO

J CO

+

OS

<

CM

Eh CO

+

OS

< >H

Cu CQ

>H >H

Eh

CO >H

•• J

S CM

* M

os

<

CM

CJ Q <

< a

H >H

W < OS K

a

>H X

< Q

a

fc 2 ro m

00- Eh

* 2 O

< u

Q J -

os

Oj

u

Q <

U

CJ

OS

o

<

Q

>H -I

< + OS >H

OS sC

< OS W CM OS

a <

CQ < CM < fr> Eh CO Q

OS >i

O rt ••

Skh

OS +

O < >H

Eh CM rf

CO CJ OS

Q <

< CM

<

Eh CO

W CJ

,J Q

CQ <

< •• Eh H Q J

os ca

o <

S Eh

v a

* os

o

< 13 Q a

J *

CJ <

J Q

U J

COO^S^HCNm^LDcDr^COC^

Modifying LADS: Special Notes on Atari and Apple LADS

S'HCNro^ini^i^coc^SiHCN<^^iniflr^rac^QrHCNt-0'=i'u-)u3r^cocr>Q

romnc»)'»inm(»imrri'H^.^^^^.^^^.^.i/ linu1tfl i n ^ 1I1 i r | inullfl

Modifying LADS: Special Notes on Atari and Apple LADS

Modifying LADS: Special Notes on Atari and Apple LADS

— x

OCS13CS1QS53CS53QQQ3355S53G3S5S555S35355555353535555

^^copciHMro'-fir)\Dscoa\iSHNrr)'r| , inkor--coc>sir-irN<»i^ , in HHHHNCNNMNiNiNci|MNronrirnrir5rori(»in<t^rfrfrfH.

Modifying LADS: Special Notes on Atari and Apple LADS

cSriS)c2cS)QiStS}caiacScCS}tS)Si

coi^cocrvtarHCNco^incot^-co ^■sf^'i'ininLnLnininininin

Modifying LADS: Special Notes on Atari and Apple LADS

Notes on the Structure of Atari LADS

The Atari and Commodore machines have one thing in common—a 6502 microprocessor. The Atari 6502 runs at 1.79 megahertz, making it somewhat faster than the Commodore machines. However, the non-6502 hardware—input/output, graphics, and sound—is entirely different. Although many Atari enthusiasts argue that it is the most powerful available on any 6502-based microcomputer, the operating system of the Atari does not perform basic tasks like input/output in the same manner as Commodore machines. An understanding of these differences is essential to fully understand the Atari LADS source code.

The common tasks machine language programs need to perform with input/output are: open a file, read a character or block of characters from the file, write a character or block of characters to a file, and close the file. With the Commodore operating system (often called the Kernal), there are separate routines for each task. You approach each task by adjusting the Accumulator, X, and Y Registers as necessary, as well as storing any required information into special memory locations (usually in zero page). See the discussion of OPEN1 in Chapter 5 for details. For example, the Commodore OPEN must know where to find the filename, the length of the filename, parameters like read or write, and the device number.

On the Atari, there is just one entry point—$E456, called CIO, for all these tasks. Instead of separate entry points, CIO checks a memory location for the command, a number representing the action to take, such as OPEN, CLOSE, PUT, or GET. Other memory locations hold the starting address of a filename or buffer, and the length of the filename or buffer. Extra locations hold specialized information. Each block of I/O information is called an IOCB, for Input/Output Control Block. There are eight of these IOCBs, numbered 0 to 7. IOCB 0 is reserved for the screen editor, and 7 is usually reserved for language I/O, such as LPRINT in BASIC, or SAVE in the LADS editor.

Although much of LADS is concerned with internal data base-type manipulations, such as looking up a label or converting a mnemonic, there is also a good amount of Commodore-style input/output. Routines like OPEN, CLRCHN, CHKIN, and PRINT are actual ROM entry points on Commodore computers. To avoid complex changes in the source code,

Modifying LADS: Special Notes on Atari and Apple LADS

Atari LADS has a special file called Kernal (see program listings below), which transparently supports all these routines, making the conversion between the Atari's I/O system and the Commodore's transparent. Explanations of Commodore I/O given in Chapter 5, then, are valid as well for the Atari LADS system. In other words, when the original Commodore version of LADS was translated to the Atari, the Kernal subprogram was added to mimic the operations of the Commodore operating system I/O. This emulation allows the descriptions of LADS to remain essentially identical for non-Commodore machines.

Atari Memory Layout

Memory maps for Commodore computers are relatively simple. Zero page is used by the system, page 1 for the stack, page 2 for operating system storage, and page 3 for the cassette buffer(s). On the Commodore PET, page 4 (starting at address 1024) on up to location 32768 is free RAM. 32768 is the start of screen memory on the PET, and never moves. On the 64, the screen occupies page 4 up to 2047 ($07FF). Free RAM starts at 2048 ($0800) all the way up to 40959 ($9FFF). BASIC in ROM and the operating system start at 40960 ($A000). Although there is hidden memory beneath the ROMs on both the Atari XL series and the Commodore 64, LADS does not use it.

The Atari memory layout is less fixed. Zero page from locations 0 to 127 completely used by the operating system. An applications program like BASIC can use almost all the memory from 128 to 255. Since Atari LADS operates outside the BASIC environment, it is free to use this zero page memory upwards from location $80.

Unlike the PET and 64, Atari machines have no set amount of memory. Atari 400/800 owners have the option of expanding to 48K, without using bank selection or other tricks. Without DOS, free memory starts at $0700 (page 6 is reserved). With DOS, free RAM starts at about $2000. The screen memory, a little over IK in length, is stored at the top of memory, and is not fixed, due to memory expansion. Many Atari machine language programs store themselves at the bottom of memory, then use memory above themselves to store text or other information. But because LADS stores its labels below itself, the Atari version must be located at the top of

Modifying LADS: Special Notes on Atari and Apple LADS

memory. Since the top of memory with a cartridge (or with 40K of RAM) is $9FFF, and since Atari LADS is about 7K long, $8000 seems to be a good place. If you have a 48K Atari, you may want to reassemble LADS at $A000. The choice of $8000 does exclude Atari owners with less than 40K, but if you have access to a 40K machine, you could reassemble LADS at 8K below the top of memory.

Let's look at the major differences between the Atari LADS and Commodore LADS source code. We won't get into specifics; for that you can refer to the source code itself. The translation of Atari LADS involved two goals: the creation of a powerful assembly development system without making major changes to most of the Commodore LADS source code. Some subprograms needed no changes, others did. Three new subprograms are required by the Atari version: Kernal, System, and Edit.

Here's how all the subprograms in the Atari LADS are linked:

Defs -» Eval -» Equate -* Array -» Openl -» Findmn -» Getsa -» Valdec -> Indisk -» Math -» Printops -» Pseudo -» Kernal -» System -» Edit -» Tables

Defs. Here we set the origin to $8000. Since we are simulating Commodore I/O, we have to create some label variables such as FNAMELEN (filename length). These are used by the Kernal routines. Other LADS variables like MEMTOP and PMEM are also given zero page definitions for the sake of speed and for indirect addressing. The BABUF, used for holding comments and holding a line in the editor, is defined as $0500. On Commodore machines it is $0200, the address of the BASIC input buffer.

Eval. The first difference between the Commodore and Atari versions of Eval is that instead of reading the filename off the screen, Atari LADS gets the filename from the command line, passed by the editor. The editor has previously set RAMFLAG to 1 if there is no filename. This is a default to RAM-based assembly (your source code is already in memory and need not be read from disk). If RAMFLAG is 0, LADS must assemble from disk. If the RAMFLAG is nonzero, we skip over putting the filename into FILEN, and jump past the JSR OPEN1 in Eval (since there is nothing to open). At the top of Eval, the left margin is set to zero.

Since LADS has complete control of the Atari, no memory

Modifying LADS: Special Notes on Atari and Apple LADS

needs to be protected from anything, so the top-of-memory pointer need not be lowered.

In FINI, the RAMFLAG is also checked so that JSR OPEN1 is skipped. In FIN, which FINI falls to after the end of the second pass, we send an extra byte out to the object file, if .D was used.

Equate, Array, and Findmn. There was no need to change any of these modules, since they contain no system-specific coding.

Openl. Many changes have also been made to Openl, although a lot of the source code is similar. FDEV and FSECOND hold the device number and secondary address in Commodore LADS. Here they are used to hold the access type (4 for read, 8 for write) and the auxiliary byte (which is zero here). Openl checks the RAMFLAG to see whether it should load the file after it's been opened, in case memory assembly has been elected. The actual load is done by using part of the editor's load routine. Because of RAMFLAG, we don't need a separate LOAD1 routine.

If the file can't be opened, we call the editor's error message routine, and then return to the editor. The same error handling is performed for all the OPENs.

OPEN2 writes out the binary file header, made up of two 255's, followed by the starting and ending addresses in low byte/high byte format. The origin (the starting address for the object code) is saved in the variable TA. The object code's ending address is known, and stored in LLSA. LLSA is actually one higher than the ending address, which is why we write an extra zero to the end of the file in Eval. This prevents an ERROR 136 when loading the file from DOS.

OPEN4 just opens a file for write to the printer. The printer's filename is P:, which is given in the .BYTE statement as 80 58.

Getsa. Getsa is very similar to the Commodore version. There is no MEMSA—Getsa initializes PMEM to point to the start of the editor's text buffer (TEXTBAS), even if PMEM is not used. Since CHARIN is smart, checking RAMFLAG to decide whether to assemble from memory or from disk, no more changes need to be made.

Valdec. Valdec would have been unchanged from the Commodore version, since there is no machine-specific code. However, the editor makes use of Valdec to convert ASCII line

Modifying LADS: Special Notes on Atari and Apple LADS

numbers into integers. The ASCII line number does not end with a zero, though. The first part of Valdec finds the length of the number by checking for a zero. It has been changed in the Atari version to exit on any nonnumeric digit (one with an ASCII value less than 48 or greater than/equal to 58). The change does not affect any other use of Valdec.

Indisk. It is in Indisk where we see many modifications to the Commodore version. Since the editor does not tokenize anything, KEYWORD and KEYWAD are not needed, and references to them in this source code, as well as the KEYWORD and KEYWAD routines themselves, have been deleted. Again, since nothing is tokenized, checks for +,*,<, >, etc., look for the ASCII values instead of the tokenized ones. Since line numbers are stored as a string of digits instead of a two-byte integer, we must call LINENUMBER in the SYSTEM module in order to set LINEN. ENDPRO, instead of looking for three zeros to signify the end of a program, must check the disk status variable for end of file. End of file returns 136 after the last character has been read, and $03 if you try to read past the end of file, so we check for both to be safe. We check the status for file #1 (the input file) directly ($0353), instead of ST, since ST may have been changed by another I/O operation. Nonetheless, large parts of Indisk are unchanged from the Commodore version.

Printops. Because of the Kernal simulator, even though Printops has plenty of Commodore I/O calls, few changes were needed to make Printops work on the Atari.

Pseudo. There are some minor changes here. KEYWORD does not need to be used by .END or .FILE. FILE finds the end of the pseudo-op by looking for a space delimiter. The filename is then copied into FILEN, and the file opened. If the current operation is a RAM-based assembly, Openl takes care of loading in the next file. PEND, which supports .END, first calls FILE to open the file, then copies SA, which holds the current address, into LLSA for use with OPEN2.

Speaking of OPEN2, some code was deleted from PDISK and instead implemented in OPEN2. There were no more changes after PDISK to the Pseudo module. In Commodore LADS, Pseudo links to Tables, the last module. Here we link to Kernal, inserting Kernal, System, and Edit into the chain.

Kernal. This is the most important module in the Atari translation. It implements all the Commodore I/O functions

Modifying LADS: Special Notes on Atari and Apple LADS

by simulating CHKIN and CHKOUT, and referencing the appropriate IOCB according to FNUM. The CIO equates are first defined: ICCOM, the command byte; ICBADR, which holds the address of the filename or buffer; ICBLEN, which holds the length of the filename or buffer; ICAUX1 and ICAUX2, which need to be set to zero; and CIO itself, that single entry point for all input/output.

A simple routine is XI6, which multiplies the Accumulator times 16 and stores it in the X Register. X will be an offset from the first IOCB. Since each IOCB is 16 bytes long, we can use Indexed addressing to change the appropriate IOCB with a statement like STA ICCOM,X.

OPEN is the basic open-file routine. It uses XI6 to get the IOCB offset, then stores the filename pointer and filename length into ICBADR and ICBLEN. The command byte for open ($03) is stored in ICCOM, then CIO is called. CIO's error status, which is returned in the Y Register, is saved in ST.

CHKIN changes the default input IOCB, which is used in CHARIN. CHKOUT changes the default output IOCB, which is checked for in PRINT. CLOSE just stores the close command (12) into ICCOM and jumps to CALLCIO, part of OPEN. CLRCHN sets the default INFILE and OUTFILE, as well as FNUM and ST to zero, which makes CHARIN and PRINT use IOCB #0, opened to the screen editor.

PRINT is expected to print the character currently in the Accumulator. It first changes any 13's it sees, which are Commodore carriage returns, into 155's (Atari carriage returns). Another entry point, OBJPRINT, does not transform 13's. This is called when object bytes need to be sent to disk, where you don't want 13's changing into 155's. Depending on OUTFILE, PRINT will automatically use the appropriate IOCB (0 for screen, 2 for object output, 4 for printer output). We then set the buffer length to zero, which tells CIO to expect to find the character to print in the Accumulator. The print text command is used, then we call CIO and restore the X and Y Registers, which were saved when PRINT was entered. This prevents any interference with LADS.

CHRIN is also a busy routine. It first checks RAMFLAG to see whether it should get a byte from an I/O device or from the editor's text memory. If it gets a byte from memory, it must check to see if it has gone past the last byte. If so, we jump straight to FINI in Eval. Otherwise, CHRIN gets a byte

Modifying LADS: Special Notes on Atari and Apple LADS

from disk or the keyboard. It uses INFILE to decide which IOCB to use, then sets the buffer length to zero. This way it requests a single byte from CIO. If a 155 is returned, it is changed into a zero, which is what LADS looks for as end of line.

There is no "check for BREAK key" routine in Atari ROM, so STOPKEY checks the BREAK key flag, which is set to zero if the BREAK key is pressed. If BREAK was pressed, we execute TOBASIC, which jumps back to the editor.

CLALL is not used by LADS, but is used by the editor to close all files in case of an error. It works like the Commodore CLALL routine, and restores the default I/O (input from keyboard, output to screen) by jumping to CLRCHN.

System. A few more routines are provided here which are not directly supported by the operating system. OUTNUM prints the ASCII number given to it in the X Register, which holds the low byte of the number to print, and the Accumulator holding the high byte. We then call $D9AA, which converts the integer number in locations $D4 and $D5 into floating point, and then call $D8E6, which converts the floating point into a printable ASCII sequence of digits starting at $0580. The routine at $D8E6 sets bit 7 in the last digit of the ASCII numeral string. We print the string, checking and masking off bit 7. LINENUMBER reads the ASCII line number from source code and converts it to an integer, using VALDEC. The result is saved in LINEN.

Tables. The major changes here are that the error messages must be typed in inverse video. One extra variable is defined: LLSA to hold the ending address.

Program 11-3. Kernal

100 ICCOM = $0342

110 ICBADR = $0344

120 ICBLEN = *034S

130 ICAUX1 = *034A

140 ICAUX2 = *034B

150 CCLOSE = 12

160 CIO = $E456

170 X16 ASL

180 ASL

190 ASL

200 ASL

2 10 TAX

220 RTS

Modifying LADS: Special Notes on Atari and Apple LADS

#FNUM,FDEV,FSECOND,<F

Modifying LADS: Special Notes on Atari and Apple LADS

Modifying LADS: Special Notes on Atari and Apple LADS

1220 KTEHP .BYTE 0

1230 .FILE D:SYSTEM.SRC

Program 11-4. System

170 OUTNUM STX $D4

180 STA *D5

190 JSR *D9AA

200 JSR *D8E6

230 LDY #0

240 ONUMLODP STY OYSAVE

250 LDA ($F3),Y

260 PHA

270 AMD tt$7F

280 JSR PRINT

290 PLA

300 BMI ONUMEXIT

310 LDY OYSAVE

320 I NY

330 BNE ONUMLOOP

340 ONUMEXIT RTS

360 OYSAVE .BYTE 0

390 LINENUMBER LDY #0

400 LINELOOP JSR CHARIN

410 CMP #32

420 BEQ OUTLINE

430 STA BABUF.Y

440 INY

450 JMP LINELOOP

460 OUTLINE LDA #0

470 STA BABUF.Y

480 LDA 4KBABUF

490 STA TEMP

500 LDA #>BABUF

510 STA TEMP+1

520 JSR VALDEC

530 LDA RESULT

540 STA LINEN

550 LDA RESULT+1

560 STA LINEN+1

570 LDY #0

580 RTS

590 .FILE D:EDIT.SRC

The Atari LADS Editor

The Atari editor is a whole minilanguage system itself. The source code for this subprogram is well commented and should be understandable as it stands. Since it is not a part of

Modifying LADS: Special Notes on Atari and Apple LADS

LADS proper, we'll limit ourselves here to an overview of the major routines.

UMOVE and DMOVE are high-speed memory move routines used to adjust the source code when lines are deleted, added, and so forth. UMOVE can move one range of memory to another, provided that the block to be moved is higher in memory. The range of bytes can overlap so UMOVE can be used as a delete routine. DMOVE moves memory downward, and is used for inserting. If the memory ranges do not overlap, either one can be used. FROML and FROMH hold the start of the block to be moved. DESTL and DESTH are where the block is moved to. LLEN and HLEN are set to hold the length of the block to be moved. These routines use self-modifying code for speed.

EDIT is the entry point for LADS when it is first run, as well as the return point from the LADS assembler. It cleans up the stack, resets the left margin to 2, then stores the addresses of all the editor commands into COMVECT, which is a lookup table used by COMMAND. The BRK interrupt is initialized to point to a special breakpoint entry to the editor. We then check to see if this is the first time EDIT has been entered. If so, we need to NEW out any garbage in memory. The NEW routine sets the end-of-text pointer to point to the beginning of text. No memory is actually cleared.

PROMPT is the entry point for a new line. It prints "LADS Ready", then falls through to ENTER, which is the entry point for a new line without printing a prompt. CHARIN from Kernal gets a byte, which is then processed to remove lowercase, etc. The line is stored in the BABUF, starting at $0500. When a carriage return is detected, an Atari carriage return is added to the end of the line in BABUF, and the length of the line is saved in INLEN. If the length is zero, we go back for another line. The first character of the line is checked. If it is a numeric digit, there must be a line number. If there is no line number, then the line must be a command.

If it is a line number, we call GETLNUM to get the integer value of the line number. GETLNUM also calls FINDLINE to see if that line already exists. If it does, the line is deleted. Then we check to see if there is anything else besides just a line number. If not, we don't insert the line into the source code. Since the line was already deleted, this has the desired effect. We then go back for another line.

Modifying LADS: Special Notes on Atari and Apple LADS

COMMAND searches through a table of commands, matching the line the user typed in against the table. If the command is not found, a syntax error message is displayed, and we return to PROMPT. If the command is found, we save the position of whatever's after the command (the argument) in ARGPOS. The command number (COMNUM) is used as an index into COMVECT, which holds the addresses of all commands. We get the address, subtract one from it, then put it on the stack. A RTS then ends up pulling this address off and jumping to it. It's like ON-GOTO in BASIC.

MLIST lists the entire text buffer, from TEXTBAS to TEXEND. A second entry point in MLIST, INLIST, is called by the LIST routine to list a part of a program. We also check here for the BREAK key. MLIST is used by SAVE to list the program to disk, cassette, or the printer.

DOS is spectacularly simple. It just jumps through the DOS vector, location $0A.

FINDLINE is crucial to the editor. It searches through the source code, trying to match the line number given to it (LNUM) against all the ASCII line numbers in the program. It uses Valdec to convert the ASCII line number into an integer. Because of all the ASCII to integer conversions, FINDLINE can be slow on long programs. It returns with BEGPTR pointing to the beginning of the line found, and ENDPTR pointing to the end of the line. If there is no program in memory, it returns with BEGPTR and ENDPTR pointing to the start of text. If the line is not found, BEGPTR and ENDPTR point to the next line greater than the line number searched for. If there is no such line, they point to the end of text. The size of the line found is also calculated for the benefit of the delete routine.

DELETE calls FINDLINE, then calls UMOVE to move memory from the end of the line on top of the beginning of the line. TEXEND is then changed to reflect a shorter program. Many checks have to be made to prevent a crash under conditions such as no program in memory. INSERT is similar to DELETE. It calls DMOVE to insert a gap at the position the line was found.

ERRPRINT is used to display an error message. To be safe, it also closes all files. GETNUM gets and converts an ASCII line number to an integer, using the system ASCII-to-floating-point and floating-point-to-integer routines. The routines return a pointer to the end of the number. This

Modifying LADS: Special Notes on Atari and Apple LADS

pointer is always kept track of so we can check for new command arguments. GETLNUM uses this routine, then calls FiNDLINE.

LIST calls GLIST, which is also used by SAVE. GLIST finds out the line number range you want to list. If there is no line number range given, it goes to MLIST to list the entire program. Otherwise, it has to check for just one line given, or a range of lines. It's complicated, but it works.

OPENFILE is used by SAVE, LOAD, and MERGE. It looks at the argument of the command to get the filename, then calls OPEN within Kernal. If there is an error, we jump to PROMPT. SAVE calls OPENFILE with an 8 for output. It then sets the output file and calls GLIST, which sends the listing out to the current output file. After GLIST returns, the file is closed.

MERGE just sets the input file to the device and jumps to PROMPT. PROMPT keeps requesting input and storing lines until it gets an error. It then closes the file and restores default I/O.

Adding Your Own Editor Commands

The LADS command checks to see if there is a filename, then sets the RAMFLAG accordingly and jumps into EVAL. The SYS command calls GETNUM to get the decimal argument, then stores the address right after a JSR, to which it then falls through, creating a self-modifying indirect JSR. If the routine being called ends in a RTS, control will be returned to PROMPT. You can use SYS to add new editor commands. Just check location $D0, which will point to a position with BABUF ($0500) after the SYS number. You can use $D0 to check for extra arguments within BABUF.

LOAD calls OPENFILE to open the load file for read. It has a second entry point (AFTEROPEN) if the file has already opened. For maximum speed, the program is loaded by calling the CIO get-record routine, which loads in the entire file directly at TEXTBAS, the start of text. Beware, though, that no conversions are done on any of the text, and no checks are made for a legal source file. You could even load and list word processing files. AFTEROPEN is called by Openl if RAM needs to be reloaded during a memory assembly.

The last routine in the editor handles a BRK instruction entry encountered. It prints a message, uses OUTNUM to dis-

Modifying LADS: Special Notes on Atari and Apple LADS

play the address where the BRK was found, clears the interrupt flag, cleans the stack, then jumps to the Edit entry point. Edit then links to Tables.

Program 11-5. Editor

100 ;Line Editor +or LADS

110 ;Charles Brannon 1984

0120 ;

0130 PTR = tCB

0140 TEXTBAS = $2000

015 0 ;Move routines

0 160 ;

0170 JMP EDIT

0180 FROML .BYTE 0

0190 FROMH .BYTE 0

0200 DE5TL .BYTE 0

0210 DESTH .BYTE 0

0220 LLEN .BYTE 0

0230 HLEN .BYTE 0

0240 ENDPOS .BYTE 0

0250 INLEN .BYTE 0

0260 LNUH -BYTE 0 0

0270 TEXTPTR .BYTE 0

0280 COHNUM .BYTE 0

0290 TEXEND .BYTE 0 0

0300 LEN .BYTE 0

03 10 YSAVE .BYTE 0 0320 BESPTR .BYTE 0 0 0330 ENDPTR .BYTE 0 0 0340 FOUNDFLAG .BYTE 0 0350 LINESIZE .BYTE 0 0 0360 SAVEND .BYTE 0 0 0370 SAVBEG .BYTE 0 0 0380 ARGPOS .BYTE 0 0390 ZFLAG .BYTE 0 0400 LCFLAG .BYTE 0

04 10 FIRSTRUN .BYTE 0 0420 INDEX = *D0 0430 TMP .BYTE 0

044 0 ;

0450 UMOVE LDA FROML

0460 STA MOVLOOP+1

0470 LDA FRDMH

0480 STA MOVLDDP+2

0490 LDA DESTL

0500 STA MOVLOOP+4

0510 LDA DESTH

0520 STA MOVLOOP+5

Modifying LADS: Special Notes on Atari and Apple LADS

0530 LDX HLEN

0540 BEQ SKIPMOV

0550 MOV1 LDA #0

0560 M0V2 STA ENDPOS

0570 LDY #0

05S0 MOVLOOP LDA $FFFF,Y

0590 STA *FFFF.Y

0600 I NY

06 10 CPY ENDPOS

0620 BNE MOVLOOP

0630 INC M0VL00P+2

0640 INC MQVL00P+5

0650 CPX #0

0660 BEQ OUT

0670 DEX

0680 BNE MOV1

0690 SKIPMOV LDA LLEN

0700 BNE MOV2

0710 OUT RTS

0720 ;

0730 DMOVE LDA HLEN

0740 TAX

0750 ORA LLEN

0760 BNE NOTNULL

0770 RTS

0780 NOTNULL CLC

0790 TXA

0800 ADC FROMH

0810 STA DM0VL00P+2

0820 LDA FROML

0830 STA DMOVLOOP+1

0840 CLC

0850 TXA

0860 ADC DESTH

0870 STA DM0VL00P+5

0880 LDA DESTL

0390 STA DMOVLOOP+4

0900 I NX

0910 LDY LLEN

0920 BNE DMOVLOOP

0930 BEQ SKIPDMOV

0940 DMOV1 LDY 4*255

0950 DMOVLOOP LDA $FFFF,Y

0960 STA *FFFF,Y

0970 DEY

0980 CPY #255

0990 BNE DMOVLOOP

1000 SKIPDMOV DEC DMOVLOOP+2

Modifying LADS: Special Notes on Atari and Apple LADS

1010 DEC DMOVLOOP+5

1020 DEX

1030 BNE DMOV1

1040 RTS

1050 ;

1060 EDIT LDX #255;Reset stack

1070 TXS

1071 JSR CLALL

1080 LDA #0;CI ear RAMFLAB

1090 STA RAMFLAB

1100 LDA #2;Left margin

1110 STA B2

1120 JSR PRNTCR

1130 ;Store addresses of commands

1140 LDA «<LIST

1150 STA COMVECT

1160 LDA #>LIST

1170 STA COMVECT+1

1180 LDA #<DOS

1190 STA COMVECT+2

1200 LDA #>DOS

1210 STA COMVECT+3

1220 LDA #<INIT

1230 STA COMVECT+4

1240 LDA #>INIT

1250 STA COMVECT+5

1260 LDA «<SfiVE

1270 STA COMVECT+6

1280 LDA #>SAVE

1290 STA COMVECT+7

1300 LDA #<LOAD

1310 STA COMVECT+8

1320 LDA #>LOAD

1330 STA COMVECT+9

1340 LDA #<MERBE

1350 STA COMVECT+10

1360 LDA #>MERSE

1370 STA COMVECT+1 1

1380 LDA #<LADS

1390 STA COMVECT+12

1400 LDA #>LADS

1410 STA COMVECT+13

1420 LDA #<SYS

1430 STA COMVECT+14

1440 LDA #>SYS

1450 STA COMVECT+15

1460 :Set BRK instr. interrupt to breakpoint

entry

1470 LDA #<BREAK:STA 518:LDA #>BREAK:STA 519

Modifying LADS: Special Notes on Atari and Apple LADS

1480 LDA FIRSTRUN

1490 BEQ DOIMEW

1500 JMP PROMPT

1510 DONEW LDA tt$CB

1520 STA FIRSTRUN

1530 JMP INIT

1540 NEW LDA #<TEXTBAS;St ore beginning locat

ion at ending pointer

1550 STA TEXEND

1560 LDA #>TEXTBAS

1570 STA TEXEND+1

1580 JSR CLRCHN;Keyboard/Screen

1590 RTS

1600 INIT JSR NEW

1610 ;

1620 PROMPT LDA #<PMSG;Print prompt

1630 LDY #>PMSG

1640 JSR PRMSG

1650 ENTER LDY #0;Get a line

1660 STY 2FLAG

1670 STY LCFLAG

1680 GETIT JSR CHARINja character

1690 LDX ST;Error?

1700 BPL NOERR

1710 CPX #136;End of file?

1720 BEQ EOF:don't print error

1730 CPX #128; same -for break key abort

1740 BEQ EOF

1750 JSR ERRPRI NT;print other error

1760 EOF JSR CLOSEIT;close down active -file

1770 JMP PROMPT;get new line

1780 NOERR CMP #34;A quote toggles the lower

case flag

1790 BNE NOTQUOTE

1800 PHA;save quote

1810 LDA LCFLAG;flip lowercase

1820 EOR #1

1830 STA LCFLAG

1840 PLA;restore quote

1850 NOTQUOTE CMP #48;an ASCII "0"?

1860 BNE NOTZ

1870 LDX ZFLAG;if so, check to see if it's a

1ead i ng z er o

1880 BEQ GETIT;if it is, ignore it

1890 NOTZ INC ZFLAG;if we get here, reset le

ading zero flag

1900 CMP #59;now check for comment

1910 BNE NOTREM

Modifying LADS: Special Notes on Atari and Apple LADS

19 20 INC LCFLAG;di5able lowercase conversion

■for rest of line 1930 NOTREM LDX LCFLAG 1940 BNE NOTLOWER;if remflag has been set, d

on't convert lowercase 1950 AND #127:l;ill inverse 1960 CMP #97; lowercase " a " 1970 BCC NOTLOWER;if less than, not lowercas

e 19S0 CMP #123; 1owercase "z" + 1 1990 BCS NOTLOWER;if >=, not lowercase 2000 AND #95;kill bit 5 <127-32=95) 2010 NOTLOWER STA BABUF,Y;store it 2020 INY 2030 CMP #0 2040 BNE BETIT 2050 DEY 2060 LDA #155 2070 STA BABUF,Y

2080 STY INLEN;save length of line 2090 CPY #0 2100 BEQ ENTER;if length=0, blank line, so g

o back 2110 LDA BABUF;first character: is it a numb

er? 2 120 CMP #58 2130 BCS COMMAND;greater than "9", so must b

e a c ommand 2140 CMP #48;"0" 2150 BCS LINE;greater than "9", but greater

t h a n / = " 0 " ? 2160 JMP COMMAND;no, so command 2170 ;Must be a line, so get line number 2180 LINE LDA #255;no offset 2190 JSR GETLNUM 2200 LDA INDEX; INDEX points to first non-nun

eric digit 2210 STA TEXTPTR;so save it 2220 LDA FOUNDFLAG:if it exists 2230 BNE NDDELETE;it not. don't delete it 2240 JSR DELETE 2250 NDDELETE LDY TEXTPTR;is there any text

on the 1i ne? 2260 CPY INLEN;compare to line length 2270 BED OVERINS;no text, just delete 2280 JSR INSERT; otherwi se insert line 2290 OVERINS JMP ENTER;and get another line 230 0 :

Modifying LADS: Special Notes on Atari and Apple LADS

2310 COMMAND LDA 4»< COMT ABLE ; p o i nt to start f command table

command number

get a character of com

mand table 2390 BED COMFQUND;if we get zero here, comma

nd is found 2400 CMP #255;or syntax error 2410 BEQ SYNERR 2420 CMP BABUF,X; match with parallel charact

er in line buffer? 2430 BNE NOTFND;if comparison fails, try nex

t c omman d 2440 INX;next character 2450 BACK IN I NY

2460 BNE SRCH;bump high byte? 2470 INC PTR+1;yes 2480 JMP SRCH;continue 2490 NOTFND LDA <PTR),Y;if not found, skip p

ast ending zero 2500 BED NXTONE 25 10 I NY

0 BNE NOTFND

2530 INC PTR+1

2540 JMP NOTFND

2550 NXTONE INC COMNUM;bump up command numbe

r

256 0 LDX #0;continue search

2570 JMP BACK IN

2580 SYNERR LDA #<SYNMSG;print syntax error

2590 LDY #>SYNMSG

2600 JSR PRMSG

2610 JMP PROMPT

2620 COMFOUND STX ARGPOS

2630 LDA COMNUM; indirect jump to address of

c omman d

2640 ASL

2650 TAX

2660 LDA COMVECT,X

2670 SEC

2680 SBC #1

2690 STA TMP

2700 LDA COMVECT+1,X

Modifying LADS: Special Notes on Atari and Apple LADS

each comman

d routine in low, high -format 2960 COMVECT .BYTE 0000 0 00000000

0 0 0 2970 ; 2980 MLIST LDA #<TEXTBAS;Point to beginning

o-f pr ogr am 2990 STA PTR

3000 SEC;get length o-f program to list 3010 LDA TEXEND 3020 SBC PTR 3030 STA LLEN;into LLEN 3040 LDA #>TEXTBAS 3050 STA PTR+1 3060 LDA TEXEND+1 3070 SBC PTR+1 3080 STA HLEN;and HLEN 3090 INLIST LDA HLEN 3100 TAX

3110 ORA LLENsboth zero? 3120 BNE DOLIST

3130 RTS;if so, exit LIST

3131 DOLIST LDA #1:STA 766 3140 CPX #0ihigh byte zero?

Modifying LADS: Special Notes on Atari and Apple LADS

3150 3 160 3 1 7S 3 180 3190 3200 3 210 3220 3230 3 24 0 3250 3260 3270 3280 3290 330 0

3 310

33 20 3330

3340 3 350 3360 3 37 0

3 3 8 0 339fi 340 0

34 10 3420

3 4 30

>4 4 0

3450

>460 '■470 :480

;490 ; s 0 e ;si!? ;520

f .530

;540

'.550

5560

357 0

3580

BEQ LOLST;if bo. skip primary LDA #0;for primary pass, list STA LEN RELIST LDY #0 PRLIST LDA (PTR),Y JSR PRINT;print a character ST

exit on error

pass fully

LDA

BMI

INY

CPY

BNE

INC

DEX

BMI

BNE

LOLST

nd ar y

OUTLIST

LEN

PRLIST

PTR + 1

primary pass completed?

OUTLIST; i f so, do secondary

PRLIST; if not, continue

LDA L L E N ; n o w list remainder (seco

p a 5 s )

pass

go back to R

STA LEN

JMP RELIST; conti nue OUTLIST LDA #0:STA 766:RTS; ead y

DOS JMP (10);DOS Vector

FINDLINE LDA #<TEXTBAS:start at top of

pr ogr am

STA PTR;initialize pointer

LDA #>TEXTBAS;same for high bytes

STA PTR+1

LDA #0

STA FOUNDFLAG;set foundflag to affirmat

i ve

TAY

LEQ TYA CLC ADC STA STA STA LDA ADC STA STA STA ; ch SEC

STY YSAVE;preserve Y ;point to first byte in line

PTR

TEMP;so we can convert line BEGFTR;save start of line ENDPTR

PTR+1;same for high byte #0

TEMP+1 BEGPTR+1 E N D P T R + 1 eck to see if at end

Modifying LADS: Special Notes on Atari and Apple LADS

3590 LDA BEBPTR

3600 SBC TEXEND

36 10 STA TMP

3620 LDA BEGPTR+1

3630 SBC TEXEND+1

3640 ORA TMP

3650 BCC NOTEND

3660 JMP NOTFOUND2

3670 NOTEND JSP. VALDEC

3680 SEC;see if line number matches

3690 LDA RESULT

3700 SBC LNUM

3710 STA TMP

3720 LDA RESULT+1

3730 SBC LNUM+1

3740 ORA TMP

3750 BEQ FOUNDLINEjif match, line found

3760 BCS NOTFOUND

3770 ; n o match at all, so continue search

3780 NEXTLINE JSR EOL;skip to end of line

3790 INY;skip over eol

3800 BNE NOADJ2

3810 INC PTR+1

3820 NOADJ2 JMP LEO;continue search

3830 FOUNDLINE DEC FOUNDFLAG;set to found (a

fter INC in N0TF0UND2)

3840 NOTFOUND JSR EOL;skip past end of line

3850 CLC;store at ending address

3860 TYA

3870 ADC PTR

3880 STA ENDPTR

3890 LDA #0

3900 ADC PTR+1

3910 STA ENDPTR+1

3920 N0TF0UND2 INC F0UNDFLA6;if 255, then 0

(found), else 1 (not found)

3930 SEC;get size of line

3940 LDA ENDPTR

3950 SBC BEBPTR

3960 STA LINESIZE;put it in LINESIZE

3970 LDA ENDPTR+1

3980 SBC BEGPTR+1

3990 STA LINESIZE+1

4000 INC LINESIZE

4010 BNE NOINC3

4020 INC LINESIZE+1

4030 N0INC3 RTS

4040 ;skip past end of line

4050 EOL LDY YSAVE;rest ore Y

Modifying LADS: Special Notes on Atari and Apple LADS

4060 SRCHEND LDA <PTR),Y;get character

4070 CMP #155

4080 BEQ ENDLINE;if zero (EOL)

4090 INY;bump up pointer

4100 BNE SRCHEND;zero?

4110 INC PTR + l;ne>:t block

4120 NOADJ JMP SRCHEND;end o-f line?

4130 ENDLINE RTS

4140 ;

4150 ; Print message

4160 PRMSG STA PTR;prepare pointer

4170 STY PTR+1

4 180 LDY #0

4190 PRLOOP LDA <PTR>,Y;get msg char

4200 BEQ OUTMSG;zero (end o-f message)

4210 JSR PRINT;else print char

4220 INY;continue loop

4230 BNE PRLOOP

4240 OUTMSG RTS

4250 ;

4260 jFINDLINE has initialized BEGPTR, ENDPT

R, and LINESIZE

4270 DELETE LDA ENDPTRsmove FROM Cend o-f lin

e+1 1

4280 CLC

4290 ADC #1

4300 STA FROML

4310 LDA ENDPTR+1

4320 ADC #0

4330 STA FROMH

4340 LDA BEGPTR;to beginning o-f line

4350 STA DESTL

4360 LDA BEGPTR+1

4370 STA DESTH

4330 SEC: length of move is TEXEND-ENDPTR

4390 LDA TEXEND

4400 SBC ENDPTR

4410 STA LLEN

4420 LDA TEXEND+1

4430 SBC ENDPTR+1

4440 BCS ZLAST

4450 LDA TEXEND

4460 BEQ N0DEC2

4470 DEC TEXEND+1

4480 N0DEC2 DEC TEXEND

4490 JMP NOMOV

4500 ZLAST STA HLEN

4510 ORA LLEN

4520 BEQ SKIPDEL; nothi nq to move!

Modifying LADS: Special Notes on Atari and Apple LADS

4530 JSR LIMDVE

4540 NOMDV SEC

4550 LDA TEXEND; subtract size of deleted lin

e -from program end

4560 SBC LINESIZE

4570 STA TEXEND

4580 LDA TEXEND+1

4590 SBC LINESIZE+1

4600 STA TEXEND+1

4610 SKIPDEL RTS;delete done!

4620 ;

4630 INSERT LDA BEGPTR; insert gap at -found 1

ine position

4640 STA PTR;also set pointer

4650 STA FROML;move From BESPTR

4660 SEC

4670 ADC INLEN;to BEGPTR+INLEN+1

4680 STA DESTL

4690 LDA BEGPTR+1

4700 STA PTR+l:same for high

4710 STA FROMH

4720 ADC #0

4730 STA DESTH

4740 SEC;# of bytes to move is

4750 LDA TEXEND; (TEXEND-BEGPTR)+1

4760 SBC BEGPTR

4770 STA LLEN

4780 LDA TEXEND+1

4790 SBC BEGPTR+1

4800 STA HLEN

4810 BCS NOTLAST

4820 LDA TEXEND

4830 BNE NODEC

4840 DEC TEXEND+1

4850 NODEC DEC TEXEND

4860 JMP INSEXIT

4870 NOTLAST ORA LLEN

4880 BEQ INSEXIT;nothing to insert!

4890 N0INC2 JSR DMOVE;do insert

4900 INSEXIT SEC;add length of line added

4910 LDA TEXEND;to end of te>:t pointer

4920 ADC INLEN

4921 STA TEXEND 4940 LDA TEXEND+1 4950 ADC #0

4960 STA TEXEND+1

4970 LDY #0;gap ready, put in line

4980 INSLOOP LDA BABUF,Y

4990 STA (PTR) ,Y

Modifying LADS: Special Notes on Atari and Apple LADS

5000 INY

5010 CPY INLEN

5020 BCC INSLOOP

5030 BEQ INSLOOP

5040 RTS;insert done!

5050 CLOSEIT LDA FNUM

5060 BEQ NOCLOSE

5070 JSR CLOSE

5080 NOCLOSE JSR CLRCHN

5090 RTS

5100 ERRPRINT LDA ST

5110 STA TMP

5120 JSR CLALL

5130 LDA #<ERRMSG

5140 LDY #>ERRMSG

5150 JSR PRMSG

5160 LDX TMP

5170 LDA #0

5180 JSR OUTNUM

5190 JSR PRNTCR

5200 RTS

5210 PMSG .BYTE 155

5220 .BYTE "LADS Ready."

5230 .BYTE 155 0

5240 SYNMSB .BYTE 253

5250 .BYTE "Syntax Error"

5260 .BYTE 155 0

5270 ERRMSG .BYTE 253

5280 .BYTE "Error - "

5290 .BYTE 0

500 BRKMSG .BYTE "BRK from "

510 .BYTE 0

5320 ;

530 GETNUM STA $F2

540 INC *F2

5350 LDA #<BABUF; point to line buffer

5360 STA *F3

570 LDA #>BABUF

5380 STA *F4joffset should be in tf2

r.90 JSR *D800; convert ASCII to -floating poi nt

5400 BCS NUMERR

5410 JSR $D9D2; f1 oatinq point to integer

5420 LDA tF2;store pointer to first non— nume

r al

5430 STA INDEX

5440 RTS

5450 NUMERR LDA #0;clear result

5460 STA $D4

Modifying LADS: Special Notes on Atari and Apple LADS

5470 STA tD5

5480 RTS

5490 GETLNUM JSR GETNUM;Get number from BABU

F +(a c c u m u 1 ator + 1 ) 5500 LDA *D4:put it in LNUH 5510 STA LNUM

5.52

0 LDA SB!

5530 STA LNUM+1

5540 JSR FI NDLINE; find the line

5550 RTS

5560 LIST JSR GLIST

5570 JMP PROMPT

5580 GLIST LDA ARGPOS;Any arguments?

5590 CMP INLEN;not i-f argpo5 is at end of li

ne 5600 BNE YESARG 5610 JMP MLIST;so list all 5620 YESARG JSR GETLNUM;get first numeric ^r

qument 5630 LDA BEBPTR; list from beginning of first line

SAVBES;save beginning pointer

BEGPTR+1

SAVBEG+1

ENDPTR;save end of first line

SAUEND

ENDPTR+1

SAVEND+1

5710 LDA INDEX;point to second argument 5720 CMP I N L E N ; i f equal, no second argument 5730 BNE YESARG2 5740 LDA FOUNDFLAG;no second arg, so check f

or legal line 5750 BNE NOLIST;line wasn't found, so don't

list it 5760 LDA SAVEND;restore end of line 5770 STA ENDPTR 5780 LDA SAVEND+1 5790 STA ENDPTR+1 5800 JMP 0VER2;and skip 5810 YESARG2 JSR GETLNUM;get second line num

ber 5820 0VER2 LDA SAVBEG 5830 STA PTR 5840 LDA SAVBEG+1 5850 STA PTR+1

5860 SEC;calculate length 5S70 LDA ENDPTR 5880 SBC PTR

Modifying LADS: Special Notes on Atari and Apple LADS

5890 STA LLEN

5900 LDA ENDPTR+1

5910 SBC PTR+1

5920 STA HLEN

5930 BCS 60LIST;if second # < first*, don't 1 i st

5940 NOLIST RTS

5941 GOLIST LDA FOUNDFLAG:BNE NOINCH 5950 INC LLEN

5960 BNE NOINCH

5970 INC HLEN

5980 NOINCH JMP INLIST

5990 ;

6000 OPENFILE CLC

60 10 LDA ARGPOS

6020 ADC #<BABUF

6030 STA FNAMEPTR; poi nt to filename

6040 LDA #0

6050 ADC #>BABUF

6060 STA FNAMEPTR+1

6070 LDY ARGPOS; find end of filename

6080 GETFNAME LDA BABUF.Y

6090 CMP #155;end of line?

6100 BEQ ENDFNAME;if so, exit loop

6110 CMP #44;end of filename?

6120 BEQ ENDFNAME;also legal

6 130 I NY

6140 BNE GETFNAME;if no delimiter found...

6150 JMP SYNERR;it's a syntax error

6160 ENDFNAME TYA;convert Y pointer to lengt

h

6 170 SEC

6180 SBC ARGPOS; Y-argpos

6190 STY ARGPOS;reset argpos for list

6200 STA FNAMELEN;fi1 ename length

6210 LDA #7;CLOSE #7

6220 STA FNUM

6230 JSR CLOSE

6240 LDA #0;OPEN #7, n, 0. fi 1 ename

6250 STA FSECOND

6260 JSR OPENido open

627 0 LDX STuhecl: for error

6280 BMI ERRABORT;yes, error

6290 RTS

6300 ERRABORT PLA;disk error, so abort

6310 PLA

6320 JSR ERRPRINT

6330 JMP PROMPT

6340 SAVE LDA #8;8 means output

Modifying LADS: Special Notes on Atari and Apple LADS

6350 STA FDEV

6360 JSR OFENFILE;open the file

6370 LDX FNUM;all PRINTs go

63S0 JSR CHKOUT;to file

6390 JSR GLISTjsend out listing

6400 JSR CLOSE IT;close file

6410 JMP PROMPT

6420 MERGE LDft #4;4 for input

6430 STA FDEV

6440 JSR OPENFILE;open it

6450 LDX FNUM;all input comes from this file

6460 JSR CHKIN

6470 JMP ENTERsfile will be closed automatic

ally

6480 LADS LDA ARGPOS;Any argument?

6490 CMP INLEN

6500 BIME NOTMEM;if argpos< >in1 en, then there

is,so don't change RAMFLAG

6510 INC RAMFLAG

6520 NOTMEM JMP START

6530 SYS LDA ftRGPOS; locate number

6540 JSR GETNUM;get it

6550 LDA $D4;put address directly

6560 STA JUMPVEC+1;i nto code

6570 LDA $D5;self-modifying!

65S0 STA JUMPVEC+2

6590 JUMPVEC JSR $FFFF;this address will be

changed by above

6600 JMP PROMPT

6610 LOAD JSR PLOAD;do load

6620 JMP PROMPT;done

6630 PLOAD LDA #4;4 for read

6640 STA FDEV

6650 JSR OPENFILE;open file

6660 AFTEROPEN LDA FNOM;all input comes from

this file

6670 JSR X16

6680 LDA #<TEXTBAS

6690 STA ICBADR, X

6700 LDA #>TEXTBAS

6710 STA ICBADR+1,X

6720 LDA #0

6730 STA ICBLEN,X

6740 LDA #*50

6750 STA ICBLEN+l.X

6760 LDA #7

6770 STA ICCOM,X

6780 JSR CALLCIO

6790 LDA FNUM

Modifying LADS: Special Notes on Atari and Apple LADS

6800 JSR X16

6310 CLC:add buffer length to get ending add

r 6820 LDA ICBLEN,X 6830 ADC #<TEXTBAS 6840 STA TEXEND; update end 6850 LDA 1CBLEN+1.X 6860 ADC #>TEXTBAS 6870 STA TEXEMD+1 6880 LDA ST

6890 CMP #136-,end of file? 69 0 0 BEQ NOPRERRjif so, don't print an error

messag e 6910 JSR ERRPRINT 6920 JMP PROMPT

6930 NOPRERR JSR CLOSE IT;c1ose down file 6940 RTS;end of load 6950 BREAK CLI:LDA #<BRKMSG:LDY #>BRKMSG:JSR

PRMSG 6960 PLA:PLA:PLA:SEC:SBC #2:TAX:PLA:SBC #0:J

SR OUTNUM 6965 LDX #255:TXS:JSR PRNTCR:JMP EDIT 6970 .FILE D:TABLES.SRC

Atari Machine Language Programming

There is a lot to be learned from the Atari LADS source code. Both the assembler and the editor are complex, powerful programs. You might find uses in your own programming for such general-purpose routines as Valdec, UMOVE, and DMOVE. You can add functions to the editor such as search and replace. Or you could simply bypass the editor altogether, creating LADS-compatible source files using an ordinary word processor (and thus have access to the search and replace and other features of the word processor program).

Since maps are invaluable in sophisticated ML programming, you might want to purchase Mapping the Atari (COMPUTE! Books, 1983).

Special Apple Notes

The Apple version of LADS works the same as the Commodore 64 version with only slight modifications. The Apple doesn't have the convenience of Kernal routines to access DOS, so routines had to be written which could directly access the DOS file manager routines. This required extensive changes to the Openl subprogram, which are discussed below.

Modifying LADS: Special Notes on Atari and Apple LADS

Also, because the Applesoft tokenize routine takes the spaces out of the text, it was necessary to put a wedge into Apple's CHRGET routine to intercept the BASIC tokenize routine. And the wedge includes a routine that puts the filename of the program you want to assemble to the top of the screen where LADS expects to find it.

Apple Disk Access

The Apple DOS file manager is the part of DOS that handles all file input and output to the disk. It calls RWTS (Read/Write to Track/Sector) and is called from the command interpreter. The command interpreter sends control bytes to the file manager through the file manager parameter list. You can access the file manager directly by sending it the parameters it requires.

To get the address of the parameter field you JSR to $03DC. This loads the Accumulator with the high byte and the Y Register with the low byte of the parameter field. You can then store these to a zero page location for easy transfer of the parameters.

Table 11-1. Apple File Manager Parameter List

Parameter

OPEN

CLOSE

DELETE

CATALOG

LOCK

UNLOCK

RENAME

INIT

VERIFY

Modifying LADS: Special Notes on Atari and Apple LADS

Parameter

1 2 3/4 5/(

READ 1 Byte

READ Range

POSITION and

READ 1 Byte

POSITION and

READ Range

WRITE 1 Byte

WRITE Range

POSITION and

WRITE 1 Byte

POSITION and

WRITE Range

POSITION

Note: The numbers in the leftmost column represent the opcode; the numbers across the top of this chart represent byte positions relative to the start of the parameter list. Asterisks signify that a byte is required for the operation listed. A blank space means that this parameter can be ignored. Nevertheless, the byte positions must be maintained. For example, to DELETE, you do not need to worry about the second, third, or fourth bytes—anything can be in them— but they must exist. The first byte must contain a five, and the fifth through the eighteenth bytes must be set up as described below.

The parameters are expained in sections. The first section tells you about all the opcodes except for the read, write, and positions opcodes, because they are slightly different from the rest. The second section tells you about the read, write, and position opcodes; the third, about the last set of parameters that is common to all opcodes.

The first byte of the parameter field is the opcode type. This parameter can be in the range of 1 to 12.

The second parameter is used only with the INIT opcodes. If you are using a 48K Apple, the correct value for this parameter is 157.

The third and fourth parameters are used with the OPEN and RENAME opcodes. Together they hold the record length of a random access file. If you are not using a random access file, you should have a zero in both of these locations. With the RENAME opcode, these bytes hold the address of the new name.

Modifying LADS: Special Notes on Atari and Apple LADS

The fifth byte holds the volume number. The sixth byte holds the drive number. The seventh byte holds the slot number. The eighth byte holds the file type.

The ninth and tenth bytes hold the address of the filename. The filename must be stored in the address pointed to by these bytes. It must be padded with spaces.

This section explains the read, write, and position opcodes.

The first byte holds the opcode. The second byte holds the subcode.

The next four bytes are used only when you require a position command. The third and fourth bytes hold the record number. The fifth and sixth bytes hold the byte offset. To reposition the pointer in an open file, you can use these bytes to calculate a new position. The new position is equal to the length of the file specified in the open opcode times the record number plus the byte offset.

The seventh and eighth bytes hold the length of the range of bytes. This is used only when reading or writing a range.

When reading or writing a range of bytes, the ninth and tenth bytes hold the start address of the range. If you are reading or writing only one byte, then the ninth byte holds the byte you read or the byte you are going to write.

The following are parameters for all the opcodes.

The eleventh byte is the error byte. It should be checked each time after you access the file manager. The errors are as follows:

0: NO ERROR

2: INVALID OPCODE

3: INVALID SUBCODE

4: WRITE PROTECTED

5: END OF DATA

6: FILE NOT FOUND

7: VOLUME MISMATCH

8: I/O ERROR

9: DISK FULL

10: FILE LOCKED

The twelfth byte is unused. The thirteenth and fourteenth bytes are used for the address of the work area buffer. This is a 45-byte buffer in one of the DOS buffers.

330

-

Modifying LADS: Special Notes on Atari and Apple LADS

The fifteenth and sixteenth bytes hold the address of the track/sector list sector buffer. This is a 256-byte buffer in one of the DOS buffers.

The seventeenth and eighteenth bytes hold the address of the data sector buffer. This is another 256-byte buffer in one of the DOS buffers.

Once you have sent the correct parameters, you can call the file manager by a JSR to $03D6. You must specify if you want to create new file on disk if the one you are accessing doesn't exist. This is done by loading the X Register with a 0. If you don't want to create a new file, you can load the X Register with a 1. If you don't want to create a new file and you try to access a file that doesn't exist, you will receive an error number 6 in byte 11 of the parameter field.

Apple LADS uses the routines in the file manager that read or write one byte from or to the disk at a time. The general routine to transfer the parameters from Tables to the file manager can be found between lines 810 and 920 in the Openl listing. This is called from the individual subroutines for opening, closing, reading, and writing. The OPEN routines require a filename. Lines 580-800 handle the transfer of the filename from the filename buffer to the specific buffer.

There is also a check to see whether a file about to be opened has been opened previously. This was needed because you cannot close a file unless it was previously opened. This is handled in the close routine (370-570).

The PRINT routine handles all output, and the CHARIN routine handles all input. There is one input and one output channel, and all input and output must be handled through a channel. The bytes which govern this event are set in the CHKIN and CHKOUT routine. The CHKIN routine (930-940) sets all input to come from that file. The CHKOUT routine (950-1030) sets all output to go to that file. The PRINT routine (1170-1430) and the CHARIN routine (1040-1160) check to see what channel is currently open, then go to that routine.

The BASIC wedge (1700-2530) handles the tokenizing of the BASIC text. It checks to see if the text pointer is at $200 (the input buffer). If not, it goes to the normal GETCHR routine. Otherwise, it checks to see if the first character is a number. If so, it goes to the insert line routine, and if not, it checks for the characters ASM. If that is found, the wedge concludes

Modifying LADS: Special Notes on Atari and Apple LADS

its work by putting the filename at the top of the screen and jumping to the start of LADS.

The insert line routine gets the line number, then jumps to the Apple tokenize routine, which loads the Y Register with the length of the line plus six and then jumps to the normal line insert and tokenize routine.

The last subroutine in Openl is the first thing that is called when you BRUN LADS. It initializes the wedge and sets HIMEM to the start of LADS.

1 -1

1

1 1 1

1 i

How to Use LADS

Here is a step-by-step explanation of how to assemble machine language programs using the LADS assembler. As you familiarize yourself with its features and practice using it, you will likely discover things about the assembler which you 11 want to change or features you'll want to add. For example, if you find yourself frequently using an impossible addressing mode like LDY (15,Y), you might want to insert an error trap for that into LADS source code. Chapter 11, "Modifying LADS," shows you how these customizations can be accomplished. But here is a description of the features which are built into LADS.

Apple and Atari Versions

For the most part, the commands and features of LADS are the same for all versions: Apple, Atari, and Commodore. A few differences are discussed at the end of the general instructions for all versions of LADS. No matter which computer you use, you should read the body of this chapter to understand how to get the most out of LADS. Then, if you use an Atari or an Apple, you can read the special notes at the end of this appendix which explain some minor variations applicable to those computers.

General Instructions for Using LADS

LADS assembles from source files. They are particularly easy and convenient to create; just turn on your computer and pretend you're writing a BASIC program. (To create source files for the Atari, see "Special Atari Notes" below.) Commodore and Apple LADS work with source files created exactly the way you would write a BASIC program. Here's an example:

10 * = $0360

15 .S

20 LDA #22:LDY #0

30 STA $1500,Y

40 .END TEST

Use line numbers, colons, and whatever programmer's aids (Toolkit, BASIC Aid, POWER, automatic line numbering, etc.) you ordinarily use to write BASIC itself.

Appendix A: How to Use LADS

After you've typed in a program, save to disk in the normal way. (Tape drive users: See special "Note to Tape Users" at the end of this appendix.) Notice line 10 in the example above. The first line of any LADS source file must provide the starting address, the address where you want the ML program to begin in the computer's memory. You signify this with the *= symbol, which means "program counter equals." When LADS sees * = , it sets the Program Counter to the number following the equals sign. Remember that there must be a space between the = and the starting address.

The last line of each LADS source file must contain either the .END pseudo-op or the .FILE pseudo-op. Both of them link source files together in case you want to chain several files into one large ML program. However, .FILE names the next linked source file in the chain whereas .END always specifies the first source file of the chain. If there is only one file (as in our example above), you still must end it with .END and give its name as the first file. More about this shortly.

Also notice that you can use either decimal or hexadecimal numbers interchangeably in LADS. Lines 10 and 30 contain hex; line 20 has decimal numbers.

After you've saved the source code to disk, you can assemble it by loading LADS and then typing the name of the source file in the upper left-hand corner of the screen. (The Atari version differs here as well.) Let's go through the process step by step. Type in the little source program above as if you were writing a BASIC program. SAVE it by typing:

SAVE "TEST",8 Then LOAD "LADS",8,1

Type NEW

Clear the screen and type in the source file's name in the upper left-hand corner:

TEST

Then cursor down a line or two and type SYS 11000 and hit the RETURN key. That will activate LADS on the Commodore 64, VIC-20, and 8032 PET/CBM. See the special notes below for using the Atari and Apple versions of LADS.

You will see the assembler create the object code, the bytes which go into memory and comprise the ML program.

Note: Be sure to remember that every source code program must end with the .END NAME pseudo-op. In our example, we

Appendix A: How to Use LADS

concluded with .END TEST because TEST is the name of the only file in this source code. Also notice that you do not use quotes with these filenames.

To review: Every source code program must contain the starting address in the first line (for example, 10 *= $0800) and must list the filename on the last line (for example, 500 .END SCREENPROG). If you chain several source code programs together using the .FILE pseudo-op, you end only the final program in the chain with the .END pseudo-op. These two rules will become clearer in a minute when we discuss the .END and .FILE pseudo-ops.

Features

There are a number of pseudo-ops (direct instructions to the assembler) available in LADS. The .S in line 15 is such an instruction. It tells LADS to print the results of an assembly to the screen. If you add the following lines to our test program, you will cause the listing to be in decimal instead of hex and cause LADS to save the object code (the runnable ML program) to a disk file called T.OBJ.

10 *= $0360

11 .NH

12 .D T.OBJ

20 LDA #22:LDY #0 30 STA $1500,Y 40 .END TEST

The pseudo-op .NH means no hex (causing the listing to change from hex to decimal), and .D means create a disk file containing the ML program which results from the assembly process.

You can add REM-like comments by using a semicolon. And you can turn the screen listing off with ,NS, anytime. Turn it on or off as much as you want:

10 *= $0360

11 .NH

12 .D T.OBJECTPROGRAM 15 .NS

20 LDA #22:LDY #0; load A with 22, load Y with zero 30 STA $1500,Y 40 .END TEST

You turn on printer listings with .P and turn them off with .NP. However, for the .P pseudo-op to work, the .S

Appendix A: How to Use LADS

screen listings pseudo-op must also be turned on. In other words, you cannot have listings sent to the printer without also having them listed on the screen at the same time. To have the ML stored into memory during assembly, use .O and turn off these POKEs to memory with .NO.

The pseudo-ops which turn the printer on and off; direct object code to disk, screen, and RAM; or switch between hex or decimal printout can be switched on and off within your source code wherever convenient. For example, you can turn on your printer anywhere within the program by inserting .P and turn it off anywhere with .NP. Among other things, this would allow you to specify that only a particular section of a large program be printed out. This can come in very handy if you're working on a 5000-byte program: you would have a long wait if you had to print out the whole thing.

Always put pseudo-ops on a line by themselves. Any other programming code can be put on a line in any fashion (divided by colons: LDA 15:STA 27:INY), but pseudo-ops should be the only thing on their lines. (The .BYTE pseudo-op is an exception—it can be on a multiple-statement line.)

100 .P .S (wrong)

100 .P (right)

110 .S (right)

Here's a summary of the commands you can give LADS:

•P Turn on printer listing of object code (.S must

be activated).

.NP Turn off printer listing of object code.

.O Turn on POKEs to memory. Object code is

stored into RAM during assembly.

.NO Turn off POKEs to memory.

.D filename Open a file and store object code to disk during

assembly (use no quotes around filename).

.FILE filename Link one source file to the next in a chain so

that they will all assemble together as a single large source program (end the chain with .END pseudo-op).

.END filename Link the last source file to first source file in a

chain. If you are assembling from a single file, give its filename as the .END so the assembler knows where to go for the second pass. Any source code must have .END as the last line in the program, whether the source code is con-

Appendix A: How to Use LADS

tained within a single disk file or spread across a multiple-file chain.

.S Turn on screen listing during assembly (re-

quired if you desire a hardcopy listing from a printer using the .P pseudo-op).

.NS Turn off screen listing during assembly.

,H Turn on hexadecimal output for screen or

printer listing.

.NH Turn off hexadecimal output for screen or

printer listing. (As a result, the listings are in

decimal.)

Set program counter to new address.

* =

A Stable Buffer

The pseudo-op *= is mainly useful when you want to create data tables. The subprogram Tables in LADS source code is an example. (A subprogram is one of the source code files which, when linked together, form an entire ML program.) You might want to create an ML program and locate its tables, equates, buffers, and messages at the high end of the ML program the way LADS does with its Tables subprogram. Since you don't know what the highest RAM address will be while you're writing the program, you can set *= to some address perhaps 4K above the starting address. This gives you space to write the program below the tables. The advantage of stable tables is that you can easily PEEK them and this greatly assists debugging. You'll always know exactly where buffers and variables are going to end up in memory after an assembly—regardless of the changes you make in the program.

Here's an example. Suppose you write:

10 *= $5000

20 STA BUFFER

30 *= $6000

40 BUFFER .BYTE 00000000000000

50 .END BUFFEREXAMPLE

This creates an ML instruction (STA buffer) at address $5000 (the starting address of this particular ML program), but places the buffer itself at $6000. When you add additional instructions after STA buffer, the location of the buffer itself will remain at address $6000. This means that you can write an entire program without having to worry that the location of the buffer is changing each time you add new instructions,

Appendix A: How to Use LADS

new code. It's high enough so that it remains stable at $6000,

and you can debug the program more easily. You can always

check if something is being correctly sent into the buffer by

just looking at $6000. —

This fragment of code illustrates two other features of LADS. You can use the pseudo-op .BYTE to set aside some space in memory (the zeros above just make space to hold ——

other things in a "buffer" during the execution of an ML program). You can also use .BYTE to define specific numbers in memory:

.BYTE 65 66 67 68

This would put these numbers (you must always use decimal numbers with this pseudo-op) into memory at the location of the .BYTE instruction. An easy way to create messages that you want to print to the screen is to use the .BYTE pseudo-op with quotes: 500 FIRSTLETTERS .BYTE "ABCD":.BYTE 0

Then, if you wanted to print this message, you could write:

2 *= $0360

5 LDY #0

10 LOOP LDA FIRSTLETTERS,Y

20 BEQ ENDMESSAGE

30 STA $0400,Y; location of screen RAM on Commodore 64

40 INY

50 JMP LOOP

60 ENDMESSAGE RTS; finished printout

500 FIRSTLETTERS .BYTE "ABCD:.BYTE 0

900 .END MESSAGETEST

Note that using the second set of quotes is optional «

with the .BYTE pseudo-op: You can use either .BYTE "ABCD:.BYTE 0 or .BYTE "ABCD":.BYTE 0. To POKE numbers instead of characters, just leave out the quotes: .BYTE 10 —— 15 75. And since these numeric values are being POKEd directly into bytes in memory, they cannot be larger than 255.

Labels

With LADS, or with other assemblers that permit labels, you need not refer to locations in memory or numeric values by using numbers. You can use labels.

In the example above, line 10 starts off with the word

Appendix A: How to Use LADS

LOOP. This means that you can use the word LOOP later on to refer to that location (see line 50). That's quite a convenience: The assembler remembers where the word LOOP is used and you need not refer to an actual memory address; you can refer to the label instead. Throughout this book, this kind of label is called a PC-type (for Program Counter) or address-type label.

The other type of label is defined is with an assembly convention called an equate (an equals sign). This is quite similar to the way that BASIC allows you to assign value to words—it's called "assigning variables" when you do it in BASIC. In ML, the = pseudo-op works pretty much the way the = sign does in BASIC. Here's an example:

5 *= $0360

10 SCREEN = $0400; the location of the 1st byte in RAM of the 64 screen

20 HEARTSYMBOL = 83; the heart figure

30;

40 START LDA HEARTSYMBOL; notice "START" (an address-type label)

50 STA SCREEN

60 RTS

Line 10 assigns the number $0400 (1024 decimal) to the word SCREEN. Anytime thereafter that you use the word SCREEN, LADS will substitute $0400 when it assembles your ML program. Line 20 "equates" the word HEARTSYMBOL to the number 83. So, when you LDA HEARTSYMBOL in line 40, the assembler will put an 83 into your program. (Notice that, like BASIC, LADS requires that equate labels be a single word. You couldn't use HEART SYMBOL, since that's two words.)

Line 30 is just a REMark. The semicolon tells the assembler that what follows on that line is to be ignored. Nevertheless, blank lines or graphic dividers like line 30 can help to visually separate subroutines, tables, and equates from your actual ML program. In this case, we've used line 30 to separate the section of the program which defines labels (lines 10-20) from the program proper (lines 40-60). All this makes it easier to read and understand your source code later.

Appendix A: How to Use LADS

Automatic Math

There are times when you will want to have LADS do addition for you. That's where the + pseudo-op comes in. If you write "label+ 1" you will add 1 to the value of the label. Here's how it works:

10 *= 864

20 MEMTOP = $34; top-of-memory pointer for 8032 PET.

30;

40 LDA #0:STA MEMTOP:LDA #$50:STA MEMTOP + 1

Here we are putting a new location into the top-of-memory pointer which the computer uses to decide where it can store things. (Doing that could protect an ML program which resides above the address stored in this pointer.) Like all pointers, it uses two bytes. If we want to store $5000 into this pointer, we store the lower half (the least significant byte) into MEMTOP. We'll want to put the number $50 into the most significant byte of the pointer—but we don't want to waste time making a new label. It's just one higher in memory than MEMTOP. Hence, MEMTOP +1.

You'll also want to use the + pseudo-op command in constructions like this:

10 *= 864

15 SCREEN = $0400

17;

20 LDA #32; the blank character

30 LDA #0

40 START STA SCREEN,Y

50 STA SCREEN + 256,Y

60 STA SCREEN+ 512,Y

70 STA SCREEN + 768,Y

80INY

90 BNE START

This is the fastest way to fill memory with a given byte. In this case we're clearing out the screen RAM by filling it with blanks. But it's easy to indicate multiples of 256 by just adding them to the label SCREEN.

A similar pseudo-op command is the #<. This refers to the least significant byte of a label. For example:

10 *= $0360

20 SCREEN = $8011

25 SCREENPOINTER = $FB

30 ;

Appendix A: How to Use LADS

40 LDA #<SCREEN; LSB (least significant byte of the label

SCREEN, $11) 50 STA SCREENPOINTER

You'll find this technique used several times in the LADS source code. It puts the LSB (least signficant byte) or the MSB (most signficant byte) of a label into the LSB or MSB of a pointer. In the example above, we want to set up a pointer that will hold the address of the screen RAM. The pointer is called SCREENPOINTER and we want to put $11 (the LSB of SCREEN) into SCREENPOINTER. So, we extract the LSB of SCREEN in line 40 by using # combined with the less-than symbol. We would complete the job with the greater-than symbol to fetch the MSB: 60 LDA #>SCREEN. Notice that these symbols must be attached to the label; no space is allowed. For example, LDA #> SCREEN would create problems. This LSB or MSB extraction from a label is something you'll need to do from time to time. The #< and #> pseudo-ops do it for you.

Chained Files

It is sometimes convenient to create several source code subprograms, to break the ML program source code into several pieces. LADS source code is divided into a number of program files: Array, Equate, Math, Pseudo, etc. This way, you don't need to load the entire source code in the computer's memory when you just want to work on a particular part of it. It also allows you to assemble source code far larger than could fit into available RAM.

In the last line of each subprogram you want to link, you put the linking pseudo-op .FILE NAME (use no quotes) to tell the assembler which subprogram to assemble next. Subprograms, chained together in this fashion, will be treated as if they were one large program. The final subprogram in the chain ends with the special pseudo-op .END NAME, and this time the name is the filename of the first of the subprograms, the subprogram which begins the chain. It's like stringing pearls and then, at the end, tying thread so that the last pearl is next to the first, to form a necklace.

Remember that you always need to include the .END pseudo-op, even if you are assembling from a single, unlinked source code file. In such a case (where you're working with a solo file), you don't need the linking .FILE pseudo-op. Instead,

Appendix A: How to Use LADS

refer the file to itself with .END NAME where you list the solo file's name. Here's an illustration of how three subprograms would be linked to form a complete program:

5 * = 864

10; "FIRST" first program in chain

20;its first line must contain the start address

30; —

40 LDA #20

50 STA $0400

60 .FILE SECOND

Then you save this subprogram to disk (it's handy to let the first remark line in each subprogram identify the subprogram's filename):

SAVE "FIRST",8

Next you create SECOND, the next link in the chain. But here, you use no starting address; you enter no *= since only one start address is needed for any program:

10 ; "SECOND"

20 INY:INX:DEY:DEX

30 .FILE THIRD

SAVE"SECOND",8

Now write the final subprogram, ending it with the clasp pseudo-op .END NAME which links this last subprogram to the first:

10 ; "THIRD"

20 LDA #65:STA $0400

30 .END FIRST

SAVE "THIRD",8

When you want to assemble this chain, just type FIRST in the upper left-hand corner of the screen, SYS to LADS, and it will assemble the entire chain. ___.

If you want the object code (the finished ML program) stored in the computer's memory during the LADS assembly, add this line to FIRST above:

35 .O

If you want to save the object code as an ML program on disk that can be later loaded into the computer and run, add this line to FIRST:

Appendix A: How to Use LADS

36 .D PROGRAMNAME

When LADS is finished assembling, there will be an ML program on disk called PROGRAMNAME. You can load it and SYS 864 (that was the start address we gave this program), and the newly assembled ML program will execute.

One additional pseudo-op is the #". It is sometimes useful when you want to load the Accumulator with a particular ASCII character and don't offhand recall the numerical value. The letter A is 65 in the ASCII code. If you LDA #65:STA SCREEN, you would store the letter A to the screen. But, for convenience, you can LDA #"A:STA SCREEN. You can, in other words, use the #" followed by the character itself rather than by its ASCII code number.

Rules for LADS

Here are the rules you need to follow when writing ML for LADS to assemble:

1. In general, all equate labels (labels using an equals sign) should be defined at the start of your program. While this isn't absolutely necessary for labels with numbers above 255 (see SCREEN in the example below), it is the best programming practice. It makes it easier for you to modify your programs and simplifies debugging. LADS itself locates all its equate labels in the subprogram Defs, the first subprogram in its chain of source code files.

What's more, it is necessary that any equate label with a value lower than 256 be defined before any ML mnemonics reference that label. So, to be on the safe side, just get into the habit of putting all equate labels at the very start of your programs:

10 *= 864

20 ARRAYPOINTER = $FB; (251 decimal), a zero page address

30 OTHERPOINTER = $FD; (253 decimal), another zero page

address 40 ■

50 LDY #0:LDA $41

60 STA ARRAYPOINTER,Y

70 SCREEN = $8000

Notice that it's permissible to define the label SCREEN anywhere in your program. It's not a zero page address. You do have to be careful, however, with zero page addresses (addresses lower than 255). So most ML programmers make it a

Appendix A: How to Use LADS

habit to define all their equates at the start of their source code.

2. Put only one pseudo-op on a line. Don't use a colon to put two pseudo-ops on a single line:

10 * = 864

20 .0:.NH (wrong)

30 .O (right)

40 .NH (right)

The main exception to this is the .BYTE pseudo-op. Sometimes it's useful to set up messages with a zero at their end to delimit them, to show that the message is complete. When you delimit messages with a zero, you don't need to know the length of the message; you just branch when you come upon a zero:

10 *= 864

20 SCREEN = $0364

30 ;

40 LDY #0

50 LOOP LDA MESSAGE,Y:BEQ END; loading a zero signals

end of message. 60 STA SCREEN,Y:INY: JMP LOOP; LADS ignores spaces after a

colon.

70 ; message area here

80 MESSAGE .BYTE "PRINT THIS ON SCREEN":.BYTE 0

Any embedded pseudo-ops like + or = or #> can be used on multiple-statement lines. The only pseudo-ops which should be on a line by themselves are the I/O (input/output) instructions which direct communication to disk, screen, or printer, like .P, .S, .D, .END, etc.

Generally, it's important that you space things correctly. If you wrote:

SCREEN= 864

LADS would think that your label was screen= instead of screen. So you need that space between the label and the equals sign. Likewise, you need to put a single space between labels, mnemonics, and arguments:

LOOP LDA MESSAGE

Running them together will confuse LADS:

Appendix A: How to Use LADS

LOOPLDA MESSAGE

and

LOOP LDAMESSAGE

are wrong.

It's fine to have leading spaces following a colon, however. LADS will ignore those (see line 60 above). Also, spaces within remarks are ignored. In fact, LADS ignores anything following a semicolon (see line 70). However, the semicolon should come after anything you want assembled. You couldn't rearrange line 50 above by putting the BEQ END after the remark message. It would be ignored because it followed the semicolon.

When using the text form of .BYTE, it's up to you whether you use a close quote:

50 MESSAGE .BYTE "PRINT THIS" (right) 60 MESSAGE .BYTE "PRINT THIS (also right)

3. The first character of any label must be a letter, not a number. LADS knows when it comes upon a label because a number starts with a number; a label starts with a letter of the alphabet:

10 *= 864 20 LABEL = 255 30 LDA LABEL 40 LDA 255

Lines 30 and 40 accomplish the same thing and are correctly written. It would confuse LADS, however, if you wrote:

20 5LABEL = 255 (wrong)

since the number 5 at the start of the word label would signal the assembler that it had come upon a number, not a label. You can use numbers anywhere else in a label name—just don't put a number at the start of the name. Also avoid using symbols like # < > * and other punctuation, shifted letters, or graphics symbols within labels. Stick with ordinary alphanumerics:

10 5LABEL (wrong) 20 LABEL15 (right) 30 *LABEL* (wrong)

4. Move the Program Counter forward, never backward. The *= pseudo-op should be used to make space in memory. If

Appendix A: How to Use LADS

you set the PC below its current address, you would be writing over previously assembled code:

10 *= 864

20 LDA #15

30 *= 900 (right)

10 * = 864 20 LDA #15

30 *= 864 (wrong, you'll assemble right over the LDA #15)

Special Note to Tape Drive Users

LADS will assemble source code from disk or RAM memory. It is possible to use the assembler with a tape drive, using the RAM memory-based version (see Chapter 11). Of course, disk users can also assemble from RAM if they choose. But tape users must.

There is a restriction when using a tape drive as the outboard memory device. You cannot link files together, forming a large, chained source code listing. The reason for this is that LADS, like all sophisticated assemblers, makes two passes through the source code. This means that tape containing the source code would have to be rewound at the end of the first pass.

It would be possible, of course, to have LADS pause at the end of pass 1, announce that it's time to rewind the tape (see Atari notes below), and then, when you press a key, start reading the source code from the start of the tape. But this causes a second problem: The object code cannot then be stored to tape. A tape drive cannot simultaneously read and write.

The best way to use LADS with a tape drive is to assemble from source code in RAM memory and to use the .O (store object code to RAM pseudo-op). Then, when the finished object code is in RAM, use a monitor program like "Tinymon" or "Micromon" to save it to tape. If you have access to a disk drive, you could construct a version of LADS which automatically directs object code to tape during assembly using the .D pseudo-op.

Special Atari Notes

The Atari version of LADs is a complete programming environment. Unlike the Commodore and Apple versions of LADS, where you use the BASIC program editor to write and

Appendix A: How to Use LADS

edit your source code, the Atari version has a special editor integrated into LADS itself. This is necessary because with Atari BASIC, you can only enter BASIC instructions. The line

10*= $0600

is just as illegal as

10 PRIMT "NAME":INPPUT A#

Both are coolly received with an error message. This syntax checking is fine when working with BASIC, but prevents the standard BASIC editor from accepting and storing LADS source code. Once the decision was made to create an entirely new source code editor, LADS became a self-contained package. The BASIC cartridge is neither needed nor especially desired. Since LADS takes over the Atari, DOS is the only other program in memory, freeing up all the RAM ordinarily used by BASIC.

One note: If you'd rather use a word processor or other text editor to enter and edit your source code, you can, as long as your editor will send out numbered statements, in ASCII, ending with 155's (ATASCII carriage returns). Most Atari word processors conform to this; it you're not sure, experiment with a short source code program. Be sure to end each source line with a carriage return. You can then load the file into the LADS editor or assemble directly from disk with the LADS D:filename command.

Entering LADS

The object code for Atari LADS is typed in with the Atari version of MLX, a machine language entry editor. See Appendix C for details. After you've typed it in, you can save LADS to disk under the filename AUTORUN.SYS. This will cause LADS to load and automatically run when you turn on (boot) your computer and disk drive. LADS as assembled requires at least 40K of memory. If you have access to a 40K Atari, you can reassemble the source code to almost any memory location you want (see "Programming Atari LADS" in Chapter 11).

If you didn't save LADS as AUTORUN.SYS, you need to load it from the DOS menu, then use menu selection M and run it at address 8000. If you bought the LADS source/object code disk, LADS will automatically load and run when you insert the disk and turn on your system. LADS will then print

Appendix A: How to Use LADS

its prompt, "LADS Ready." This indicates that LADS is ready to receive commands or source code.

Using the Editor

You enter your ML source code just as you do in BASIC. To start a new line, type a line number, then the text, followed by the RETURN key. To delete a line, type the line number by itself, then press RETURN. To insert a line between two existing lines, just give it a line number that falls between the two. For example, line 105 will end up between line 100 and 110.

The editor assumes that a line beginning with a line number should be stored as part of your source code. If your line starts with leading zeros, these leading zeros will be erased. As the editor reads the line you've entered, it converts lowercase to uppercase, and inverse video characters to normal ones. It will not convert characters within double quotes (SHIFT-2) or after a semicolon, which marks the start of a comment. This line:

0100 Ida #"a":jmp ($fffc); FFFC is the reset vector

would become:

100 LDA #"a":JMP ($FFFC); FFFC is the reset vector

If there is no line number, the editor assumes you've entered an editor command. Note that if a command has any parameters after it, the command must be followed by a space.

Atari Editor Commands LIST

LIST all by itself displays the entire source program. LIST 150 lists just line 150. LIST 110-160 shows all the lines between and including lines 110 through 160. If you want to list from a certain line number to the end of your program, just make the second line number very large, as in LIST 2000,9999. If you want to send a listing to the printer, use the SAVE command.

SAVE deviceifilename

SAVE works just like LIST, but sends the listing to the specified device with the given filename. To list the entire source code to the printer, use SAVE P:. Be sure to put a space between the command and the device. To LIST to cassette, use SAVE C:. When using disk, remember to use D:, for example, SAVE D:DEFS.SRC. We recommend that you do use an extender, such as .SRC (see .FILE below). Check the DOS man-

Appendix A: How to Use LADS

ual for examples of legal filenames. You can also save a portion of the program. SAVE P:,100,150 would list lines 100 to 150 to the printer.

LOAD device:filename

Load will replace any source code in memory with that read from the specified device. LOAD C: reads from tape, LOAD DDEFS.SRC or LOAD D2:INDISK.SRC from disk.

MERGE device:filename

Merge is used to combine two programs. MERGE works just like ENTER does in BASIC. Instead of the keyboard being used to accept text, the editor looks to the file for input. After all the lines have been entered, the editor restores keyboard control. MERGE does not just append one program to the other. If there is a line 150 in the program to be merged, it will replace line 150 in memory. Therefore, MERGE can replace selected lines, or add lines to the top or bottom of a program in memory. You can use SAVE to list to disk a part of a program, then use MERGE to add it to another program. You can have a whole disk full of commonly used routines, then use MERGE to combine the routines you need, speeding up the development of large ML programs.

DOS

If used with standard Atari DOS 2.OS, this command will load and run DUP.SYS, the DOS menu. Remember that DUP.SYS will erase any program in memory if MEM.SAV is not used. Now you can manipulate files and display the disk directory. The DOS command makes an indirect jump through the DOS vector, location $0A.

SYS address

Transfers control with a JSR to the decimal address following the SYS. Always put a space between SYS and the address. If the routine ends with a RTS, control will return to the LADS editor. If a BRK ($00) is encountered, the editor will also be reentered through the breakpoint, and the address where the BRK was found will be displayed.

LADS (optional device:filename)

Transfers control to the assembler. Although the editor merely manipulates text source code, it's as if all of LADS was just another editor command. When LADS takes control, the left margin is set to 0, to give a full 40-column width for printout.

Appendix A: How to Use LADS

The left margin reverts to 2 when the editor is reentered. If you give the filename, as in LADS D:DEFS.SRC, then LADS will assemble the given source code from disk. This is like Commodore LADS' default—assembling from disk. If you leave off the filename, LADS will behave as a RAM-based assembler, reading the current source code in memory and assembling it. Unlike Commodore or Apple LADS, where you change the source code and reassemble a separate version of LADS dedicated to RAM-based assembly, Atari LADS features both disk assembly and memory assembly in the same program, executing the appropriate code by checking RAMFLAG. For more information on this, see "Notes on the Structure of Atari LADS" in Chapter 11.

After an assembly is complete, or if you halt assembly by hitting the BREAK key, control will return to the editor.

Error Handling

Within the editor, any error will be displayed with Error - and the error number. This may be Error - 170 for file not found when you try to load a nonexistent file from the editor, or it may be an error returned from the assembler. Use your DOS or BASIC manual for a list of error numbers and error messages. Any illegal command or a command the editor can't understand will result in a Syntax Error.

Special Notes for Cassette Users

The filename for the cassette is C:. It is possible to assemble from cassette. When you see the .END, and hear the single tone, rewind the tape, press play, and then press any key to start the second pass. If you're using linked files, each file must link to the next with .FILE C:. The last source file should end with .END C:. Assembling from tape is a cumbersome affair in any case. It might be preferable for tape drive users to keep all source code in memory, then assemble to memory, using the cassette only to store and retrieve source code.

Pseudo-ops

All the pseudo-ops described above for the Commodore and Apple versions are fully operative in Atari LADS. A few usage notes follow:

.O This causes the assembler to POKE the object code into memory. Its converse is .NO. You must not overwrite the

Appendix A: How to Use LADS

assembler, which uses memory from $8000 to approximately $9FFF. During assembly, the labels are stored below $8000, descending towards $7000. Only a very long program will need memory between $7000 and $8000 when it is assembled. Also avoid overwriting your source code, which starts at $2000 and works its way up.

A good location for very small routines is in page 6, $0600-$06FF. During assembly, all of page 5 will be corrupted. You can store your object code fairly safely at $5000 or $6000, assuming your source code in memory is not too long. You can break your source code into modules, which will link together with .FILE and .END (see below). If you remove all cartridges (or hold down OPTION when you turn on your machine, which removes BASIC on a 600XL or 800XL), there will be unused memory from $A000 to about $AFFF, less screen memory usage.

An alternative to .O is the .D pseudo-op, which stores the object code to disk. This entirely avoids any memory constraints. You can go to DOS and load the object code, then use the M. RUN AT ADDRESS option to execute and test it.

.D If storing object code to disk, be sure to use the D:, as in .D D:LADS.OBJ. Storing object code to tape is risky, since an excessively long leader may be written. Besides, there is no facility for loading cassette object files without a BASIC loader program. After the assembly is complete, you can go to the DOS menu and use menu selection L to load your program, then selection M to run it. Menu selection M. RUN AT ADDRESS requires a hexadecimal number without the dollar sign.

.P This assumes an 80-column printer. Remember to use it with .S if you want the assembly listing to also go to the printer. If the printer is not turned on, assembly will abort and you will be returned to the editor with an Error - 138.

.FILE Be sure to follow .FILE (or simply .F) with a space, then D:, followed by the filename. You may get occasional errors if you don't use an extender. It is recommended that you add the extender .SRC, as in VALDEC.SRC (SRC for SouRCe). For example, .FILE D:EVAL.SRC

.END Use this only at the end of the last file in a linked chain of source code. You can abbreviate it to .E. An example of proper usage is .END D:DEFS.SRC

Appendix A: How to Use LADS

Programming Aids —'

Following are two utility programs, written in BASIC. Program A-l will renumber an Atari LADS source program. Just run it and follow the prompts. Program A-2 partially converts a file ""^

from the Assembler Editor, EASMD, or MAC/65 assembler to the LADS syntax. It removes leading spaces after a line number, trailing spaces at the end of a line, and tucks comments «^ right next to the operand fields. Into the DATA statements starting at 500, insert the filenames of the files you want converted. Be sure to make END the last item in the DATA statements. To use LADS to assemble code written for one of these other assemblers, you must complete the conversion yourself by adjusting the pseudo-ops. See the descriptions of the LADS pseudo-ops at the start of this appendix.

Program A-l. Atari LADS Renumber Utility

10 GRAPHICS 0:? ."Renumber LADS":? ,"

20 DIM T*(20).F$ (20) ,F2*(20),A*<120)

30 ? "Enter filename. Do not use D:":INPUT T

$:F* = "D: " :F* ( 3)=T$ 40 F2*="DsTEMP.":F2* CLEM (F2$)+1)=T* 50 TRAP 500: OPEN #1,4,0 , F*5 TRAP 40000 60 ? : ? "We will renumber the entire file." 7 0 ? : 7 "What line number do you want the -f i

le"s? "to start with?100C4 LEFT]";:INPUT

T*:LNUM=VAL(T$) 30 ? :V "What step do you want between":? "e

ach line?10{3 LEFT}";:INPUT T$:INCR=VAL(T

% ) 90 OPEN #2,3.0."D:TEMP" 100 TRAP 150: INPUT #1,A$:Z=1 110 IF A*(2,Z)<>" " THEN IF Z<LEN(A*) THEN Z

=7+1:GOTO 110 130 PRINT #2 ; LNLIM; A£ ( Z ) : LNUM = LNUM+I NCR 140 GOTO 100

150 IF PEEK(195)<>136 THEN 200 160 CLOSE #1:CLOSE #2:XIO 33. #1 ,0, 0, F*: X I 0 3

2 , # 1 , 0 , 0 . F 2 * 170 ? :? "Finished!":END 20 0 ? "CBELL 3 Error - ";PEEK<195);" during re

number":END 500 ? "{BELL y Cannot open ";F$:7 "Error - ";P EEK(195):END

Appendix A: How to Use LADS

Program A-2. Atari LADS File Converter Utility

3 GRAPHICS 0

4 DIM A * i 1 0 0 ) . T* ( 1 & 0 ) ,F$(20) ,F2t (50)

10 READ T % '. ? Tt:F$="D: ":F$«3)=T$: IF T$="END"

THEN END 2 0 F2$="D:TEMP. ":F2$(LEN(F2t)+1)=T$ 100 OPEN #1.4.0.F* 110 OPEN #2,8,0, "DsTEMP"

130 TRAP 170: INPUT #1.A$: IF A*<1,1>=" 0" THEN A*=A*(2)

13 5 2=LEN(A$1

140 IF A4<Z„Z)=" " THEN Z=Z-1 :GOTO 140

1 42 AS = A$ > 1 . Z > : Z=1

144 IF A*<Z«Z><>" " THEN 7=Z+l:GCTO 144

14 5 SZ=Z:Z=Z+1

146 IF A*(Z.Z">=" " THEN Z=Z+l:GOTD 146

147 T* = A* < Z) :At = At(l.SZ) ;A*(SZ + 1)=T*s Z = LEN(A $! : IF T$U.li= ! ' ;" THEN 169

150 IF A*<Z.!><>":" THEN Z=Z-lsIF Z THEN 150

152 Sz=Z;Z=Z 1:IF Z-0 THEN 169

154 IF A*<Z,Z>=" " THEN Z=Z-l:GOTO 154

1 56 T$ = A* !SZ) :A* = A* i 1 , Z) :A*(Z +■ 1 ) =T*

16^ PRINT #2;A*:60T0 13 0

!70 CLOSE #1: CLOSE #2:XTO 3 3, #1 ,0, 3,Ft: X ID 3

2. # 1 , 0. 0, F 2 $: G Q T O 10

130 REM PUT YOUR FILENAMES HERE

190 REM E.G. DATA DEFS.SRC,EVAL.SRC.END

Special Apple Notes

Once you have typed in Apple LADS, you must BSAVE it to disk. The start address is $79FD and the length is $1674. To execute LADS you BRUN the binary file. After it loads and sets up its special wedge (see Chapter 11 for details on this wedge), you will be prompted with the BASIC prompt and a cursor. You can now type in your files and save them just as you would an Applesoft file. After saving the program to disk, you assemble it by typing:

ASM filename

Make sure you have a space between ASM and your filename. If you do not have the space, you will get a syntax error. With the wedge in, the BASIC tokenize routine does not execute, so you cannot type in BASIC programs after you BRUN LADS. Otherwise, all the features of Apple LADS operate as described under the general instructions at the start of this chapter.

LADS Object Code

LADS will run on the Commodore 64, VIC-20, PET/CBM, Atari, and Apple computers. If you have a Commodore or Atari you should use the "MLX" machine language editor to enter the object code for LADS. Complete instructions on how to enter the object code using MLX, as well as the MLX programs, can be found in Appendix C. PET/CBM owners may find it convenient to use their built-in machine language monitor to make the changes shown in Programs B-3a and B-3b. Apple users should use the Apple built-in monitor and enter the hex data found in Program B-5. Additional instructions for the use of LADS can be found in Appendix A, "How to Use LADS."

LADS is nearly 5K long, and for those who prefer not to type it in, it can be purchased on a disk by calling COMPUTE! Publications toll free at 1-800-334-0868. Be sure to state whether you want the Commodore, Atari, or Apple disk.

Program B-l. Commodore 64 LAJDS: MLX Format

11000 :169,000,160,048,153,113,123 11006 :062,136,208,250,169,248,047 11012 :133,176,133,055,141,135,009 11018 :062,169,042,133,177,133,214 11024 :056,141,136,062,169,001,069 11030 :141,157,062,185,000,004,059 11036 : 201,032, 240,012,176,003,180 11042 :024,105,064,153,150,061,079 11048 :200,076,025,043,153,150,175 11054 :061,200,185,000,004,201,185 11060 : 032, 208,226,136,132,183,201 11066 :032,248,049,032,184,050,141 11072 :169,000,141,119,062,032,075 11078 :104,051,173,138,062,208,0 38 11084 :063,032,133,056,169,230,247 11090 :032,210,255,169,076,032,088 11096 : 210,255,169,065,032,210,005 11102 : 25 5,169, 068, 03 2, 210, 25 5, 059 11108 :169,083,032,210,255,032,113 11114 :133,056,173,128,062,208,098 11120 : 011,169,068,133,251,169,145 11126 :061,13 3,252,032,219,050,097 11132 : 173,122,062,133,253,141,240 11138 :115,062,173,123,062,133,030

Appendix B: LADS Object Code

11144 11150 11156 11162 11168 11174 11180 11186 11192 11198 11204 11210 11216 11222 11228 11234 11240 11246 11252 11258 11264 11270 11276 11282 11288 11294 11300

11306 11312 11318 11324 11330 11336 11342 11348 11354 11360 11366 11372 11378 11384 11390 11396 11402 11408 11414 11420 11426 11432

198 226 113 026 171 016 109 047 014 081 203 077 187 125 193 080 038 223 119 191 014 063 255 101 237 252 150 039 106 065 093 222 160 188 118 052 108 047 155 223 177 117 160 242 183 155 241 064 145

Appendix B: LADS Object Code

11438 11444 11450 11456 11462 11468 11474 11480 11486 11492 11498 11504 11510 11516 11522 11528 11534 11540 11546 11552 11558 11564 11570 11576 11582 11588 11594 11600 11606 11612 11618 11624 11630 11636 11642 11648 11654 11660 11666 11672 11678 11684 11690 11696 11702 11708 11714 11720 11726

159 083 115 113 119 227 209 229 054 126 027 039 013 148 196 017 162 092 170 137 008 103 020 057 166 127 229 100 059 243 200 179 236 032 150 235 045 057 105 244 070 130 021 008 134 123 081 024 199

Appendix B: LADS Object Code

-

Appendix B: LADS Object Code

12026 12032 12038 12044 12050 12056 12062 12068 12074 12080 12086 12092 12098 12104 12110 12116 12122 12128 12134 12140 12146 12152 12158 12164 12170 12176 12182 12188 12194 12200 12206 12212 12218 12224 12230 12236 12242 12248 12254 12260 12266 12272 12278 12284 12290 12296 12302 12308 12314

,195 ,223 ,189 ,032 ,015 ,185 ,110 ,045 ,096 ,039 ,174 ,021 ,109 ,182 ,145 ,123 ,028 ,015 ,198 ,158 ,113 ,064 ,031 ,111 ,123 ,029 ,176 ,152 ,142 ,255 ,163 ,076 ,080 ,130 ,023 ,140 ,009 ,202 ,170 ,161 ,172 ,106 ,229 ,107 ,043 ,093 ,124 ,197 ,065

Appendix B: LADS Object Code

12320 12326 12332 12338 12344 12350 12356 12362 12368 12374 12380 12386 12392 12398 12404 12410 12416 12422 12428 12434 12440 12446 12452 12458 12464 12470 12476 12482 12488 12494 12500 12506 12512 12518 12524 12530 12536 12542 12548 12554 12560 12566 12572 12578 12584 12590 12596 12602 12608

,238 ,110 ,107 ,013 ,193 ,174 ,071 ,141 ,111 ,227 ,082 ,178 ,023 ,022 ,110 ,012 ,026 ,244 ,118 ,185 ,042 ,156 ,024 ,211 ,189 ,100 ,144 ,049 ,014 ,164 ,159 ,184 ,066 ,231 ,006 ,108 ,129 ,019 ,223 ,103 ,080 ,058 ,179 ,183 ,203 ,083 ,079 ,051 ,127

Appendix B: LADS Object Code

12614 12620 12626 12632 12638 12644 12650 12656 12662 12668 12674 12680 12686 12692 12698 12704 12710 12716 12722 12728 12734 12740 12746 12752 12758 12764 12770 12776 12782 12788 12794 12800 12806 12812 12818 12824 12830 12836 12842 12848 12854 12860 12866 12872 12878 12884 12890 12896 12902

,234 ,047 ,088 ,248 ,219 ,093 ,174 ,202 ,070 ,197 ,171 ,075 ,017 ,106 ,181 ,211 ,023 ,101 ,147 ,125 ,087 ,046 ,111 ,139 ,208 ,252 ,052 ,043 ,051 ,029 ,133 ,152 ,047 ,113 ,117 ,177 ,070 ,137 ,077 ,141 ,131 ,058 ,121 ,141 ,119 ,149 ,120 ,000 ,191

Appendix B: LADS Object Code

12908 12914 12920 12926 12932 12938 12944 12950 12956 12962 12968 12974 12980 12986 12992 12998 13004 13010 13016 13022 13028 13034 13040 13046 13052 13058 13064 13070 13076 13082 13088 13094 13100 13106 13112 13118 13124 13130 13136 13142 13148 13154 13160 13166 13172 13178 13184 13190 13196

006 130 225 122 005 168 124 050 102 079 057 221 213 103 087 028 144 037 127 190 194 055 041 023 095 251 084 037 045 089 063 005 205 073 219 018 061 192 145 245 028 193 225 033 084 050 195 202 158

Appendix B: LADS Object Code

13202 13208 13214 13220 13226 13232 13238 13244 13250 13256 13262 13268 13274 13280 13286 13292 13298 13304 13310 13316 13322 13328 13334 13340 13346 13352 13358 13364 13370 13376 13382 13388 13394 13400 13406 13412 13418 13424 13430 13436 13442 13448 13454 13460 13466 13472 13478 13484 13490

,046 ,188 ,192 ,149 ,078 ,189 ,163 ,072 ,153 ,241 ,172 ,099 ,087 ,176 ,049 ,045 ,095 ,105 ,230 ,012 ,075 ,144 ,199 ,153 ,114 ,102 ,139 ,198 ,036 ,032 ,018 ,088 ,149 ,040 ,076 ,132 ,101 ,208 ,103 ,223 ,238 ,220 ,159 ,202 ,105 ,112 ,161 ,207 ,155

Appendix B: LADS Object Code

13496 13502 13508 13514 13520 13526 13532 13538 13544 13550 13556 13562 13568 13574 13580 13586 13592 13598 13604 13610 13616 13622 13628 13634 13640 13646 13652 13658 13664 13670 13676 13682 13688 13694 13700 13706 13712 13718 13724 13730 13736 13742 13748 13754 13760 13766 13772 13778 13784

,004 ,132 ,169 ,105 ,101 ,203 ,194 ,078 ,017 ,004 ,236 ,104 ,076 ,155 ,028 ,009 ,245 ,157 ,058 ,077 ,027 ,169 ,203 ,018 ,218 ,155 ,048 ,007 ,013 ,019 ,105 ,094 ,083 ,031 ,171 ,086 ,017 ,098 ,006 ,103 ,148 ,126 ,191 ,065 ,022 ,206 ,234 ,133 ,245

Appendix B: LADS Object Code

13790 13796 13802 13808 13814 13820 13826 13832 13838 13844 13850 13856 13862 13868 13874 13880 13886 13892 13898 13904 13910 13916 13922 13928 13934 13940 13946 13952 13958 13964 13970 13976 13982 13988 13994 14000 14006 14012 14018 14024 14030 14036 14042 14048 14054 14060 14066 14072 14078

,198 ,160 ,066 ,195 ,187 ,163 ,239 ,140 ,234 ,062 ,051 ,226 ,255 ,130 ,074 ,093 ,125 ,119 ,180 ,063 ,163 ,195 ,185 ,138 ,028 ,049 ,148 ,145 ,055 ,161 ,037 ,094 ,052 ,242 ,040 ,128 ,179 ,225 ,069 ,055 ,143 ,063 ,246 ,164 ,186 ,237 ,221 ,076 ,036

Appendix B: LADS Object Code

14084 14090 14096 14102 14108 14114 14120 14126 14132 14138 14144 14150 14156 14162 14168 14174 14180 14186 14192 14198 14204 14210 14216 14222 14228 14234 14240 14246 14252 14258 14264 14270 14276 14282 14288 14294 14300 14306 14312 14318 14324 14330 14336 14342 14348 14354 14360 14366 14372

158 236 248 004 060 029 136 160 082 149 169 081 046 243 146 160 200 250 203 057 244 235 199 028 153 215 077 130 053 170 190 174 081 235 106 220 178 088 171 245 180 177 101 205 151 103 198 010 177

Appendix B: LADS Object Code

14378 14384 14390 14396 14402 14408 14414 14420 14426 14432 14438 14444 14450 14456 14462 14468 14474 14480 14486 14492 14498 14504 14510 14516 14522 14528 14534 14540 14546 14552 14558 14564 14570 14576 14582 14588 14594 14600 14606 14612 14618 14624 14630 14636 14642 14648 14654 14660 14666

,171 ,200 ,091 ,237 ,133 ,040 ,158 ,007 ,013 ,019 ,076 ,127 ,112 ,087 ,151 ,139 ,031 ,028 ,001 ,203 ,045 ,091 ,146 ,187 ,097 ,117 ,007 ,007 ,172 ,044 ,097 ,139 ,017 ,074 ,118 ,062 ,235 ,156 ,098 ,185 ,207 ,014 ,093 ,042 ,017 ,220 ,171 ,152 ,239

Appendix B: LADS Object Code

14672 14678 14684 14690 14696 14702 14708 14714 14720 14726 14732 14738 14744 14750 14756 14762 14768 14774 14780 14786 14792 14798 14804 14810 14816 14822 14828 14834 14840 14846 14852 14858 14864 14870 14876 14882 14888 14894 14900 14906 14912 14918 14924 14930 14936 14942 14948 14954 14960

,005 ,068 ,107 ,238 ,179 ,036 ,255 ,178 ,155 ,042 ,241 ,046 ,143 ,146 ,197 ,192 ,141 ,013 ,050 ,206 ,113 ,075 ,146 ,082 ,099 ,117 ,250 ,120 ,100 ,212 ,150 ,018 ,161 ,065 ,042 ,215 ,178 ,214 ,030 ,067 ,089 ,110 ,212 ,126 ,050 ,082 ,250 ,136 ,142

Appendix B: LADS Object Code

14966 14972 14978 14984 14990 14996 15002 15008 15014 15020 15026 15032 15038 15044 15050 15056 15062 15068 15074 15080 15086 15092 15098 15104 15110 15116 15122 15128 15134 15140 15146 15152 15158 15164 15170 15176 15182 15188 15194 15200 15206 15212 15218 15224 15230 15236 15242 15248 15254

,120 ,236 ,044 ,173 ,105 ,038 ,087 ,157 ,175 ,217 ,187 ,199 ,013 ,247 ,136 ,031 ,038 ,138 ,239 ,117 ,052 ,135 ,067 ,084 ,255 ,088 ,088 ,073 ,158 ,012 ,062 ,066 ,191 ,025 ,199 ,168 ,100 ,094 ,214 ,195 ,008 ,042 ,193 ,177 ,129 ,021 ,111 ,213 ,027

Appendix B: LADS Object Code

15260 15266 15272 15278 15284 15290 15296 15302 15308 15314 15320 15326 15332 15338 15344 15350 15356 15362 15368 15374 15380 15386 15392 15398 15404 15410 15416 15422 15428 15434 15440 15446 15452 15458 15464 15470 15476 15482 15488 15494 15500 15506 15512 15518 15524 15530 15536 15542 15548

,086 ,131 ,187 ,192 ,202 ,199 ,023 ,128 ,110 ,059 ,141 ,013 ,114 ,035 ,175 ,009 ,102 ,019 ,024 ,220 ,114 ,121 ,254 ,231 ,205 ,240 ,014 ,038 ,036 ,013 ,016 ,022 ,047 ,245 ,056 ,073 ,055 ,040 ,053 ,063 ,078 ,104 ,114 ,089 ,105 ,128 ,124 ,151 ,154

Appendix B: LADS Object Code

15554 15560 15566 15572 15578 15584 15590 15596 15602 15608 15614 15620 15626 15632 15638 15644 15650 15656 15662 15668 15674 15680 15686 15692 15698 15704 15710 15716 15722 15728 15734 15740 15746 15752 15758 15764 15770 15776 15782 15788 15794 15800 15806 15812 15818 15824 15830 15836 15842

,112 ,238 ,217 ,224 ,220 ,232 ,000 ,253 ,245 ,057 ,111 ,077 ,184 ,066 ,058 ,191 ,204 ,202 ,148 ,099 ,155 ,082 ,070 ,076 ,082 ,088 ,094 ,100 ,106 ,112 ,118 ,124 ,130 ,136 ,142 ,148 ,154 ,160 ,166 ,172 ,111 ,102 ,113 ,203 ,216 ,222 ,236 ,104 ,143

Appendix B: LADS Object Code

15848 :032,08 2,06 5,078,071,069,11V

15854 :000,085,078,068,069,070,096 ■■«

15860 : 073,078,069,068,032,076,128

15866 :065,066,069,076,000,029,043

15872 :029,029,029,029,029,029,174

15878 :029,029,032,078,065,075,058

15884 :069,068,032,076,065,066,132

15890 :069,076,000,0 29,029,029,2 50

15896 :029,029,032,060,060,060,038 —'

15902 :060,060,060,060,060,032,106

15908 :068,073,083,075,032,069,180

15914 :082,082,079,082,032,062,205

15920 :062,062,062,062,062,062,164

15926 :062,032,000,029,029,029,235

15932 :029,029,032,045,045,032,016

15938 :068,085,080,076,073,067,003

15944 :065,084,069,068,032,076,210

15950 :065,066,069,076,032,045,175

15956 : 045,032,000,029,029,029,248

15962 :029,029,032,045,045,032,046

15968 :083,089,078,084,065,088,071

15974 :032,069,082,082,079,082,016

15980 : 032,045,045,032,000,000,006

Program B-2. VIC Adjustments to Prog. B-l

To create the VIC-20 version of LADS, change the following lines in Program B-l:

11030 :141,157,062,185,000,016,071

11054 :061,200,185,000,016,201,197

12014 :255,076,116,196,185,089,131

12272 :152,032,205,221,173,139,138

12818 :133,188,032,190,225,096,114

12842 :133,188,032,190,225,032,074

128613 : 000,133,183, 032,190, 225, 05 5

12890 :188,032,114,225,032,204,117 _

13418 : 008,232,189,158,192,016,133

13430 :192,048,007,15 3,068,061,135

14114 :008,232,189,158,192,016,061

14126 :192,048,007,153,000,002,192

14426 :096,169,000,032,205,221,045

14462 :032,205,221,032,022,057,183

14486 :221,032,076,057,096,169,033

14600 :140,062,032,205,221,032,188

14654 :166,253,032,205,221,032,203

14696 :221,032,204,255,162,001,211

Appendix B: LADS Object Code

Program B-3a. PET/CBM 4.0 BASIC Adjustments to Prog. B-l

To create the 4.0 BASIC version of LADS, type in Program B-l then change the following bytes:

Appendix B: LADS Object Code

Program B-3b. PET/CBM Upgrade BASIC Adjustments to Prog. B-l

To create the Upgrade BASIC version of LADS, type in Program B-l then change the following bytes in addition to the changes shown in B-3a above:

Program B-4. Atari LADS: MLX Format

32768: 32774: 32780: 32786: 32792: 32798: 32804: 32810: 32816: 32822: 32828: 32834: 32840: 32846: 32852: 32858: 32864: 32870: 32876: 32882: 32888: 32894: 32900: 32906: 329 12:

076 082 136 138 133 00 1 145 000 000 153 04 7 135 141 173 121 145 169 068 032 173 144 135 154 173 186

146, 04 8, 250, 205, 141 . 227, 162, 062, 201 , 153, 132, 005, 154, 154, 169, 076, 032, 036, 145, 154, 134, 043, 136, 154, 032,

169,

153,

169

154

206

154

208

146

155

200

128

136

032

208

160

032

036

145

0 32

208

169

136

141

133

175

0 00 183 000 169 154 032 026 232 240

X. ■_> ~£~

032 169 190 063 032 036 145 169 121

01 1 153 173 185 137 1 45

133 154 133 128 169 014 160 189 00 8 07 6 013 00 0 136 032 036 145 169 083 14 1 169 133 192 154 14 1 173

215 018 140 185 198 087 134 077 145 070 028 03 1 146 148 23 1 181 200 123 103 003 218 069 01 1 045 241

Appendix B: LADS Object Code

329 18: 32924: 32930: 32936: 32942: 32948: 32954: 32960: 32966: 32972: 32978: 32984: 32990: 32996: 33002: 33008: 33014: 33020: 33026: 33032: 33038: 33044: 33050: 33056: 33062: 33068: 33074: 33080: 33086: 33092: 33098: 33104: 33110: 33116: 33122: 33128: 33134: 33140: 33146: 33152: 33158: 33164: 33170: 33176: 33182: 33188: 33194: 33200: 33206:

218 046 132 221 177 206 162 181 046 085 231 014 243 254 142 026 143

0 35

1 78 058 06 1 097 152 20 1 003 171 214 231 134 101 130 088 116 172 084 217 146 129 147 233 080 171 235 236 009 218 152 134 23 1

Appendix B: LADS Object Code

169 136 173 0 63 184 201 024 154 154 24 0

00 1

109

1 7 3 083 130 076 154 154 255 1 33 130

1 33 133 154 076 136 208 173

0 03 154 201

2 40 183

1 18

2 39 165 1 69 233 034 14 1 201 024 154 1 40 1 54 007 024 076 009

030 04 3 050 218 163 165 062 105 214 042 237 141 225 236 124 213 200 224 162 00 7 07 0 019 093 216 134 066 130 063 037 025 238 240 2 2 7 02 1 025 172 052 079 160 122 069 112

00 0

157 016 106 087 107 200

378

~

Appendix B: LADS Object Code

Appendix B: LADS Object Code

33300: 33806: 33812: 33818: 33824: 33830: 33836: 33842: 33848: 33854: 33860: 33866: 3387 2: 33878: 33884: 33890: 33896: 33902: 33908: 33914: 33920: 33926: 33932: 33938: 33944: 33950: 33956: 33962: 33968: 33974: 33980: 33986: 33992: 33998: 34004: 340 10: 34016: 34022: 34028: 34034: 34040: 34046: 34052: 34058: 34064: 34070: 34076: 34082: 34088:

004

145 240 153 237 015 24 0

20 1 154 183 183 184 169 104 105 233 1 30 169 141 154 201 024 154 240 201 032 169 141, 201 , 154, 104, 184, 169, 141 , 201 , 240, 1 69, 104, 183, 233,

21 1 , 160, 104, 152, 154, 1 54, 144, 248, 155,

032

185

098

201

129

173

079

00 1

20 1

154

154

154

04 9

132

028

130

141

154

076

208

00 2

109

07 6

016

005

248

02 0

183

089

201

132

154

024

183

00 1

012

051

132

154,

130,

154,

032,

168,

032,

172,

096,

153,

096,

141 ,

148 004 197 234 214 186 136 005 103 025 187

000

240 042 247 247 223 002 155 195 142 160 007 098 046 137 029 196 101 087 157 019 048 232 239 162 130 186 135 135 233 097 219 116 052 253 022 034 150

Appendix B: LADS Object Code

3409A: 34 100: 34 106: 34112: 34118: 34 124: 34 130: 34136: 34142: 34148: 34154: 34160: 34 166: 34 172: 34178: 34 184: 34190: 34196: 34 20 2: 34208: 34214: 34220: 34226: 3 4 2 3 2: 34238: 34244: 34250: 34256: 34262: 34268: 34274: 34280: 34286: 34292: 34298: 34304: 34310: 34316: 34322: 34328: 34334: 34340: 34346: 34 352: 34358: 34364: 34370: 34376; 34382:

032, 134, 03 4, 132. 144, 208, 154. 154,

000.

144, 200, 240, 133.

06 1 . 145, 138,

000.

153, 143, 03 2, 134. 034, 14 0, 208, 191 , 191 , 105. 136, 154, 200, 239, 140, 032, 055, 140, 099,

xL -ll* 3 «

2 3 3, 177, 208,

14 1. 213, 154 , 2 "% 2 173, 0 3 2, 153,

07 9,

130, 169, 141 , 13 0, 153. 246, 056, 133,

1 33 , 153, 185, 005, 200, 240, 138, 174, 189, 144. 133, 155, 169. 141 , 203, 023, 154 ,

154; 000 ;

172,

145; 145; 130; 173; 242;

146,

165; 162; 002;

000 ,

140; 0 0 2; 076;

2 12

154 ; 240; 142;

20 7 ,

242;

2 4 0,

133,

032, 076, 185, 032, 202, 202,

J— 3 -3

185 138 032 106 20 1 136 145 160 008 07 6 096 1 33 0 32 136 154 140 109 153 04 3 192 154 076 133 141 141

176 140 141

0 00

140 140 140 141

1 90 134

00 1

20 0 165 144 241

157 04 1 09 1 102 158 248 010 026 199 1 13 25 3 054 1 10 144 036 142 108 082 208 092

1 JJ

147 176 131 213 106 138 214 147 219 236

i r=r cr 1 ._J ._»

187 009 216 110 052 167 188 143 0 27 222 097 227 168 014 0 0 0 173 195

Appendix B: LADS Object Code

34388: 34394: 34 4 00: 34406: 34412: 34418: 34424: 3 4 4 30: 34436: 34442: 34448: 34454: 34460: 34466: 34472: 34478: 34484: 34490: 34496: 34502: 34508: 34514: 34520: 34526: 34532: 34538: 34544: 34550: 34556: 34562: 34568: 34574: 34580: 34586: 34592: 34598: 34604: 34610: 34616: 34622: 34628: 34634: 34640: 34646: 34652: 34658: 34664: 34670: 34676:

173 213 134 146 154

1 JJ

051 169 141

1 73 016 208 132 003 146 172 240 192 193 010 193 219 217 192 193

2 08 084 198 155 169 141 014 145 004 132 153 165 24 0

000

161

182 169 133 169 032 22 1 032 0 36 185

Appendix B: LADS Object Code

34682: 34688: 34694: 34700: 34706: 34712: 347 18: 34724: 34730: 34736: 34742: 34748: 34754: 34760: 34766: 34772: 34778: 34784: 34790: 34796: 34B02: 34808: 34814: 34820: 34826: 34832: 34838: 34844: 34850: 34856: 34862: 34868: 34874: 34880: 34886: 34892: 34898: 34904: 34910: 34916: 34922: 34928: 34934: 34940: 34946: 34952: 34958: 34964: 34970:

032 03 2

03 2 096 025 169 133 169 144 014 000 152 2 00 240 104 00 6 238 146 210 20 1 208 184 183 0 0 0 161 032 20 1 133 0 32 096 048 00 4 254 192 00 1

04 1 255 153. 018, 153, 153, 13 6. 154, 254, 0 14, 014. 024, 153, 154 ,

036 036 036 169 145

0 0 0

128 135 165 145 162 205 2 00 076

1 52

2 00 2 00 153 240 032 213 154 154 1 33 162 241 0 42 134

0 34 160

1 44

2 00 153 154 142 015 153 14 1 0 32 14 1 141 238 032 153 252 252 173 14 1 109

145, 145, 145, 004, 169, 133, 169, 133,

00 1 , 096, 255, 144, 2 00, 238, 205, 200, 185, 240, 224, 240, 189, 188, 076, 160,

001 , 1 45, 240, 169, 141 ,

000,

0 08, 076,

1 36 , 141 , 210, 141 , 169,

000,

131 , 255,

0 00,

210, 170, 208, 153, 153,

173 173 032 133

008

132 181 130 04 8 080 232 153 224 128 145 208 1 04 005 173

00 4 016 072 207

1 69 032 032 014 1 54 076 177 201 04 5 169 193 154 252

0 0 0

154 136 153 154 154 136 202 046 046 153 153 153

080 130 252 189 144 084 172 225

0 1 0 174 025 107 244 123 173 195 129 101 205 245 045

1 16 143 251 019 107 237

02 0

07 3 05 6 014 099 022 1 17 125 068 185 162

08 5

203 --> 1 ■">

nn JL ._* JL

226

00 3

203 239 129 184 228

Appendix B: LADS Object Code

34976: 34982: 34988: 34994: 350 00: 35006: 35012: 35018: 35024: 35030: 35036: 35042: 35048: 35054: 35060: 35066: 35072: 35078: 35084: 35090: 35096: 35 102: 35108: 35114: 35120: 35126: 35132: 35138: 35144: 35150: 35 156: 35 162: 35 168: 35174: 35 180: 35 186: 35192: 35 198: 35204: 35210: 35216: 35222: 35228: 35234: 35240: 35246: 35252: 35258: 35264:

141 04 6

192

193

032

198

220

224

145

032

146

076

208

058

20 1

154

141

240

088

007

020

051

121

154

154

032

000

234

060

00 3

137

104

201

240

238

003

240

153

136

164

07 6

220

00 1

136

154

036

121

032

j- J o

153 154 154 028 154 154 154 032 253 128 243 003 208 059 173 229 006 137 153 137 141 141 076 141 085 005 153 137 076 173 104 062 05 1 219 076 015 1 44 141 138 03 1 154 141 0 32 240 145 141 160

153 153 109 173 141

1 3 3 140 140 208 085 137 20 1 136 076 00 3 20 8

2 2 2 154 032 032 144 032 032 169 088 209 145 172

000

0 32 080 209 076 24 0 20 1 154 169 20 1 153 224 153 138 076 2 20 2 35

01 1 032 173 00 0

0 14 096 192 2 5 3 193 160 229 219

003

145 104 0 32 0 32 253 076 104 154 173 052 085 153 130 143

000

137 154 208 209 005 085 137 154 146 047 04 3 201

1 37 036

2 00 154 144 169 2 35 154 136 169 1 43 1 98 185

102 143 149 188 09 1 171 187 238 046 069 158 188 181 092 097 147 249 042 043 233 245 010 064 055 105 104 178 190 228 009 153 208 238 145 086 152 240 104 069 197 072 195 122 22 1 064 054 197 157 098

Appendix B: LADS Object Code

35

35270: 35276: 35282: 35288: 35294: 35300: 35306: 12: 18: 5324: 5330: 5336: 342: 348: 354: 360: 366: 372: 378: 384:

35 35 35 35 35 35 35

35390: 35396: 35402: 35408: 35414: 354 20: 35426: 35432: 35438: 35444: 35 4 50: 35456: 35462: 35468: 35474: 35480: 35486: 35492: 35498: 35504: 3 5 5 10: 35516:

O U vj J- *-

35528 35534 35540 35546 3 5 5 5 2 35558

201 195 144 169 032 240 003 154 133 128 080 173 0 1 0 0 0 0

00 1

0 00 20 1 24 0 20 1 240 153 138 153 235

0 0 0

153 154

1 4 1

1 7 0 154 ! 54 154 154 144 013

2 38 192

1 54 032 153 145 066 214

02 3 154 032 032 032

032 137 024 153 043 008 032 1 33 137 153 20 8 08 3 20 1 14 1 14 1

0 32 058 243 044

01 1 1 44 142

20 0

136 142 032 076 192 014

01 4 014 014 189

002

192 236 198

000

2 08 06 6 1 44 153 20 8 1 54 140 24 0 05 1 05 1 085

240 200 101 105 136 1 73 238

1 3 6 104 144 248

00 3

136

2 2 4 189 08 5 240 20 1 240

1 57 153 1 99

03 2 14 1 1 99 102 238, 154, 1 92 , 1 92 1 92,

192;

205,

1 54, 1 99, 154. 240,

00 9 .

1 33, 153. 144. 104. 173, 211. 0 15. 14 1. 14 1. 145.

183 147 1 12 143 200 240 243 159 178 198 220 199 0 0 8 036

00 1

06 6

04 2

0 35 024 030

05 3 230 007

1 0 0 110 1 3 1 103 055 1 1 1 101 107 1 13

07 7 070 224 1 1 0 246 22 4 069 015 181 1 60 1 14 0 63 109 156 194 21 7 174

Appendix B: LADS Object Code

Appendix B: LADS Object Code

Appendix B: LADS Object Code

Appendix B: LADS Object Code

Appendix B: LADS Object Code

Appendix B: LADS Object Code

37034: 37040: 37046: 37052: 37058: 37064: 37070: 3 7 0 7 6: 37082: 37088: 37094: 37 100: 37 106: 37112: 37118: 37124: 37 130: 37 136: 37 142: 37148: 37154: 37 160: 37166: 37172: 37178: 37184: 37190: 37196: 37202: 37208: 37214: 37220: 37226: 37 2 3 2: 37238: r . 7 2 4 4 : >7250: 37256:

•_■ / jl. O -z, -

37268: 37274: 37280: 37286: 37292: 37298: 37304: 37310: 3 7 3 16: 37322:

169, 121,

005, 076, 036, 14 5., 14 1, 010. 165, 1 29 , 157, 0 7 2, 003 , 165, 003, 228, 096, 134, 134, 169, 00 2, 169, 204, 14 3, 157, 169, 203, 204, 203, 142, 046, 230, 024, 14 1, 048,

00 5. 169, 003, 142, 157, 169, 002, 205,

1 69. 240, 146. 138. 1 45 , 145,

0 83 141 169 190 145 032 -^_ s- /

01 0 131 157

069

00 3 165 132

1 57 132 134 142

00 1 012 145 15 5 145 03 2 07 2

01 1 145 145 145 205 160 160 165 206 146 240 0 0 «

104 032 07 2

0 0 7

1 45 145

00 0

00 2 162 0 32

2 0 2 0 0 0

032 1 73 00 1 143 169 121 154 0 1 0 032 068

0 0 3 169 133

1 57 066

00 1 143 134 096 157 20 1

1 4 1 142

2 12 0 03 157 032 174 096 145 000 208 160 145 013 0 0 3 133 07 6 212

0 0 3 157

1 72 20 1 09 6 1 04 00 7 0 25 20S

0 00

Appendix B: LADS Object Code

Appendix B: LADS Object Code

57622: 17628: 57634: ! 7 6 4 0 : -.7646: '.7652: 57653: 5 7 6 6 4: 57670: 57676: 57682: 576SS: 57694: 57 700: 57706: 57712: 577 18: 57724: 57730: 57736: 57742: 57748: 57754: 57760: 57 7 66: 5 7 7 ~7 2 : 57778: 57784: 57790: 57796 : 57802: 57803: 57814: 57820: 57826: 57832: 57838: 57844: 57850:

37856: 57862: 57868: 57874: 57880: 57836: 57892: 57898: 57904:

379 10:

147, 14 1. 131, 1 48. 169, 151.

14 1, 1 37, 1 48, 169, 152, 146, 169, 084. 146, 032, 1 47, 146, 146,

1 45, 136, 0 0 3 , 150. 208, 073, 201 , 146, 20 1 , 146, 04 1 , 201 ,

15 3, 208, 00 0,

000,

20 1 , 176,

*-> >5 c= " Z. J .J «

141 , 208, 045, 003, 147, 148, 046, 240 , o *-> i

2 0 0, 002.

141, 1 30, 148. 169, 196, 141 , 1 36, 1 48, 1 69, 074, 141 . 240, 203, 147, 169, 014, 169. 149. 14 0, 166, 240, 032, 0 76, 0 1 0 , 0 0 1 , 043, 24 0. 059, 174, 127, 123,

0 0 0 ,

174, 005, 240, 058, 003. 032, 045, 003, 146, 032, 169, 133, 146, 048, 000, 208, 148.

129 1 43 169 151 14 1 135 148 169 151 141

00 7

00 3

14 1 169 032 145

1 42 160 064

00 1

007 1 15 087 072 141 208 209 208 064 20 1 176 00 5 136 140 153 176 07 6 223 146 032 204 255 082 20 4 162 20 1 00 5 239 177

1 48 169 246 141 134 148 169 224 14 1 00 6 0 0 2 076 065

0 0 0

141

096

1 60

000

146

016 224 150 147 173 064

005

233

00 3

146 097 002

2 0 0 169 042 173

0 39 243

1 50 173 163 042 149 133 160

0 0 0 255 208 230 20 3

13 1 103 20 1 130 2 03 20 7 1 44 221 166 093 078 243 094 144 244 213 118 238 199 161

06 1

0 40 049 033 151

07 1 164 157 140 012 04 0 223 153 201 253 155

'—i C *-}

jL %J jL

221 156 183 029 077 121 009 220 254

1 73 149 032

Appendix B: LADS Object Code

Appendix B: LADS Object Code

2 0 0

245 128 141 204

1 46 051 054 057 00 3

0 50

240 204 203 203

2 00 146 146 141 141 14 1

1 46 146 146 240 047 0 4 0 022 04 7 04 7 057 173 035 141 133

0 0 0 047 039 052 014

2 06 076 240

1 73 14 1 105 0 0 0

2 00 240

017 157

1 0 0 112 247 142 108 152 126 0 60 145 115

2 10 037 168

jL wJ J

247 206 236 106 114 136 123 209

00 1

1 17 178 006 015 181 253 144 204

1 17 095

2 30 06 3 216 04 0 128 039 239 023 002 193 181 113 123 188

Appendix B: LADS Object Code

188 195 175 181 2 10 159 229 129 042 2 4 2 07 3 06 3 209 186 166 126 06 2 246 036 013 102 06 1 253 169 230 2 55 209 217 221 229 234 17 1 245 253 230 178 069 1 13

00 4

170 148

000

064 169 142 176 20 7 078 067

Appendix B: LADS Object Code

_*

Appendix B: LADS Object Code

Appendix B: LADS Object Code

39386: 39392; 39398: 39404: 39 410: 39416: 39422: 39428: 39434: 39440: 39446: 39452: 39458: 394 64: 39470: 39476: 39432: 39488: 39494: 39 500: 39506: 39512: 39518: 39524: 39530: 39536: 39542: 39548: 39554: 39560: 39566: 39572: 39578: 39584: 39590: 39596: 39602:

Program B-5. Apple LADS: Hex DATA

79FD- 4C F5 82

7A00- A9 00 A0

7A08- DO FA A9 00 85 EB 85

7A10- 8D E4 8F A9 7A 85 EC

99 CE 3F 88 4C

85

7A18- 4D 8D E5 8F A9 01 8D FA 7A20- 8F B9 00 04 C9 A0 F0 07

7A28- 99 7A30- F3

F3 8E C8 4C 21 7A 99 8E C8 B9 00 04 C9 A0

7A38- DO E7 88 84 F9 20 E5 80 7A40- 20 58 83 A9 00 8D D4 8F

Appendix B: LADS Object Code

7A4S- 20 0E 84 AD E7 SF DO 3F

7A50- 20 50 89 A9 E6 20 D6 81

7A58- A9 4C 20 D6 81 A9 41 20

7A60- D6 81 A9 44 20 D6 81 A9

7A68- 53 20 D6 81 20 50 89 AD

7A70- DD 8F DO OB A9 Fl 85 FB

7A78- A9 8D 85 FC 20 81 83 AD

7A80- D7 8F 85 FD 8D DO 8F AD

7A88- D8 8F 85 FE 8D Dl 8F 20

7A90- 2F 82 AD D4 8F FO 03 4C

7A98- Al 7D 20 OE 84 A9 00 8D

7AA0- DC 8F 8D E6 8F AC E7 8F

7AA8- DO 03 4C C9 7A 8C FB 8F

7ABO- AD F9 8F FO OC 20 59 89

7AB8- 20 OA 89 20 32 89 20 OA

7AC0- 89 AD F2 BF FO 03 20 06

7AC8- 88 4C OA 83 AD CF 8F FO

7AD0- 17 C9 03 DO 72 A9 01 8D

7AD8- CF 8F AD F4 3D DO 68 A9

7AE0- 08 18 6D CE 8F 8D CE 8F

7AE8- 4C B2 7C AD E7 8F FO 39

7AF0- AO FF C8 B9 Fl 8D FO 2E

7AF8- 99 F3 8E C9 20 DO F3 C8

7B00- B9 Fl 8D C9 3D DO 03 4C

7B08- E2 7C A2 00 8E FB 8F 8A

7B10- 99 F3 8E B9 Fl 8D FO 08

7B18- 9D Fl 8D E8 C8 4C 13 7B

7B20- 9D Fl 8D 4C C9 7A 20 78

7B28- 7F 20 1A 7F 4C C9 7A AD

7B30- 38 8E C9 40 BO 06 AD 39

7B38- 8E EE E6 8F 49 80 8D D5

7B40- 8F 20 BC 7F 4C BE 7B AO

7B48- 00 8C DC 8F B9 F5 8D C9

7B50- 41 90 03 EE DC 8F 99 38

7B58- 8E C8 B9 F5 8D FO 16 99

7B60- 38 8E C9 41 90 03 EE DC

7B68- 8F C8 B9 F5 8D FO 06 99

7B70- 38 8E 4C 69 7B 88 BC DB

7B78- 8F AD DD 8F DO 40 AD DC

7B80- 8F DO AC A9 38 85 FB A9

7B88- 8E 85 FC AO 00 AD 38 8E

7B90- C9 30 BO 07 18 E6 FB 90

7B98- 02 E6 FC Bl FB FO 10 C9

7BA0- 29 FO OC C9 2C FO 08 C9

7BA8- 20 FO 04 C8 4C 9B 7B 48

7BB0- 98 48 A9 00 91 FB 20 81

7BB8- 83 68 A8 68 91 FB AD 38

7BC0- 8E C9 23 FO 3F C9 28 FO

7BC8- 17 AD CF 8F C9 08 FO 37

Appendix B: LADS Object Code

7C90- DC 7C AD 39 8E C9 22 DO

7C98- 06 AD 3A 8E 8D D7 8F AD

7CA0- CF 8F C9 01 DO Dl A9 OB

7CAB- 18 6D CE 8F 8D CE BF 4C

7CB0- 77 7C 20 4D 88 4C E2 7C

7CBB- AD CF 8F C9 02 FO 04 C9

7CC0- 07 DO OC AD CE 8F IB 69

7CC8- 08 8D CE 8F 4C DC 7C C9

7D40- 24 AD F5 8F FO IF 20 1C 7D48- 82 A2 04 20 A6 81 AC E9 7D50- 8F FO OA 30 08 A9 20 20

Appendix B: LADS Object Code

7D58- D6 81 88 DO FA 20 1C 82

7D60- A2 01 20 A2 81 20 66 89

7D68- AD F3 8F FO 11 C9 01 DO

7D70- 05 A9 3C 4C 78 7D A9 3E

7D78- 20 D6 81 20 8B 89 AD FC

7D80- 8F FO 13 20 OA 89 A9 3B

7D88- 20 D6 81 A9 00 85 FB A9

7D90- 02 85 FC 20 F9 88 20 50

7D98- 89 AD D4 8F DO 03 4C 8F

7DAO- 7A AD E7 BF DO 2C EE E7

7DA8- 8F 38 A5 FD ED DO 8F 8D

7DB0- FD 8F A5 FE ED Dl BF 8D

7DB8- FE 8F AD DO 8F 85 FD AD

7DC0- Dl 8F 85 FE 20 1C 82 A9

7DC8- 01 20 35 82 20 E5 80 4C

7DD0- 40 7A 20 1C 82 A9 01 20

7DD8- 35 82 A9 02 20 35 82 AD

7DEO- F5 8F FO 15 20 1C 82 A2

7DE8- 04 20 A6 81 A9 OD 20 D6

7DF0- 81 20 1C 82 A9 04 20 35

7DF8- B2 4C DO 03 B9 38 BE C9

7EOO- 58 FO 62 88 88 B9 38 8E

7E08- C9 29 DO 03 4C EO 7B AD

7E10- D8 8F DO OF AD CF BF C9

7E18- 02 FO 4F C9 05 FO 4B C9

7E20- 01 FO 77 AD CF 8F C9 01

7E28- DO OC AD CE BF 18 69 18

7E30- 8D CE BF 4C DC 7C AD CF

7E38- 8F C9 05 FO 08 A9 31 20

7E40- DO 7E 4C 51 7E AD CE 8F

7E48- 18 69 1C 8D CE 8F 4C DC

7E50- 7C 20 72 89 20 59 89 A9

7E5B- B4 85 FB A9 8F 85 FC 20

7E60- F9 88 4C E2 7C AD DB BF

7E68- DO 33 AD CF 8F C9 02 DO

7E70- OC A9 10 18 6D CE BF 8D

7E78- CE BF 4C 77 7C C9 01 FO

7E80- 10 C9 03 FO OC C9 05 FO

7E88- 08 A9 32 20 DO 7E 4C 51

7E90- 7E A9 14 18 6D CE 8F 8D

7E9B- CE 8F 4C 77 7C AD CF 8F

7EA0- C9 02 DO OC A9 IB 18 6D

7EA8- CE 8F 8D CE BF 4C DC 7C

7EBO- C9 01 FO 10 C9 03 FO OC

7EB8- C9 05 FO 08 A9 33 20 DO

7ECO- 7E 4C 51 7E A9 1C 18 6D

7EC8- CE 8F SD CE BF 4C DC 7C

7ED0- 8D E8 BF 8C EA BF BE E9

7ED8- 8F A9 BA 20 D6 81 68 AA

Appendix B: LADS Object Code

7EE0- 68 A8 93 48 8A 48 98 20

7EE8- 24 ED AD E8 8F AC EA 8F

7EF0- AE E9 8F 60 AO 00 98 99

7EF8- Fl 8D C8 CO FF DO F8 60

7F00- 20 50 89 20 72 89 20 59

7F08- 89 A9 23 85 FB A9 8F 85

7F10- FC 20 F9 88 20 50 89 4C

7F18- 77 7C AO FF C8 B9 Fl 8D

7F20- FO 56 C9 20 DO F6 CB CB

7F28- 8C El 8F 38 A5 EB ED El

7F30- 8F 85 EB A5 EC E9 00 85

7F38- EC AO 00 B9 Fl 8D 49 80

7F40- 91 EB CB B9 Fl 8D C9 20

7F48- FO 05 91 EB 4C 42 7F C8

7F50- B9 Fl SD C9 3D FO 32 88

7F58- A5 FD 91 EB CB A5 FE 91

7F60- EB AE El 8F CA AO 00 BD

7F68- Fl 8D FO 08 99 Fl 8D E8

7F70- C8 4C 67 7F 99 Fl BD 60

7F7B- 20 72 89 A9 5C 85 FB A9

7F80- 8F 85 FC 20 F9 88 4C B7

7F88- 7F B8 BC E2 SF AD DD 8F

7F90- DO 17 C8 C8 C8 8C D6 8F

7F98- A9 Fl IB 6D D6 8F 85 FB

7FA0- A9 8D 69 00 85 FC 20 81

7FA8- 83 AC E2 8F AD D7 8F 91

7FB0- EB AD DB BF CB 91 EB 68

7FB8- 68 4C E2 7C AD E4 8F 85

7FC0- ED AD E5 8F 85 EE 20 CA

7FC8- 80 A9 FF 8D F8 8F 38 A5

7FD0- EB E5 ED A5 EC E5 EE BO

7FD8- 63 A2 00 38 A5 ED E9 02

7FE0- 85 ED A5 EE E9 00 85 EE

7FE8- AO 00 Bl ED 30 OC A5 ED

7FF0- DO 02 C6 EE C6 ED E8 4C

7FFB- EA 7F A5 ED BD EB BF A5

8000- EE 8D EC 8F Bl ED CD D5

8008- BF FO 03 4C 2C 80 E8 8E

BOIO- D6 BF A2 01 AD E6 8F FO

8018- 04 C8 20 CA BO CB B9 3B

B020- 8E FO 53 C9 30 90 4F EB

8028- Dl ED FO Fl AD EB SF B5

8030- ED AD EC 8F B5 EE 20 CA

8038- 80 4C CE 7F AD F8 8F 30

B040- 01 60 AD E7 8F DO 02 FO

8048- 17 20 72 89 20 59 89 20

8050- OA 89 A9 4C B5 FB A9 8F

8058- 85 FC 20 F9 88 20 SO 89

8060- 68 68 AD CE 8F 29 IF C9

Appendix B: LADS Object Code

8068 10 F0 08 AD F3 8F DO 03

8070 4C DC 7C 4C 77 7C EC D6

8078 8F FO 03 4C 2C 80 EE F8

8080 8F FO 03 20 D3 SO AC D6

8088 8F AD E6 8F FO 01 C8 Bl

8090 ED 8D D7 8F C8 Bl ED BD

8098 D8 8F AD F3 8F FO OA C9

80A0 02 DO IE AD D8 8F 8D D7

80A8 8F AD F2 8F FO 13 18 AD

80B0 FO 8F 6D D7 8F 8D D7 8F

B0B8 AD Fl 8F 6D D8 8F 8D D8

80C0 8F AD E7 BF FO 01 60 4C

80C8 2C 80 A5 ED DO 02 C6 EE

80D0 C6 ED 60 20 72 89 A9 96

80D8 85 FB A9 8F 85 FC 20 F9

80E0 88 20 50 89 60 20 1C 82

80E8 A9 01 20 35 82 A9 01 85

80F0 2C A9 90 85 2D 20 5F 81

80F8 EE FF 8F 60 A9 13 85 2C

8100 A9 90 85 2D 20 5F 81 EE

8108 00 90 60 60 A9 25 85 2C

8110 A9 90 85 2D 20 8A 81 20

8118 DC 03 85 2B 84 2A AO 08

8120 Bl 2A 60 8D 3F 90 A9 37

8128 85 2C A9 90 85 2D 20 8A

8130 81 60 AD FF 8F FO 27 A9

8138 49 85 2C A9 90 85 2D 20

8140 8A 81 A9 00 3D FF 8F 60

8148 AD 00 90 FO 11 A9 5B 85

8150 2C A9 90 85 2D 20 8A 81

8158 A9 00 8D 00 90 60 60 AO

8160 08 Bl 2C 85 2A C8 Bl 2C

8168 85 2B A9 F3 85 FB A9 8E

8170 B5 FC AO 00 A9 AO 91 2A

8178 C8 CO IF DO F9 AO 00 Bl

8180 FB 09 BO 91 2A C8 C4 F9

8188 DO F5 20 DC 03 85 2B 84

8190 2A AO OO Bl 2C 91 2A C8

8198 CO 12 DO F7 A2 00 20 D6

81A0 03 60 BE 6D 90 60 8A 8D

81A8 6E 90 EO 04 DO OA A9 EC

81BO 8D 53 AA A9 81 BD 54 AA

8IBS 60 8C 70 90 8E E9 8F AD

SICO 6D 90 C9 01 DO OC 20 OC

81C8 81 08 AC 70 90 AE E9 SF

81DO 28 60 AC 70 90 60 8C 70

8IDS 90 8D 6F 90 AD 6E 90 C9

81E0 02 DO IE AD 6F 90 20 23

81E8 SI 4C D2 81 8D 6F 90 C9

Appendix B: LADS Object Code

B1F0- 8D DO 02 A9 OA SD 90 CO

81F8- AD CI CI 30 FB AD 6F 90

8200- 60 AD 6E 90 C9 04 DO 09

8208- AD 6F 90 20 EC 81 4C D2

8210- 81 AD 6F 90 09 80 20 FO

8218- FD 4C D2 81 A9 00 8D 6E

8220- 90 8D 6D 90 A9 FO 8D 53

8228- AA A9 FD 8D 54 AA 60 AD

8230- 00 CO C9 83 60 C9 01 DO

8238- 03 4C 32 81 C9 02 DO 03

8240- 4C 48 81 4C 5E 81 8D 6F

8248- 90 A9 00 C5 B8 DO IB A9

8250- 02 C5 B9 DO 15 AO 00 Bl

8258- B8 C9 20 DO 05 E6 B8 4C

8260- 57 82 C9 2F 90 04 C9 3A

8268- 90 53 AD 00 02 C9 41 DO

B270- 37 AD 01 02 C9 53 DO 30

8278- AD 02 02 C9 4D DO 29 AD

8280- 03 02 C9 20 E>0 22 AO 00

8288- B9 04 02 C9 00 FO 09 09

8290- 80 99 00 04 C8 4C 88 82

8298- A9 AO 99 00 04 99 01 04

82A0- 99 02 04 68 68 4C 00 7A

82A8- AD 6F 90 C9 3A BO OD C9

82B0- 20 DO 03 4C Bl 00 38 E9

82B8- 30 38 E9 DO 60 A6 AF 86

82C0- 69 A6 BO 86 6A 18 20 OC

82C8- DA 20 Dl 82 68 68 4C 6A

82D0- D4 AO 00 84 94 A9 02 85

82D8- 95 Bl B8 91 94 C8 C9 00

82E0- DO F7 88 88 Bl 94 C9 20

82E8- FO F9 C8 A9 00 91 94 C8

82F0- C8 C8 C8 C8 60 A9 46 85

82F8- BB A9 82 85 BC A9 4C 85

8300- BA A9 FC 85 73 A9 79 85

8308- 74 60 AO 00 A2 FF E8 B9

8310- C9 8C CD Fl 8D FO OA C8

8318- C8 C8 EO 39 DO FO 4C EB

8320- 7A C8 B9 C9 8C CD F2 8D

8328- FO 06 C8 C8 DO EO FO EE

8330- C8 B9 C9 8C CD F3 8D FO

8338- 05 C8 DO D2 FO EO AD F4

8340- 8D C9 20 FO 04 C9 OO DO

8348- D5 BD 71 8D 8D CF 8F BC

8350- A9 8D 8C CE 8F 4C CC 7A

8358- A2 Ol 20 A2 81 A2 06 8E

8360- E9 8F 20 B9 81 AE E9 8F

8368- CA DO F4 20 B9 81 C9 2A

8370- FO OE A9 12 85 FB A9 8F

Appendix B: LADS Object Code

8378- 85 FC 20 F9 88 4C D2 7D

8380- 60 A0 00 Bl FB FO 04 C8

8388- 4C 83 83 8C OF 8F 88 A9

8390- 00 8D D7 8F 8D D8 8F A2

8398- 01 8E E9 8F Bl FB 29 OF

83A0- 8D OD 8F 8D 10 8F A9 00

83A8- 8D OE 8F 8D 11 8F CA FO

83B0- 12 20 D3 83 AD OD 8F 8D

83B8- 10 8F AD OE BF 8D 11 8F

83C0- 4C AE 83 EE E9 8F AE E9

83C8- 8F 20 FA 83 88 CE OF 8F

83DO- DO CA 60 18 OE OD 8F 2E

83D8- OE 8F OE OD 8F 2E OE 8F

83E0- 18 AD 10 8F 6D OD 8F 8D

83E8- OD 8F AD 11 BF 6D OE 8F

83F0- 8D OE 8F OE OD 8F 2E OE

B3F8- 8F 60 IB AD OD 8F 6D D7

8400- 8F 8D D7 BF AD OE 8F 6D

8408- D8 8F SD D8 8F 60 20 F4

B410- 7E AO 00 SC DD 8F 8C FC

B418- 8F 8C F3 8F 8C F2 8F AD

8420- F7 8F DO OC 20 B9 81 BD

842B- D2 BF 20 B9 Bl 3D D3 BF

8430- 20 B9 81 C9 20 DO 08 20

8438- B2 85 68 68 4C BF 7A C9

B440- 20 4C 4C B4 20 B9 Bl DO

B448- 03 4C B2 85 C9 3A DO 03

B450- 4C F6 84 C9 3B DO 73 BC

8458- E8 8F AD F5 8F FO 55 BD

B460- FC BF AD EB BF FO 06 20

846B- 94 84 4C BC 84 20 B9 Bl

B470- FO OE C9 7F 90 03 20 04

847B- 85 99 Fl 8D C8 4C 6D 84

8480- 20 59 89 20 OA 89 20 66

84B8- 89 20 50 89 A9 00 8D E8

8490- 8F 4C BC B4 8D FC 8F BD

8498- EB BF AO 00 20 B9 81 DO

84A0- 07 99 00 02 AC EB 8F 60

84AS- 10 03 20 El 87 99 00 02

84B0- CB 4C 9C 84 20 B9 81 FO

84B8- 03 4C B4 84 20 B2 85 AD

84C0- E8 8F DO 05 6B 68 4C BF

B4C8- 7A 60 C9 3E FO 5B C9 3C

84D0- FO 5F C9 2B DO 03 EE F2

84D8- BF C9 2A DO 03 4C 39 B5

B4E0- C9 2E FO 16 C9 24 FO 15

84EB- C9 7F 90 03 20 04 B5 99

84F0- Fl 8D C8 4C 44 84 8D F7

84F8- 8F 60 4C 56 86 99 Fl BD

Appendix B: LADS Object Code

8500- CB 4C Di 85 38 E9 7F 8D

8508- E0 8F A2 FF CE EO 8F FO

8510- 08 E8 BD DO DO 10 FA 30

8518- F3 E8 BD DO DO 30 07 99

8520- Fl 8D CS 4C 19 85 29 7F

8528- 60 A9 02 8D F3 8F 4C 44

8530- 84 A9 01 8D F3 BF 4C 44

8538- B4 AD F3 8F FO 20 A9 2A

8540- 99 Fl BD CB EE DD 8F AD

B548- F3 8F C9 01 FO 08 A5 FE

8550- 8D D7 BF 4C 44 84 A5 FD

855B- BD D7 BF 4C 44 84 20 44

8560- 84 AD E7 BF FO OB A9 2A

8568- 20 D6 81 20 66 89 20 50

8570- 89 AD DD 8F DO 20 AO 00

8578- B9 Fl 8D C9 20 FO 04 CB

8580- 4C 7a 85 C8 84 FB A9 Fl

8588- 18 65 FB 85 FB A9 8D 69

8590- 00 B5 FC 20 81 83 AD E7

8598- 8F FO OS AD F4 8F FO 03

85A0- 20 AO B7 AD D7 BF 85 FD

85A8- AD D8 BF B5 FE 68 68 4C

B5B0- 8F 7A 99 Fl BD C8 CO FF

85B8- DO F8 99 Fl BD 20 B9 81

85C0- 20 B9 81 FO 06 A9 00 BD

85CS- F7 8F 60 A9 01 8D D4 8F

85D0- 60 A2 00 20 B9 81 FO 2C

B5DS- C9 3A FO 28 C9 20 FO F3

85E0- C9 3B FO 20 C9 2C FO OF

B5E8- C9 29 FO OB 9D DE 8E EB

85F0- 99 Fl 8D CB 4C D3 85 8E

85F8- DE 8F 99 Fl BD C8 20 18

8600- B6 4C 44 84 BD EB 8F A9

S60B- 00 BE DE BF 99 Fl BD 20

8610- IB 86 AD EB BF 4C 47 84

8618- A9 00 3D D7 BF 8D D8 BF

8620- AA OE D7 8F 2E D8 8F OE

8628- D7 8F 2E D8 8F OE D7 8F

8630- 2E DB BF OE D7 BF 2E DB

8638- BF BD DE BE C9 41 90 02

8640- E9 07 29 OF OD D7 8F 8D

864B- D7 BF EB EC DE BF DO Dl

8650- EE DD BF A9 01 60 CO 00

8658- FO OE AE E7 8F DO 09 4B

B660- 98 48 20 1A 7F 68 A8 68

8668- 99 Fl 8D C8 20 B9 81 99

8670- Fl 8D CB C9 42 DO 68 A9

8678- 00 BD ED BF AD E7 8F FO

8680- 17 8C EA 8F AD F9 8F FO

Appendix B: LADS Object Code

8688- OF 20 59 89 20 0A 89 20

8690- 32 89 20 OA 89 AC EA 8F

8698- 20 B9 81 99 Fl 8D C8 C9

86A0- 20 DO F5 20 B9 81 99 Fl

86A8- 8D C8 C9 22 DO 45 20 B9

86B0- Bl DO 03 4C 85 87 C9 3A

86B8- DO 03 4C 88 87 C9 3B DO

86C0- OC 20 94 84 AE F5 8F 8E

86C8- FC 8F 4C 85 87 C9 22 DO

86D0- 03 4C AE 86 AE E7 8F DO

86D8- 09 20 EB 88 4C AE 86 4C

86E0- 56 BA 99 Fl 8D AA 8C EA

86E8- 8F 20 C3 88 AC EA 8F C8

86F0- 4C AE 86 A2 00 8E EE 8F

86F8- 9D 06 8F E8 AD EE 8F DO

87O0- 75 20 B9 81 FO 43 C9 3A

8708- FO 3F C9 3B DO OC 20 94

8710- 84 AE F5 8F 8E FC 8F 4C

8718- 49 87 8D 80 8E AD E7 8F

8720- DO OD AD 80 8E C9 20 DO

8728- D3 20 EB 88 4C FC 86 AD

8730- 80 8E 99 Fl 8D C8 C9 20

8738- FO 18 C9 00 FO 14 C9 3A

8740- FO 10 9D 06 8F E8 4C FC

8748- 86 EE EE 8F 8D 81 8E 4C

8750- 1A 87 A9 06 85 FB A9 8F

8758- 85 FC BC EA 8F 20 81 83

8760- AE D7 8F 20 C3 88 AC EA

8768- 8F A9 00 A2 05 9D 06 8F

8770- CA DO FA 4C FC 86 AD E7

8778- 8F DO 03 20 EB 88 AD 81

8780- 8E C9 3A FO 03 20 B2 85

8788- 8D F7 8F EE FB 8F 68 68

8790- AD E7 8F FO 08 AD F9 8F

8798- FO 03 4C 65 7D 4C 8F 7A

87AO- AD E7 8F C9 02 DO 01 60

87A8- 20 1C 82 A2 02 20 A6 81

87BO- 38 AD D7 8F E5 FD 8D D5

87B8- 8F AD D8 8F E5 FE 8D D6

87C0- 8F A9 00 20 D6 81 AD D5

87C8- 8F DO 03 CE D6 8F CE D5

87D0- 8F DO EE AD D6 8F DO E9

87D8- 20 1C 82 A2 01 20 A2 81

87E0- 60 38 E9 7F 8D EO 8F A2

87E8- FF CE EO 8F FO 08 E8 BD

87F0- DO DO 10 FA 30 F3 E8 BD

87F8- DO DO 30 07 99 00 02 CS

BBOO- 4C F6 87 29 7F 60 AO 00

8808- A2 00 B9 Fl 8D C9 2B FO

Appendix B: LADS Object Code

88F8- 60 AO 00 Bl FB FO OA 20

8900- D6 81 20 85 89 C8 4C FB

8908- 88 60 A9 20 20 D6 81 20

8910- 85 B9 60 8E E9 BF AD FA

8918- 8F FO OB SA 20 3D 8A 20

8968- 85 FB A9 8D 85 FC 20 F9

8970- 88 60 A9 07 20 D6 81 A9

8978- 12 20 D6 81 20 66 89 A9

8980- OD 20 D6 81 60 AE E7 SF

8988- DO Ol 60 AE F5 8F DO 01

8990- 60 8D E8 8F 20 1C 82 A2

Appendix B: LADS Object Code

-

899B- 04 20 A6 81 AD E8 8F 20

S9A0- D6 81 20 1C 82 A2 01 20

89A8- A2 81 AD E8 8F 60 AE E7

89B0- 8F DO 01 60 AE F5 8F DO

39B8- 01 60 20 1C 82 A2 04 20

89C0- A6 81 AD FA 8F FO 09 AD

89C8- E9 8F 20 3D 8A 4C D8 89

89D0- A9 00 AE E9 8F 20 24 ED

89D8- 20 1C 82 A2 01 20 A2 81 —

89E0- 60 AE E7 8F DO 01 60 AE

89E8- F5 8F DO 01 60 20 1C 82

89F0- A2 04 20 A6 81 AE FA 8F

89F8- FO OD A5 FE 20 3D 8A A5

8A00- FD 20 3D 8A 4C OE 8A A5

8A08- FE A6 FD 20 24 ED 20 1C

8A10- 82 A2 01 20 A2 81 60 AE

8A18- E7 8F DO 01 60 AE F5 8F

8A20- DO 01 60 20 1C 82 A2 04

8A28- 20 A6 81 AD D3 8F AE D2

8A30- 8F 20 24 ED 20 1C 82 A2

8A3B- 01 20 A2 81 60 48 29 OF

BA40- A8 B9 El 8D AA 68 4A 4A

8A4B- 4A 4A A8 B9 El SD 20 D6

8A50- 81 8A 20 D6 81 60 C9 46

8A58- DO 08 20 B9 8A 68 68 4C

8A60- BF 7A C9 45 DO 06 20 12

8A6B- 8B 4C 5D 8A C9 44 DO 03

8A70- 4C 5B 8B C9 50 DO 03 4C

8A7B- CI SB C9 4E DO 03 4C 02

BA80- 8C C9 4F DO 03 4C ED 8B

8A88- C9 53 DO 03 4C 9A 8C C9

8A90- 48 DO 03 4C B4 8C 99 Fl

8A9B- BD 20 59 89 20 OA 89 20

8AA0- 32 89 20 72 89 20 66 89

8AA8- A9 B4 85 FB A9 8F 85 FC

8AB0- 20 F9 88 20 50 89 4C D4

8AB8- 8B 20 B9 81 C9 20 FO 03 __

8AC0- 4C B9 8A AO 00 20 B9 81

8AC8- C9 00 FO OE C9 7F 90 03

BADO- 20 04 85 99 Fl 8D C8 4C

8AD8- C5 BA 84 F9 AO 00 B9 F1

8AE0- 8D FO 07 99 F3 8E C8 4C

8AE8- DE BA AD E7 BF DO 06 20

8AF0- 32 89 20 OA 89 20 66 89 ^_,

8AF8- 20 50 89 20 E5 80 A2 01

8B00- 20 A2 81 20 B9 Bl 20 B9

8B08- 81 20 B2 85 A2 00 BE D4

SB 10- 8F 60 A9 2E 20 D6 81 A9 """'

SB18- 45 20 D6 81 A9 4E 20 D6

Appendix B: LADS Object Code

8B20- 81 A9 44 20 D6 81 A9 20

8B28- 20 D6 81 20 B9 81 20 B9

8B30- 8A AD E7 8F F0 03 EE D4

8B38- 8F EE E7 8F 38 A5 FD ED

8B40- DO 8F 8D FD 8F A5 FE ED

8B48- Dl 8F 8D FE 8F AD DO 8F

8B50- 85 FD AD Dl 8F 85 FE 20

8B58- OE 84 60 AD E7 8F FO IE

8B60- 20 B9 81 99 Fl 8D AO 00

8B68- 20 B9 81 FO 14 C9 7F 90

8B70- 03 20 04 85 99 Fl 8D 99

8B78- F3 8E C8 4C 68 8B 4C D4

8B80- 8B 84 F9 20 66 89 20 50

8B88- 89 EE F4 8F 20 FC 80 A2

8B90- 02 20 A6 81 AD DO 8F 20

8B98- D6 81 AD Dl 8F 20 D6 81

8BA0- AD FD 8F 20 D6 81 AD FE

8BA8- 8F 20 D6 81 20 1C 82 A2

8BB0- 01 20 A2 81 20 B2 85 68

8BB8- 68 A2 OO 8E D4 8F 4C 8F

8BC0- 7A AD E7 8F FO OE 20 OB

8BC8- 81 EE F5 8F 20 1C 82 A2

8BD0- 01 20 A2 81 20 B9 81 FO

BBD8- 07 C9 3A FO 06 4C D4 BB

8BE0- 20 B2 85 68 68 A2 00 8E

8BE8- D4 8F 4C 8F 7A A9 2E 20

BBFO- D6 81 A9 4F 20 D6 81 20

8BF8- 50 89 A9 01 8D F6 8F 4C

8C00- D4 8B AD E7 BF FO CD 20

8C08- B9 81 C9 50 FO OC C9 4F

8C10- FO 3A C9 53 FO 6A C9 48

8C18- FO 4C A9 2E 20 D6 81 A9

8C20- 4E 20 D6 81 A9 50 20 D6

8C28- 81 20 50 89 CE F5 8F 20

8C30- 1C 82 A2 04 20 A6 81 A9

8C38- OD 20 D6 81 A9 04 20 35

8C40- 82 20 1C 82 A2 01 20 A2

8C48- 81 4C D4 8B A9 2E 20 D6

8C50- 81 A9 4E 20 D6 81 A9 4F

8C58- 20 D6 81 20 50 89 A9 00

8C60- 8D F6 8F 4C D4 8B A9 2E

8C68- 20 D6 81 A9 4E 20 D6 81

8C70- A9 48 20 D6 81 20 50 89

8C78- A9 00 8D FA 8F 4C D4 8B

8C80- A9 2E 20 D6 81 A9 4E 20

8C88- D6 81 A9 53 20 D6 81 20

8C90- 50 89 A9 OO 8D F9 8F 4C

8C98- D4 8B A9 2E 20 D6 81 A9

8CA0- 53 20 D6 81 20 50 89 AD

Appendix B: LADS Object Code

8CA8- E7 8F FO 05 A9 01 8D F9

8CBO- 8F 4C D4 8B A9 2E 20 D6

8CB8- 81 A9 48 20 D6 81 20 50

8CC0- 89 A9 01 8D FA 8F 4C D4

8CC8- 8B 4C 44 41 4C 44 59 4A

8D70- 50 01 05 09 00 08 08 08

8D78- 01 08 05 06 01 02 02 00

8D80- 00 00 02 00 02 04 04 01

8D88- 00 01 00 00 00 00 00 00

BD90- 00 00 08 08 01 01 01 07

8D98- 08 08 03 03 03 00 00 03

8DA0- 00 00 O0 00 00 00 00 00

8DA8- OO Al A0 20 60 B0 F0 90

BDB0- CI DO A2 4C 81 84 86 C8

8DB8- 88 CA C6 E8 E6 CO EO El

8DC0- 3B 61 18 AA A8 BA 98 4B

8DC8- 68 OO 30 10 21 01 41 24

8DD0- 50 70 22 62 42 D8 58 02

8BDB- 08 28 40 F8 7B BA 9A B8

8DE0- EA 30 31 32 33 34 35 36

8DE8- 37 38 39 41 42 43 44 45

8DF0- 46 00 OO 00 OO OO 00 00

8DF8- 00 OO 00 00 00 00 OO 00

8E00- 00 00 00 OO 00 00 00 00

8E08- 00 OO 00 OO 00 00 OO 00

8E10- OO OO 00 OO 00 00 00 OO

8E18- 00 00 00 00 00 OO 00 00

8E20- 00 00 00 00 00 00 00 00

8E28- 00 OO 00 00 00 00 00 00

Appendix B: LADS Object Code

8E30- 00 00 OO 00 00 00 OO 00

8E38- 00 00 00 00 00 00 00 00

8E40- 00 00 00 00 00 00 00 00

8E48- 00 OO 00 00 00 00 00 00

8E50- 00 00 00 00 00 00 00 00

8E58- 00 00 OO 00 00 00 00 00

8E60- 00 00 00 00 00 00 OO 00

8E68- 00 00 00 00 00 00 00 00

SE70- 00 00 00 00 00 00 OO 00

8E78- 00 00 00 00 OO 00 00 00

8E80- 00 00 00 00 00 00 00 00

8E88— 00 00 00 00 00 00 00 00

8E90- 00 00 OO 00 00 00 OO 00

8E98— 00 00 00 00 00 00 00 00

8EA0- 00 00 00 00 00 00 00 00

8EA8- 00 00 00 00 00 00 00 00

8EB0- 00 00 00 00 00 00 00 00

8EB8- OO 00 00 00 00 00 OO OO

8EC0- 00 00 00 00 00 00 00 00

8EC8- OO 00 00 00 00 00 OO 00

8ED0- 00 00 OO 00 00 OO OO 00

8ED8- 00 00 OO 00 OO 00 00 00

8EE0- 00 00 00 00 00 00 00 00

8EE8- 00 00 00 00 00 00 00 00

8EF0- 00 00 00 00 00 00 00 00

8EF8- 00 00 00 00 OO 00 00 00

BFOO- 00 00 00 00 00 00 00 00

8F0S— 00 00 00 00 00 00 00 00

8F10- 00 00 4E 4F 20 53 54 41

8F18- 52 54 20 41 44 44 52 45

8F20- 53 53 00 2D 2D 2D 2D 2D

8F28- 2D 2D 2D 2D 2D 2D 2D 2D

8F30- 2D 2D 2D 2D 2D 2D 2D 20

8F38- 42 52 41 4E 43 48 20 4F

8F40- 55 54 20 4F 46 20 52 41

8F48- 4E 47 45 00 55 4E 44 45

8F50- 46 49 4E 45 44 20 4C 41

8F58- 42 45 4C 00 ID ID ID ID

8F60- ID ID ID ID ID 20 4E 41

8F68- 4B 45 44 20 4C 41 42 45

8F70- 4C OO ID ID ID ID ID 20

8F78- 3C 3C 3C 3C 3C 3C 3C 3C

8F80- 20 44 49 53 4B 20 45 52

8F88- 52 4F 52 20 3E 3E 3E 3E

8F90- 3E 3E 3E 3E 20 00 ID ID

8F98- ID ID ID 20 2D 2D 20 44

8FA0- 55 50 4C 49 43 41 54 45

8FA8- 44 20 4C 41 42 45 4C 20

8FB0- 2D 2D 20 OO ID ID ID ID

Appendix B: LADS Object Code

9060- 00 OO OO 00 00 OO 00 53 9068- 95 53 94 53 93 00 00 00 9070- 00 01

8FB8- ID 20 2D 2D 20 53 59 4E

BFCO- 54 41 58 20 45 52 52 4F

8FC8- 52 20 2D 2D 20 00 00 00

8FDO- 00 OO OO 00 00 00 00 00

8FD8- OO 00 OO 00 00 OO 00 00 —

8FE0- OO OO OO OO 00 OO 00 00

8FE8- 00 OO 00 00 00 OO 00 00

8FFO- 00 OO OO 00 00 00 00 00

8FF8- 00 00 OO OO 00 OO 00 00

9000- OO 01 OO 01 00 00 01 06

Machine Language Editor for Atari and - Commodore

Charles Brannon

Have you ever typed in a long machine language program? Chances are you typed in hundreds of DATA statements, numbers, and commas. You're never sure if you've typed them in right. So you go back, proofread, try to run the program, crash, go back and proofread again, correct a few typing errors, run again, crash, recheck your typing—frustrating, isn't it?

Until now, though, that has been the best way to enter machine language into your computer. Unless you happen to own an assembler and are willing to wrangle with machine language on the assembly level, it is much easier to enter a BASIC program that reads the DATA statements and POKEs the numbers into memory.

Some of these BASIC loaders, as they are known, use a checksum to see if you've typed the numbers correctly. The simplest checksum is just the sum of all the numbers in the DATA statements. If you make an error, your checksum will not match up. Some programmers make the task easier by calculating checksums every ten lines or so, and you can thereby locate your errors more easily.

Almost Foolproof

"MLX" lets you type in long machine language (ML) listings with almost foolproof results. Using MLX, you enter the numbers from a special list that looks similar to BASIC DATA statements. MLX checks your typing on a line-by-line basis. It won't let you enter illegal characters when you should be typing numbers, such as a lowercase L for a 1 or an O for a 0. It won't let you enter numbers greater than 255, which are not permitted in ML DATA statements. It will prevent you from entering the wrong numbers on the wrong line. In short, MLX should make proofreading obsolete!

In addition, MLX will generate a ready-to-use tape or disk file. For the Commodore, you can then use the LOAD com-

Appendix C: Commodore and Atari Machine Language Editor

mand to read the program into the computer, just as you would with any program. Specifically, you enter:

LOAD "filename", 1,1 (for tape)

or

LOAD "filename", 8,1 (for disk)

To start LADS you need to type SYS 11000 (Commodore). For complete instructions for the use of LADS, please read Appendix A.

For the Atari, MLX will create a binary file for use with DOS. Atari MLX can create a boot disk or tape version of LADS, but this is not recommended.

Getting Started

To get started, type in and save MLX (VIC owners must have at least 8K of extra memory attached). When you are ready to enter LADS using MLX, Commodore 64 and VIC owners should enter the line below before loading MLX:

POKE 55,0: POKE 56,42: CLR

Commodore PET/CBM owners should use:

POKE 52,0: POKE 53,42: CLR

When you're ready to type in LADS, the program will ask you for several numbers: the starting address and the ending address. In addition, the Atari MLX will request a "Run/Init Address".

Below are the numbers you'll need.

PET/CBM, VIC and Commodore 64: Starting address 11000 Ending address 15985

Atari:

Starting address 32768 Ending address 39607 Run/Init address 32768

The Atari version will then ask you to press either T for a boot tape, or D for disk; press D. Next, you'll be asked if you —

want to generate a boot disk or a binary file; press F.

Next you'll see a prompt. The prompt is the current line you are entering from the listing. Each line is six numbers plus a checksum. If you enter any of the six numbers wrong, or en-

Appendix C: Commodore and Atari Machine Language Editor

ter the checksum wrong, MLX will ring a buzzer and prompt you to reenter the line. If you enter it correctly, a pleasant bell tone will sound and you proceed to the next line.

A Special Editor

You are not using the normal screen editor with MLX. For example, it will accept only numbers as input. If you need to make a correction, press the DEL/BACKS key (Atari) or the INST/DEL key (Commodore). The entire number is deleted. You can press it as many times as necessary back to the start of the line. If you enter three-digit numbers as listed, the computer will automatically print the comma and prepare to accept the next number. If you enter less than three digits (by omitting leading zeros), you can press either the comma, space bar, or RETURN key to advance to the next number. When you get to the checksum value, the Atari MLX will emit a low drone to remind you to be careful. The checksum will automatically appear in inverse video; don't worry, it's highlighted for emphasis.

When testing MLX, we've found that it makes entering long listings extremely easy. We have tested MLX with people lacking any computer background whatsoever. No one here has managed to enter a listing wrong with it.

Done at Last!

When you finish typing (assuming you type the entire listing in one session), you can then save the completed program on tape or disk. Follow the screen instructions. (For Atari we suggest that you use the filename AUTORUN.SYS when saving a copy of LADS. This way LADS will automatically load and run when you boot up your computer.) If you get any errors while saving, you probably have a bad disk, or the disk is full, or you made a typo when entering the actual MLX program. (Remember, it can't check itself!)

Command Control

What if you don't want to enter the whole program in one sitting? MLX lets you enter as much as you want, save that portion, and then reload the file from tape or disk when you want to continue. MLX recognizes these few commands:

S: Save

L: Load

Appendix C: Commodore and Atari Machine Language Editor

N: New Address

D: Display For the Atari, hold down the CTRL key while you type the appropriate key. Hold down SHIFT on Commodore machines to enter a command key. You will jump out of the line you've been typing, so it's best to perform these commands at a new prompt. Use the Save command to save what you've been working on. It will write the tape or disk file as if you've finished, but the tape or disk won't work, of course, until you finish the typing. Remember what address you stop on. The next time you run MLX, answer all the prompts as you did before, then insert the disk or tape. When you get to the entry prompt, press CTRL-L (Atari) or SHIFT-L (Commodore) to reload the file into memory. You'll then use the New Address command to resume typing.

New Address and Display

Here's how the New Address command works. After you press SHIFT-N or CTRL-N, enter the address where you previously stopped. The prompt will change, and you can then continue typing. Always enter a New Address that matches up with one of the line numbers in the special listing, or else the checksum won't match up.

You can use the Display command to display a section of your typing. After you press CTRL-D or SHIFT-D, enter two addresses within the line number range of the listing. You can abort the listing by pressing any key.

Tricky Business

The special commands may seem a little confusing at first, but as you work with MLX, they will become easy and valuable. What if you forgot where you stopped typing, for instance? Use the Display command to scan memory from the beginning to the end of the program. You can stop a listing by hitting any key.

Making Copies

You can use the MLX Save and Load commands to make copies of the completed ML program. Use Load to reload the tape or disk, then insert a new tape or disk and use the Save command to make a new copy.

418

Appendix C: Commodore and Atari Machine Language Editor

PET and VIC Users

The Commodore 64, PET, and VIC data are almost exactly the same. There are some lines, though, that are different. Commodore 64, PET, and VIC owners should use the Commodore 64 data (Program B-l) with MLX. VIC owners should substitute the lines found in Program B-2 (VIC) for the same lines in Program B-l. PET owners should type in and save the 64 data, then make the necessary changes shown in Program B-3a and B-3b using the built-in PET monitor. Commodore 64 users should use the data in Program B-l as is.

We hope you will find MLX to be a true labor-saving utility. Since it has been thoroughly tested by entering actual programs, you can count on it as an aid for generating bug-free machine language. And be sure to save MLX; it will be used for future all machine language programs in COMPUTE!, COMPUTE'S Gazette, and COMPUTE! Books.

Program C-l. Commodore 64 MLX

Refer to Appendix E "How to Type In BASIC Programs" before entering this program.

100 PRINT"{CLR}g63";CHR$(142);CHR$(8);:POKE53281,1 :POKE53280,1

101 POKE 788,52:REM DISABLE RUN/STOP 110 PRINT"{RVS}{39 SPACES}";

120 PRINT" {RVS} {14 SPACES }{ RIGHT } {OFF } E* >|£{ RVS }

{RIGHT} {RIGHT}{2 SPACES}g*3{OFF}i*3£TRVS}£

{RVS}{14 SPACES}"; 130 PRINT" {RVS } {14 SPACES} {RIGHT} Eg>|{RIGHT}

{2 RIGHT} {OFF}£{RVS}£g*i{OFF}i*i{RVS}

{14 SPACES}"; 140 PRINT"{RVSJ{41 SPACES}" 200 PRINT"{2 DOWN}{PUR}{BLK}{9 SPACES}MACHINE LANG

UAGE EDITOR{5 DOWN}" 210 PRINT"g53{2 UP}STARTING ADDRESS?{8 SPACES}

{9 LEFT}"; 215 INPUTS:F=1-F:C$=CHR$(31+119*F) 2 20 IFS<256OR(S>40960ANDS<49152)ORS>5 3247THENGOSUB

3000:GOTO210 225 PRINT:PRINT:PRINT 230 PRINT"E53{2 UP}ENDING ADDRESS?{8 SPACES}

{9 LEFT}";:INPUTE:F=1-F:C$=CHR$(31+119*F) 240 IFE<256OR(E>40960ANDE<49152)ORE>53247THENGOSUB

3000:GOTO230 250 IFE<STHENPRINTC$;"{RVS}ENDING < START

{2 SPACES}":GOSUB1000:GOTO 230 260 PRINT:PRINT:PRINT 300 PRINT"{CLR}";CHR$(14):AD=S:POKEV+21,0

Appendix C: Commodore and Atari Machine Language Editor

310 A=1:PRINTRIGHT$("0000"+MID$(STR$(AD),2),5);":"

315 F0RJ=AT06

320 GOSUB570:IFN=-1THENJ=J+N:GOTO320

390 IFN=-211THEN 710

400 IFN=-204THEN 790

410 IFN=-206THENPRINT:INPUT"{DOWN}ENTER NEW ADDRES

S";ZZ 415 IFN=-206THENIFZZ<SORZZ>ETHENPRINT"{RVS}OUT OF

{SPACE}RANGE":GOSUB1000:GOTO410 417 IFN=-206THENAD=ZZ:PRINT:GOTO310 420 IF N<>-196 THEN 480

430 PRINT?:INPUT"DISPLAY:FROM";F:PRINT,"TO";:INPUTT 440 IFF<SORF>EORT<SORT>ETHENPRINT"AT LEAST";S;"

{LEFT}, NOT MORE THAN";E:GOTO430

450 FORI=FTOTSTEP6:PRINT:PRINTRIGHT$("0000"+MID$(S, TR?(I),2),5);":";

451 FORK=0TO5:N=PEEK(I+K):PRINTRIGHT$("00" +MID$ ( ST R$(N),2),3);\";

460 GETA$:IFA$ >""THENPRINT:PRINT:GOT0310 470 NEXTK:PRINTCHR$(20);:NEXTI:PRINT:PRINT:GOTO310 480 IFN<0 THEN PRINT:GOTO310 490 A(J)=N:NEXTJ

500 CKSUM=AD-INT(AD/256)*256:F0RI=1T06:CKSUM=(CKSU M+A(I))AND255:NEXT

510 PRINTCHR$(18 ) ; :GOSUB570:PRINTCHR$(146 ) ;

511 IFN=-1THENA=6:G0T0315

515 PRINTCHR$(20):IFN=CKSUMTHEN530

520 PRINT:PRINT"LINE ENTERED WRONG : RE-ENTER":PRI

NT:GOSUB1000:GOTO310 530 GOSUB2000 540 F0RI=1T06:POKEAD+I-1,A(I):NEXT:POKE542 72,0:POK

E54273,0 550 AD=AD+6:IF AD<E THEN 310 560 GOTO 710 570 N=0:Z=0

580 PRINT"i£3";

581 GETA$:IFA$=""THEN581

585 PRINTCHR$(20);:A=ASC(A$):IFA=130RA=440RA=32THE

N670 590 IFA>128THENN=-A:RETURN 600 IFA<>20 THEN 630 610 GOSUB690:IFI=1ANDT=44THENN=-1:PRINT"{OFF}

{LEFT} {LEFT}";:GOTO690 620 GOTO570

630 IFA<48ORA>57THEN580 640 PRINTA$;:N=N*10+A-48

650 IFN>255 THEN A=20:GOSUB1000:GOTO600 660 Z=Z+1:IFZ<3THEN580

Appendix C: Commodore and Atari Machine Language Editor

670 IFZ=0THENGOSUB1000:GOTO570 680 PRINT",";:RETURN

690 S%=PEEK(209)+256*PEEK(210)+PEEK(211)

691 F0RI=1T03:T=PEEK(S%-I)

695 IFT<>44ANDT<>58THENPOKES%-I,32:NEXT

700 PRINTLEFT$("{3 LEFT}",1-1);:RETURN

710 PRINT"{CLR}{RVS}*** SAVE ***{3 DOWN}"

715 PRINT"{2 DOWN}(PRESS {RVS}RETURN{OFF} ALONE TO

CANCEL SAVE){DOWN}" 720 F$="":INPUT"{DOWN} FILENAME";F$:IFF$=""THENPRI

NT:PRINT:GOTO310 730 PRINT:PRINT"{2 DOWN}{RVS}T{OFF}APE OR {RVS}D

{OFF}lSK: (T/D)" 740 GETA$ :IFA$o"T"ANDA$<>"D"THEN740 7 50 DV=1-7*(A$="D"):IFDV=8THENF$="0:"+F$:OPEN15,8,

15,"S"+F$:CLOSE15 760 T$=F$:ZK=PEEK(53)+256*PEEK(54)-LEN(T$):POKE782

,ZK/256

762 POKE781,ZK-PEEK(782)*256:POKE780,LEN(T$):SYS65 469

763 POKE780,1:POKE781,DV:POKE782,1:SYS65466

765 K=S:POKE254,K/256:POKE253,K-PEEK(254)*256:POKE 780,253

766 K=E+1:POKE782,K/256:POKE781,K-PEEK(782)*256:SY S65496

770 IF(PEEK(783)AND1)OR(191ANDST)THEN780 775 PRINT"{DOWNJDONE.{DOWN}":GOTO310

780 PRINT"{DOWN}ERROR ON SAVE.{2 SPACES}TRY AGAIN. ":IFDV=1THEN720

781 OPEN15,8,15:INPUT#15,El$,E2$:PRINTEl$;E2$:CLOS E15:GOTO720

790 PRINT"{CLR}{RVS}*** LOAD ***{2 DOWN?"

795 PRINT"{2 DOWN}(PRESS {RVS} RETURN {OFF} ALONE TO

CANCEL LOAD)" 800 F$="":INPUT"{2 DOWN} FILENAME";F$:IFF$=""THENP

RINT:GOTO310 810 PRINT:PRINT"{2 DOWN}{RVS}T{OFF}APE OR {RVS}D

{OFF}lSK: (T/D)" 820 GETA$:IFA$<>"T"ANDA$<>"D"THEN820 830 DV=1-7*(A$="D"):IFDV=8THENF$="0:"+F$

840 T$=F$:ZK=PEEK(53)+256*PEEK(54)-LEN(T$):POKE782 ,ZK/256

841 POKE781,ZK-PEEK(782)* 256:POKE780,LEN(T$):SYS65 469

845 POKE780,1:POKE781,DV:POKE782,1:SYS65466 850 POKE780,0:SYS65493

860 IF(PEEK(783)AND1)OR(191ANDST)THEN870 865 PRINT"{DOWN}DONE.":GOTO310

Appendix C: Commodore and Atari Machine Language Editor

870 PRINT"{DOWN}ERROR ON LOAD.{2 SPACES}TRY AGAIN.

{DOWN}":IFDV=1THEN800 880 OPEN15,8,15:INPUT*15,El$,E2$:PRINTE1$;E2$:CLOS

E15:GOTO800

1000 REM BUZZER

1001 POKE54296,15:POKE54277,45:POKE54278,165

1002 POKE54276,33:POKE 54273,6:POKE54272,5

1003 FORT=1TO200:NEXT:POKE54276,32:POKE54273,0:POK E54272,0:RETURN

2000 REM BELL SOUND

2001 POKE54296,15:POKE54277,0:POKE54278,247

2002 POKE 54276,17:POKE54273,40:POKE54272,0

2003 FORT=1TO100:NEXT:POKE54276,16:RETURN

3000 PRINTC$;"{RVS}NOT ZERO PAGE OR ROM":GOTO1000

Program C-2. VIC MLX

Refer to Appendix E "How to Type In BASIC Programs" before entering this program.

100 PRINT"{CLR}{PUR}";CHR$(142);CHR$(8);

101 POKE 788,194:REM DISABLE RUN/STOP 110 PRINT"{RVS}{14 SPACES}"

120 PRINT"{RVS} {RIGHT}{OFF}E*3£{RVS} {right}

{RIGHT}{2 SPACES}|*3{OFF}i*T£{RVS}£{RVS} " 130 PRINT"{RVS} {RIGHT} |g3{RIGHT} {2 RIGHT} {OFF}

£{RVS}£g*3{OFF}|<*3 {RVS} " 140 PRINT"TRVS}{14 SPACES}" 200 PRINT" [2 DOWN}{PUR}{BLK}A FAILSAFE MACHINE":PR

INT"LANGUAGE EDITOR{5 DOWN}" 210 PRINT"{BLK}{3 UPjSTARTING ADDRESS":INPUTS:F=1-

F:C$=CHR$(31+119*F) 220 IFS<256ORS>32767THENGOSUB3000:GOTO210 225 PRINT:PRINT:PRINT:PRINT 230 PRINT"{BLK}{3 UPjENDING ADDRESS":INPUTE:F=1-F:

C$=CHR$(31+119*F) 240 IFE<256ORE>32 76 7THENGOSUB3000:GOTO2 30 250 IFE<STHENPRINTC$;"{RVS}ENDING < START

{2 SPACES}":GOSUB1000:GOTO 230 260 PRINT:PRINT:PRINT 300 PRINT"{CLR}";CHR$(14):AD=S 310 PRINTRIGHT$("0000"+MID$(STR$(AD),2),5);":"?:FO

RJ=1T06 320 GOSUB570:IFN=-1THENJ=J+N:GOTO320 390 IFN=-211THEN 710 400 IFN=-204THEN 790 410 IFN=-206THENPRINT:INPUT"{DOWN}ENTER NEW ADDRES

s"?zz

415 IFN=-206THENIFZZ<SORZZ>ETHENPRINT"{RVSjOUT OF {SPACEjRANGE":GOSUB1000:GOTO410

Appendix C: Commodore and Atari Machine Language Editor

417 IFN=-206THENAD=ZZ:PRINT:GOT0310

420 IF N<>-196 THEN 480

430 PRINT:INPUT"DISPLAY:FROM";F:PRINT,"TO";:INPUTT

440 IFF<SORF>E0RT<SORT>ETHENPRINT"AT LEAST";S ; "

{LEFT}, NOT MORE THAN";E:GOTO430 450 FORI=FTOTSTEP6:PRINT:PRINTRIGHT$("0000"+MID$(S

TR$(I),2),5);":"; 455 FORK=0TO5:N=PEEK(I+K):IFK=3THENPRINTSPC(10); 457 PRINTRIGHT$("00"+MID$(STR$(N),2),3);","; 460 GETA$:IFA$>""THENPRINT:PRINT:GOTO310 470 NEXTK:PRINTCHR$(20);:NEXTI:PRINT:PRINT:GOT0310 480 IFN<0 THEN PRINT:GOTO310 490 A(J)=N:NEXTJ 500 CKSUM=AD-INT(AD/256)*256:F0RI=1T06:CKSUM=(CKSU

M+A(I))AND255:NEXT 510 PRINTCHR$(18);:GOSUB570:PRINTCHR$(20) 515 IFN=CKSUMTHEN530 520 PRINT:PRINT"LINE ENTERED WRONG":PRINT"RE-ENTER

":PRINT:GOSUBl000:GOT0310 530 GOSUB2000

540 F0RI=1T06:P0KEAD+I-1,A(I):NEXT 550 AD=AD+6:IF AD<E THEN 310 560 GOTO 710 570 N=0:Z=0

580 PRINT"E+3";

581 GETA$:IFA$=""THEN581

585 PRINTCHR$(20);:A=ASC(A$):IFA=130RA=440RA=32THE

N670 590 IFA>128THENN=-A:RETURN 600 IFA<>20 THEN 630 610 GOSUB690:IFI=1ANDT=44THENN=-1:PRINT"{LEFT}

{LEFT}";:GOTO690 620 GOTO570

630 IFA<48ORA>57THEN580 640 PRINTA$;:N=N*10+A-48

650 IFN>255 THEN A=20 :GOSUB1000 .-GOTO600 660 Z=Z+1:IFZ<3THEN580 670 IFZ=0THENGOSUB1000:GOTO570 680 PRINT",";:RETURN

690 S%=PEEK(209)+2 56*PEEK(210)+PEEK(211) 692 F0RI=1T03:T=PEEK(S%-I)

695 IFT<>44ANDT<>58THENPOKES%-I,32:NEXT 700 PRINTLEFT$("{3 LEFT}",1-1);:RETURN 710 PRINT" {CLR} {RVS}*** SAVE ***{3 DOWN}" 720 INPUT"{DOWN} FILENAME";F$ 7 30 PRINT: PRINT" {2 DOWN }{RVS }j_{ OFF } APE OR {RVS}D

{OFF}lSK: (T/D)"

Appendix C: Commodore and Atari Machine Language Editor

740 GETA$:IFA$<>"T"ANDA$<>"D"THEN740 750 DV=1-7*(A$="D"):IFDV=8THENF$="0:"+F$ 760 T?=F$:ZK=PEEK(53)+256*PEEK(54)-LEN(T$):POKE782 ,ZK/256

762 POKE781,ZK-PEEK(782)*256:POKE780,LEN(T$):SYS65 m ^ r 469

763 POKE780,1:POKE781,DV:POKE78 2,1:SYS6546 6

765 POKE254,S/256:POKE253,S-PEEK(254)*256:POKE780,

253 -""

766 POKE782,E/256:POKE781,E-PEEK(782)*256:SYS65496 770 IF(PEEK(783)AND1)0R(ST AND191)THEN780

775 PRINT"{DOWN}DONE.":END

780 PRINT"{DOWN}ERROR ON SAVE.{2 SPACES}TRY AGAIN. ":IFDV=1THEN720

781 0PEN15,8,15:INPUT#15,E1$,E2$:PRINTE1$;E2$:CLOS E15:GOTO720

782 GOTO720

790 PRINT"{CLR}{RVS}*** LOAD ***{2 DOWN}"

800 INPUT"{2 DOWN} FILENAME";F$

810 PRINT:PRINT"{2 DOWN}{RVS }t{OFF }APE OR {RVSJd

{OFF}lSK: (T/D)" 820 GETA$:IFA$ < >"T"ANDA$ < >"D"THEN820 830 DV=1-7*(A$="D"):IFDV=8THENF$="0:"+F$

840 T$=F$:ZK=PEEK(53)+256*PEEK(54)-LEN(T$):POKE782 .ZK/256

841 POKE781,ZK-PEEK(782)*256:POKE780,LEN(T$):SYS65 469

845 POKE780,1:POKE781,DV:POKE78 2,1:SYS65466

850 POKE780,0:SYS65493

860 IF(PEEK(783)AND1)0R(ST AND191)THEN870

865 PRINT"{DOWN}DONE.":GOTO310

870 PRINT"{DOWN}ERROR ON LOAD.{2 SPACES}TRY AGAIN.

{DOWN}":IFDV=1THEN800 880 0PEN15,8,15:INPUT#15,E1$,E2$:PRINTE1$;E2$:CLOS

E15:GOTO800

1000 REM BUZZER

1001 POKE36878,15:POKE36874,190 —

1002 FORW=1TO300:NEXTW

1003 POKE36878,0:POKE36874,0:RETURN

2000 REM BELL SOUND mm ^

2001 FORW=15TO0STEP-1:POKE36878,W:POKE36876,240:NE XTW

2002 POKE36876,0:RETURN

3000 PRINTC$;"{RVS}NOT ZERO PAGE OR ROM":GOTO1000

Appendix C: Commodore and Atari Machine Language Editor

Program C-3. PET MLX

Refer to Appendix E "How to Type In BASIC Programs" before entering this program.

100 PRINT"{CLR}";CHR$(142):POKE53,43:CLR

110 PRINT"{RVS}{38 SPACES}"

120 PRINT"{RVSJif 18 SPACES}MLX{17 SPACES}"

140 PRINT"{RVS}{38 SPACES]"

200 PRINT" [2 DOWN} MACHINE LANGUAGE EDITOR PET VER

SION{5 DOWN}" 210 PRINT"{2 UPlSTARTING ADDRESS?{8 SPACES}

{9 LEFT}"; 215 INPUTS

220 IFS<256ORS>3276 7THENGOSUB3000:GOTO210 225 PRINT:PRINT:PRINT 230 PRINT"{2 UP}ENDING ADDRESS?{8 SPACES}{9 LEFT}"

;:INPUTE 240 IFE<256ORE>32767THENGOSUB3000:GOTO230 250 IFE<STHENPRINTC$;"{RVS}ENDING < START

[2 SPACES}":GOSUB1000:GOTO 230 260 PRINT:PRINT:PRINT 300 PRINT"{CLR}";CHR$(14):AD=S 310 A=1:PRINTRIGHT$("0000"+MID$(STR$(AD),2),5);":"

315 FORJ=AT06

320 GOSUB570:IFN=-1THENJ=J+N:GOTO320

390 IFN=-211THEN 710

400 IFN=-204THEN 790

410 IFN=-206THENPRINT:INPUT"{DOWN}ENTER NEW ADDRES

S";ZZ 415 IFN=-206THENIFZZ<SORZZ>ETHENPRINT"{RVS}OUT OF

{SPACE}RANGE":GOSUB1000:GOTO410 417 IFN=-206THENAD=ZZ:PRINT:GOTO310 420 IF No-196 THEN 480

430 PRINT:INPUT"DISPLAY:FROM";F:PRINT,"TO";:INPUTT 440 IFF<SORF>EORT<SORT>ETHENPRINT"AT LEAST";S;"

{LEFT}, NOT MORE THAN";E:GOTO430

450 FORI=FTOTSTEP6:PRINT:PRINTRIGHT?("0000"+MID$(S TR$(I),2),5);":" ;

451 FORK=0TO5:N=PEEK(I+K):PRINTRIGHT?("00"+MID$(ST R$(N),2),3);",";

460 GETA$:IFA$>""THENPRINT:PRINT:GOTO310 470 NEXTK:PRINTCHR$(20);:NEXTI:PRINT:PRINT:GOT0310 480 IFN<0 THEN PRINT:GOTO310 490 A(J)=N:NEXTJ

500 CKSUM=AD-INT(AD/256)*256:F0RI=1T06:CKSUM=(CKSU M+A(I))AND2 5 5:NEXT

510 PRINTCHR$(18);:GOSUB570:PRINTCHR$(146);

511 IFN=-1THENA=6:G0T0315

515 PRINTCHR$(20):IFN=CKSUMTHEN530

Appendix C: Commodore and Atari Machine Language Editor

520 PRINT:PRINT"LINE ENTERED WRONG : RE-ENTER":PRI

NT:GOSUB1000:GOTO310 530 GOSUB2000

540 F0RI=1T06:P0KEAD+I-1,A(I):NEXT 550 AD=AD+6:IF AD<E THEN 310 560 GOTO 710 570 N=0:Z=0

580 PRINTCHR$(168);

581 GETA$:IFA$=""THEN581

585 PRINTCHR$(20)J:A=ASC(A$):IFA=130RA=440RA=32THE

N670 590 IFA>128THENN=-A:RETURN 600 IFA<>20 THEN 630 610 GOSUB690:IFI=1ANDT=44THENN=-1:PRINT"{OFF}

{LEFT} {LEFT}";:GOTO690 620 GOTO570

630 IFA<48ORA>57THEN580 640 PRINTA$;:N=N*10+A-48

650 IFN>255 THEN A=20:GOSUB1000:GOTO600 660 Z=Z+1?IFZ<3THEN580 670 IFZ=0THENGOSUB1000:GOTO570 680 PRINT",";:RETURN

690 SS=PEEK(196)+2 56*PEEK(197)+PEEK(198)

691 F0RI=1T03:T=PEEK(SS-I)

695 IFTO44ANDTO58THENP0KESS-I, 32 :NEXT

700 PRINTLEFT$("{3 LEFT}",1-1);:RETURN

710 PRINT"{CLR}{RVS}*** SAVE ***{3 DOWN}"

715 PRINT"{2 DOWN}(PRESS {RVS} RETURN {OFF} ALONE TO

CANCEL SAVE){DOWN}" 720 F$="":INPUT"{DOWN} FILENAME? *{3 LEFT}";F$:IFF

$="*"THENPRINT:PRINT:GOTO310 730 PRINT:PRINT"{2 DOWN}{RVS}T{OFF}APE OR {RVS}D

{OFF}lSK: (T/D)" 740 GETA$:IFA$o"T"ANDA$o"D"THEN740 750 DV=1-7*(A$="D"):IFDV=8THENF$="0:"+F$:OPEN15,8,

15,"S"+F$:CLOSE15 760 T$=F$:ZK=PEEK(50)+256*PEEK(51)-LEN(T$):POKE219

,ZK/256 762 POKE218,ZK-PEEK(219)*256:POKE209,LEN(T$) 76 3 POKE210,1:POKE211,0:POKE212,DV

76 5 K=S:POKE252,K/256:POKE251,K-PEEK(252)*256

766 K=E+1:POKE202,K/256:POKE201,K-PEEK(202)*256:SY

S63203:REM 63140 FOR 3.0 770 IF(191ANDST)THEN780

77 5 PRINT"{DOWN}DONE.{DOWN}":GOTO310

780 PRINT"{DOWN}ERROR ON SAVE.{2 SPACES}TRY AGAIN. ":IFDV=1THEN720

781 OPEN15,8,15:INPUT#15,El $,E2$:PRINTE1$;E2$:CLOS E15:GOTO720

Appendix C: Commodore and Atari Machine Language Editor

790 PRINT"[CLR] {RVS}*** LOAD ***{2 DOWN}"

795 PRINT"{2 DOWN}(PRESS {RVS} RETURN {OFF} ALONE TO

CANCEL LOAD)" 800 F$="":INPUT"{2 DOWN} FILENAME? *{3 LEFT}";F$:I

FF$="*"THENPRINT: PRINT .-GOTO310 810 PRINT:PRINT"{2 DOWN}{RVS}T{OFF}APE OR {RVS}D

{OFFjlSK: (T/D)" 820 GETA$ :IFA$<> "T"ANDA$<>"D"THEN820 830 DV=1-7*(A$="D"):IFDV=8THENF$="0:"+F$

840 T$=F$:ZK=PEEK(50)+256*PEEK(51)-LEN(T$):POKE219 ,ZK/256

841 POKE218,ZK-PEEK(219)*256:POKE209,LEN(T$) 845 POKE210,1:POKE211,0:POKE212,DV

850 POKE157,0:SYS62294:REM USE 62242 FOR UPGRADE P

ET 3.0 860 IF(191ANDST)THEN870 865 PRINT"{DOWNjDONE.":GOTO310 870 PRINT"{DOWN}ERROR ON LOAD.{2 SPACES}TRY AGAIN.

{DOWN}":IFDV=1THEN800 880 0PEN15,8,15:INPUT#15,E1$,E2$:PRINTE1?;E2$:CLOS

E15:GOTO800

1000 REM BUZZER

1001 POKE59467,16:POKE59466,129:POKE59464,255 1003 FORT=200TO2 50:POKE59466,T:NEXT:POKE59467,0:RE

TURN

2000 REM BELL SOUND

2001 POKE59467,16:POKE59466,51:POKE59464,100 2003 FORT=1TO50:NEXT:POKE59467,0:RETURN

3000 PRINT"{RVS}NOT ZERO PAGE, SCREEN OR ROM":GOTO 1000

Program C-4. Atari MLX

Refer to Appendix E "How to Type In BASIC Programs" before entering this program. 100 GRAPHICS 0:DL=PEEK (560) +256*PEEK(561 ) +4:

POKE DL-1,71:P0KE DL+2,6 110 POSITION 8,0:? "MLX":POSITION 23,0:? "IE

HBEmaEPEGg '' : poke 710,0:? 120 ? "Starting Address";:INPUT BEG:? " End

ing Address";:INPUT FIN:? "Run/Init Addr

ess";:INPUT STARTADR 130 DIM A(6),BUFFER*<FIN-BEG+127),T*<20),Ft(

20),CIO$(7),SECT0R*(128),DSKINV*(6) 140 OPEN #1 , 4, 0, " K: " : ? :? ,"iiape or Ei si: : " ; 150 BUFFER*=CHR*<0):BUFFER*(FIN-BEG+30)=BUFF

ER*:BUFFER*<2)=BUFFER*:SECTOR*=BUFFER* 160 ADDR = BEG:CIO*=" hhh" :CIO*<4)=CHR* <170) :CI

O* <5)="LV":CIO*(7)=CHR* (228) 170 GET #1, MEDIA: IF MEDIA<>84 AND MEDIA<>68

THEN 170

Appendix C: Commodore and Atari Machine Language Editor

180 ? CHR$(MEDIA) :? :IF MED IA<>ASC("T"> THEN

BUFFER$="":GOTO 250 190 BEG=BEG-24:BUFFER*=CHR*(0):BUFFER*(2>=CH

R* ((FIN-BEG+127)/12B> 200 H=I NT(BEG/256) :L=BEG-H*256:BUFFER* <3)= CH

R* (L> :BUFFER*(4)=CHR*(H) 210 PINIT=BEG+8:H=INT(PINIT/256):L=PINIT-H*2

56:BUFFER* <5)=CHR* (L) :BUFFER*(6)=CHR* <H> 220 FOR 1=7 TO 24:READ A:BUFFER*(I)=CHR*(A>:

NEXT I:DATA 24,96,169,60,141,2,211,169,0

,133,10,169,0,133,11,76,0,0 230 H=INT(STARTADR/256):L=STARTADR-H*256:BUF

FER* <15)=CHR* <L) :BUFFER* <19)=CHR*(H)

BUFFER*(23)=CHR*(L):BUFFER*(24)=CHR*(H)

IF MEDIA<>ASC("D") THEN 360

? :? "Boot Eisk or Binary Biles")

GET #1.DTYPE:IF DTYPEO68 AND DTYPEO70

THEN 270

? CHR*(DTYPE)sIF DTYPE=70 THEN 360

BEG=BEG-30:BUFFER*=CHR*(0):BUFFER*(2)=CH

R*((FIN-BEG+127)/12B)

H=INT(BEG/256):L=BEG-H*256:BUFFER*(3)=CH

R*(L):BUFFER*(4)=CHR*(H)

PINIT=STARTADR:H=INT(PINIT/256):L=PINIT-

H*256:BUFFER*(5)=CHR*(L):BUFFER*(6)= CHR*

(H)

RESTORE 330:FOR 1=7 TO 30:READ A:BUFFER*

(I)=CHR*(A):NEXT I

DATA 169,0,141,231,2,133,14,169,0,141,23

2,2,133,15,169,0,133,10,169,0,133,11,24,

96

H=INT(BEG/256):L=BEG-H*256:BUFFER*(8)=CH

R*(L):BUFFER*(15)=CHR*(H)

H=INT( STARTADR/256):L=STARTADR-H*256:BUF

FER*(22)=CHR*(L):BUFFER*(26)=CHR*(H)

GRAPHICS 0:POKE 712,10:POKE 710,10:POKE

709, 2

? ADDR;":";:FOR J=l TO 6

GOSUB 570:IF N=-l THEN J=J-l:GOTO 380

IF N=-19 THEN 720

IF N=-12 THEN LET READ=l:GOTO 720

TRAP 410:IF N=-14 THEN ? :? "New Address

";:INPUT ADDR:? :GOTO 370

TRAP 32767: IF NO-4 THEN 480

TRAP 430:? :? "Disp1 ay:From"; : INPUT F:?

,"To";:INPUT T:TRAP 32767 440 IF F<BEG OR F>FIN OR T<BEG OR T>FIN OR T

<F THEN ? CHR*(253);"At least ";BEG;", N

ot More Than ";FIN:GOTO 430

428

Appendix C: Commodore and Atari Machine Language Editor

450 FOR I=F TO T 5TEP 6:? :? I;":";:FOR K=0

TO 5:N=PEEK(ADR(BUFFER*)+I+K-BEG):T$="00

0":T*(4-LEN(STR$(N)))=STR$(N> 460 IF PEEK(764)<255 THEN GET #l,A:POP :POP

:? :GOTO 370 470 ? T$;",";:NEXT K:? CHRt(126);:NEXT I:? :

? :GOTO 370 480 IF N<0 THEN ? :GOTO 370 490 A(J)=N:NEXT J 500 CKSUM=ADDR-INT(ADDR/256)*256:FOR 1=1 TO

6:CKSUM=CKSUM+A(I):CKSUM=CKSUM-256*(CKSU

M>255):NEXT I 510 RF=128:S0UND 0,200,12,8:GOSUB 570:SOUND

0,0,0,0:RF=0:? CHRt(126) 520 IF N< >CKSUM THEN ? :? "Incorrect "" ;CHRt(2

53); : ? :GOTO 370 530 FOR W=15 TO 0 STEP -l:SOUND 0,50,10,W:NE

XT W 540 FOR 1=1 TO 6:P0KE ADR(BUFFER*)+ADDR-BEG+

1-1,A(I):NEXT I 550 ADDR=ADDR+6:IF ADDR<=FIN THEN 370 560 GOTO 710 570 N=0:Z=0 580 GET #l,fl:IF A=155 OR A=44 OR A=32 THEN 6

70 590 IF A<32 THEN N=-A:RETURN 600 IF A<>126 THEN 630 610 GOSUB 690:IF 1=1 AND T=44 THEN N=-l:? CH

Rt(126)::GOTO 690 620 GOTO 570

630 IF A<48 OR A>57 THEN 580 640 ? CHRt(A+RF);:N=N*10+A-48 650 IF N>255 THEN ? CHRt(253);:A=126:GOTO 60

0 660 Z=Z+1:IF Z<3 THEN 580

670 IF Z=0 THEN ? CHRt(253);:GOTO 570 680 ? ",";:RETURN 690 POKE 752,l:FOR 1=1 TO 3:? CHRt(30)j:GET

#6,T:IF T<>44 AND T<>58 THEN ? CHRt(A);:

NEXT I 700 POKE 752,0:7 " ";CHRt(126);:RETURN 710 GRAPHICS 0:POKE 710,26:POKE 712,26:POKE

709.2 720 IF MEDIA=ASC("T") THEN 890 730 REN M3HCT

740 IF READ THEN 7 :7 "Load File":? 750 IF DTYPEOASC ( " F" ) THEN 1040 760 ? :? "Enter AUTORUN.SYS for automatic us

e":? :? "Enter filename":INPUT Tt

Appendix C: Commodore and Atari Machine Language Editor

770 F$=T*:IF LEN(T$)>2 THEN IF T*(1,2)<>"D:"

THEN F*="D:":F*(3)= T* 780 TRAP 870:CLOSE #Z:OPEN #2,8-4*READ,0,F«:

? :? "Working. .." 790 IF READ THEN FOR 1=1 TO 6:GET #2,A:NEXT

I:GOTO 820 800 PUT #2,255:PUT #2,255 810 H=INT(BEG/256):L=BEG-H*256:PUT #2,L:PUT

#2,H:H=INT(FIN/256):L=FIN-H*256:PUT #2, L

:PUT #2,H 820 GOSUB 970:IF PEEK(195)>1 THEN 870 830 IF STARTADR=0 OR READ THEN 850 840 PUT #2,224:PUT #2,2:PUT #2,225:PUT #2,2:

H=INT(STARTADR/256):L=STARTADR-H*256:PUT #2,L:PUT #2,H 850 TRAP 32767-.CLDSE #2:7 "Fini shed. ": IF REA

D THEN ? :? :LET READ=0:GOTO 360 860 END 870 7 "Error " ;PEEK(195) ; " trying to access"

:7 Ft:CLOSE #2:7 :GOTO 760

880 rem ■ saMaa ■

890 IF READ THEN 7 :7 "Read Tape"

900 ? :? :? "Insert, Rewind Tape.":? "Press

PLAY ";:IF NOT READ THEN 7 "& RECORD" 910 7 :? "Press 133X331 when ready:"; 920 TRAP 960:CLOSE #2:OPEN #2,8-4*READ, 128, "

C: " : 7 :7 "Working. . . " 930 GOSUB 970:IF PEEK(195)>1 THEN 960 940 CLOSE #2:TRAP 32767:7 "Finished.":? :? :

IF READ THEN LET READ=0:GOTO 360 950 END 960 ? :? "Error ":PEEK(195);" when reading/w

riting boot tape":? :CLOSE #2:GOTO 890 970 REM

CIO Load/Save File»2 i

for- write. REfiD=l for- rea

IJilsEJ

:1=f:1>33s]

980 X=32:REM File#2,*20

990 ICCOM=834:ICBADR=836:ICBLEN=840:ICSTAT=8

35 1000 H=INT(ADR(BUFFER*>/256): L=ADR(BUFFER*)-

H*256:POKE ICBADR+X,L:POKE ICBADR+X+l.H 1010 L=FIN-BEG+l:H=INT(L/256):L=L-H*256:POKE

ICBLEN+X,L:POKE I CBLEN + X-»-1 , H 1020 POKE ICCOM + X, 1 1-4JREAD:A = USR(ADR(CIO*) ,

X) 1030 POKE 195,PEEK(ICSTAT):RETURN 1040 REN WM3BSMSE3E3Bnm 1050 IF READ THEN 1100 1060 ? :? "Format Disk In Drive 1? (Y/N):":

Appendix C: Commodore and Atari Machine Language Editor

1070 1080 1090

1 100

1 1 10 1 120 1 130

1 140 1 150 1 160 1 170 1 180 1 190 1200

1210 1220 1230 1240 1250 1260 1270 1280 1290

1300 1310 1320 1330 1340 1350

1360 1370

1380 1390

1400 1410

GET ? C

NR = EG +

FOR

IF

27)

SEC

BOS

IF

NEX

IF

or m REM REM REM REM REM REM REM BAS DUN 3

DBU DBY DAU REM DSK POK ) : L POK POK POK POK (DA A = U RET

#1

HR$

" F

INT

2) =

SOT

Wri

1 =

REA

= SE

TOR

UB

PEE

T I

NO o .

Err

att

, A: I

(A) : Form or ma

( (FI CHR*

0 1 1 ting

1 TO D TH CTOR * = BU 1220 K (DS

F A<>78 AND A<>89 THEN 1070 IF A=78 THEN 1100

atting...":XIO 254,#2,0,0,"D: t Complete":?

N-BEG+127)/12S):BUFFER*(FIN-B (0):IF READ THEN ? "Reading.. 20

II

NR:S=I EN GOSUB 1220:BUFFER*(1*128-1 *:GOTO 1160 FFER*(I 1128-127)

TATS)<>1 THEN 1200

T READ THEN END LET READ=0:GOTO 360 or on disk access.": i ng. ":GOTO 1040

' May need f

SECTOR RCCE55 SUBROUTIN

Drive ONE

Pass buffer in SECTOR*

sector # in variable S

READ=1 -for read,

READ=0 for write E=3*256 IT=BASE+1:DC0MND=BASE+2:DSTATS=BASE+

FLO = TLO = X 1=B DIM INV* E DU = A-2 E DB E DB E DC E DA UX2) SR (A URN

BASE BASE ASE + DSK = "hl_ NIT, 56*H UFHI UFLO OMND UX2, *256 DR <D

+4:DBUFHI=BASE+5

+B:DBYTHI=BASE+9

10:DAUX2=BASE+11

INV* <4)

S" :DSKINV*(4)=CHR*(228)

1:A=ADR(SECTOR*):H=INT(A/256

H L

,B7-5*READ INT(S/256):

SKINV*))

POKE DAUX1,S-PEEK

A Library of Subroutines

Here is a collection of techniques you'll need to use in many of your ML programs. Those techniques which are not inherently easy to understand are followed by an explanation.

Increment and Decrement Double-Byte Numbers

You'll often want to raise or lower a number by 1. To increment a number, you add 1 to it: Incrementing 5 results in 6. Decrement lowers a number by 1. Single-byte numbers are easy; you just use INC or DEC. But you'll often want to increment two-byte numbers which hold addresses, game scores, pointers, or some other number which requires two bytes. Two bytes, ganged together and seen as a single number, can hold values from 0 ($0000) up to 65535 ($FFFF). Here's how to raise a two-byte number by 1, to increment it:

(Let's assume that the number you want to increment or decrement is located in addresses $0605 and $0606, and the ML program segment performing the action is located at $5000.)

5000 INCREMENT INC $0605; raise the low byte

5003 BNE GOFORTH; if not zero, leave high byte alone

5005 INC $0606; raise high byte

5008 GOFORTH ... continue with program

The trick in this routine is the BNE. If the low byte isn't raised to zero (from 255), we don't need to add a "carry" to the high byte, so we jump over it. However, if the low byte does turn into a zero, the high byte must then be raised. This is similar to the way an ordinary decimal increment creates a carry when you add 1 to 9 (or 99 or 999). The lower number turns to zero, and the next column over is raised by one.

To double decrement, you need an extra step. The reason it's more complicated is that the 6502 chip has no way to test if you've crossed over to $FF, down from $00. BNE and BEQ will test if something is zero, but nothing tests for $FF. (The N flag is turned on when you go from $00 to $FF, and BPL or BMI could test it.) The problem with it, though, is that the N

Appendix D: A Library of Subroutines

flag isn't limited to sensing $FF. It is sensitive to any number higher than 127 decimal ($7F).

So, here's the way to handle double-deckers:

5000 LDA $0605; load in the low byte (affecting the zero flag)

5003 BNE FIXLOWBYTE; if it's not zero, lower it, skipping high

byte 5005 DEC $0606; zero in low byte forces this. 5008 FIXLOWBYTE DEC $0605; always dec the low byte.

Here we always lower the low byte, but lower the high byte only when the low byte is found to be zero. If you think about it, that's the way any subtraction would work.

Comparison

Comparing a single-byte against another single-byte is easily achieved with CMP. Double-byte comparison can be handled this way:

(Assume that the numbers you want to compare are located in addresses $0605,0606 and $0700,0701. The ML program segment performing the comparison is located at $5000.)

5000 SEC

5001 LDA $0605; low byte of first number

5004 SBC $0700; low byte of second number

5007 STA $0800; temporary holding place for this result 500A LDA $0606; high byte of first number 500D SBC $0701; high byte of second number, leave result in A 5010 ORA $0800; results in zero if A and $0800 were both zero.

The flags in the Status Register are left in various states after this routine—you can test them with the B instructions and branch according to the results. The ORA sets the Z (zero) flag if the results of the first subtraction (left in $0800) and the second subtraction (in A, the Accumulator) were both zero. This would only happen if the two numbers tested were identical, and BEQ would test for this (Branch if EQual).

If the first number is lower than the second, the carry flag would have been cleared, so BCC (Branch if Carry Clear) will test for that possibility. If the first number is higher than the second, BCS (Branch if Carry Set) will be true. You can therefore branch with BEQ for =, BCC for <, and BCS for >. Just keep in mind which number you are considering the first and which the second in this test.

Appendix D: A Library of Subroutines

Double-Byte Addition

CLC ADC and SEC SBC will add and subtract one-byte numbers. To add two-byte numbers, use:

(Assume that the numbers you want to add are located in addresses $0605,0606 and $0700,0701. The ML program segment performing the addition is located at $5000.)

5000 CLC; always do this before any addition

5001 LDA $0605 5004 ADC $0700

5007 STA $0605; the result will be left in $0605,0606 500A LDA $0606 500D ADC $0701 5010 STA $0606

It's not necessary to put the result on top of the number in $0605,0606—you can put it anywhere. But you'll often be adding a particular value to another and not needing the original any longer—adding ten points to a score for every blasted alien is an example. If this were the case, following the logic of the routine above, you would have a 10 in $0701, 0702:

0701 0A; the 10 points you get for hitting an alien

0702 00

You'd want that 10 to remain undisturbed throughout the game. The score, however, keeps changing during the game and, held in $0605,0606, it can be covered over, replaced with each addition.

Double-Byte Subtraction

This is quite similar to double-byte addition. Since subtracting one number from another is also a comparison of those two numbers, you could combine subtraction with the double-byte comparison routine above (using ORA). In any event, this is the way to subtract double-byte numbers. Be sure to keep straight which number is being subtracted from the other. We'll call the number being subtracted the second number.

(Assume that the number you want to subtract [the "second number"] is located in addresses $0700,0701, and the number it is being subtracted from [the "first number"] is held in $0605,0606. The result will be left in $0605,0606. The ML program segment performing the subtraction is located at $5000.)

Appendix D: A Library of Subroutines

5000 SEC; always do this before any subtraction

5001 LDA $0605; low byte of first number 5004 SBC $0700; low byte of second number

5007 STA $0605; the result will be left in $0605,0606 500A LDA $0606; high byte of first number 500D SBC $0701; high byte of second number 5010 STA $0606; high byte of final result

Multi-Byte Addition and Subtraction

Using the methods for adding and subtracting illustrated above, you can manipulate larger numbers than can be held within two bytes (65535 is the largest possible two-byte integer). Here's how to subtract one four-byte-long number from another. The locations and conditions are the same as for the two-byte subtraction example above, except the "first number" (the minuend) is held in the four-byte chain, $0605,0606,0607,0608, and the "second number" (the subtrahend, the number being subtracted from the first number) is in $0700,0701,0702,0703.

Also observe that the most significant byte is held in $0703 and $0608. We'll use the Y Register for Indirect Y addressing, use four bytes in zero page as pointers to the two numbers, and use the X Register as a counter to make sure that all four bytes are dealt with. This means that X must be loaded with the length of the chains we're subtracting—in this case, 4.

5000 LDX #4; length of the byte chains

5002 LDY #0, set Y

5004 SEC; always before subtraction

5005 LOOP LDA (FIRST),Y 5007 SBC (SECONDLY

5009 STA (FIRSTLY; the answer will be left in $0605-0608. 500B INY; raise index to chains

500C DEX; lower counter

5010 BNE LOOP; haven't yet done all four bytes

Before this will work, the pointers in zero page must have been set up to allow the Indirect Y addressing. This is one way to do it:

Appendix D: A Library of Subroutines

2000 FIRST = $FB; define zero page pointers at $FB and $FD

2000 SECOND = $FD

2000 SETUP LDA #5; set up pointer to $0605

2002 STA FIRST

2004 LDA #6

2006 STA FIRST+ 1

2008 LDA #0; set up pointer to $0700

200A STA SECOND

200C LDA #7

200E STA SECOND+ 1

Multiplication

X 2

ASL (no argument used, 'Accumulator addressing mode") will

multiply the number in the Accumulator by 2.

X 3

(To multiply by 3, use a temporary variable byte we'll call TEMP.)

5000 STA TEMP; put the number into the variable

5003 ASL; multiply it by 2

5004 ADC TEMP; (X * 2 + X = X * 3) the answer is in A.

X 4

(To multiply by 4, just ASL twice.)

5000 ASL; * 2

5001 ASL; * 2 again

X 4 (two byte)

(To multiply a two-byte integer by 4, use a two-byte variable we'll call TEMP and TEMP + 1.)

5000 ASL TEMP; multiply the low byte by 2 5003 ROL TEMP + 1; moving any carry into the high byte 5006 ASL TEMP; multiply the low byte by 2 again 5009 ROL TEMP + 1; again acknowledge any carry.

Appendix D: A Library of Subroutines

X 10

(To multiply a two-byte integer by 10, use an additional two-byte variable we'll call STORE.)

5000; first put the number into STORE for safekeeping

5000 LDA TEMP-.STA STORE:LDA TEMP + 1:STA STORE+ 1

500C; then multiply it by 4

500C ASL TEMP; multiply the low byte by 2

500F ROL TEMP + 1; moving any carry into the high byte

5012 ASL TEMP; multiply the low byte by 2 again

5015 ROL TEMP + 1; again acknowledge any carry.

5018; then add the original, resulting in X * 5

5018 LDA STORE

501B ADC TEMP

501E STA TEMP

5021 LDA STORE+ 1

501D ADC TEMP + 1

5024 STA TEMP + 1

5027; then just multiply by 2 since (5*2 = 10)

5027 ASL TEMP

502A ROL TEMP + 1

X

(To multiply a two-byte integer by other odd values, just use a similar combination of addition and multiplication which results in the correct amount of multiplication.)

X 100

(To multiply a two-byte integer by 100, just go through the above subroutine twice.)

X 256

(To multiply a one-byte integer by 256, just transform it into a two-byte integer.)

5000 LDA TEMP 5003 STA TEMP + 1 5006 LDA #0 5008 STA TEMP

Appendix D: A Library of Subroutines

Division

-J- 2

LSR (no argument used, 'Accumulator addressing mode") will

divide the number in the Accumulator by 2.

- - 4

(To divide by 4, just LSR twice.)

5000 LSR; / 2

5001 LSR; / 2 again

-T- 4 (two byte)

(To divide a two-byte integer, called TEMP, by 2)

5000 LSR TEMP + 1; shift high byte right

5001 ROR TEMP; pulling any carry into the low byte

How to Type In BASIC Programs

Some of the programs listed in this book are written in BASIC and contain special control characters (cursor control, color keys, inverse video, etc.). To make it easy to tell exactly what to type when entering one of these programs into your computer, we have established the following listing conventions. There is a separate key for each computer. Refer to the appropriate tables when you come across an unusual symbol in a program listing. If you are unsure how to actually enter a control character, consult your computer's manuals.

Atari

Characters in inverse video will appear like: m:iai=<;*i=»ak>3 =jt Enter these characters with the Atari logo key, (A).

Graphics characters, such as CTRL-T, the ball character • will appear as the "normal" letter enclosed in braces, e.g., {T}.

A series of identical control characters, such as 10 spaces, 3 cursor-lefts, or 20 CTRL-Rs, will appear as {10 SPACES}, {3 LEFT}, {20 R}, etc. If the character in braces is in inverse video, that character or characters should be entered with the Atari logo key. For example, {5 ffl} means to enter five inverse-video CTRL-Us.

Appendix E: How to Type In BASIC Programs

Commodore 64, VIC, and PET

Program listings will contain words within braces which spell out any special characters: {DOWN} would mean to press the cursor down key. (5 SPACES} would mean to press the space bar five times.

To indicate that a key should be shifted (hold down the SHIFT key while pressing the other key), the key would be underlined in our listings. For example, S would mean to type the S key while holding the SHIFT key. If you find an underlined key enclosed in braces (e.g., {10 N}), you should type the key as many times as indicated (in our example, you would enter ten shifted Ns).

If a key is enclosed in special brackets, [< >], you should hold down the Commodore key while pressing the key inside the special brackets. (The Commodore key is the key in the lower left corner of the keyboard.) Again, if the key is preceded by a number, you should press the key as many times as indicated.

Rarely, you'll see a solitary letter of the alphabet enclosed in braces. These characters can be entered by holding down the CTRL key while typing the letter in the braces. For example, {A} would indicate that you should press CTRL-A.

About the quote mode: You should know that you can move the cursor around the screen with the CRSR keys. Sometimes a programmer will want to move the cursor under program control. That's why you see all the {LEFT}'s, {HOME}'s, and {BLU}'s in our programs. The only way the computer can tell the difference between direct and programmed cursor control is the quote mode.

Once you press the quote (the double quote, SHIFT-2), you are in the quote mode. If you type something and then try to change it by moving the cursor left, you'll only get a bunch of reverse-video lines. These are the symbols for cursor left. The only editing key that isn't programmable is the DEL key; you can still use DEL to back up and edit the line. Once you type another quote, you are out of quote mode.

You also go into quote mode when you INserT spaces into a line. In any case, the easiest way to get out of quote mode is to just press RETURN. You'll then be out of quote mode and you can cursor up to the mistyped line and fix it.

Use the following tables when entering special characters:

Appendix E: How to Type In BASIC Programs

When You Read:

[CLR]

(home)

{UP}

(down)

(left)

(right)

(rvs)

(off)

(blk)

(WHT)

(RED) ( CYN ) (PUR)

Press:

See:

c m

□ m

^-CRSR-»| ETj

f CRSR A

When You Read:

(grn) (blu)

(YEL)

(Fl)

(F2)

(F3)

(F4)

(F5)

(F6)

(F7)

(F8)

t

Press:

See: □

Q

m

♦d *§

* = (Program Counter =) pseudo-op 32,

111-12, 149-51, 203, 336, 339 #> pseudo-op 342-43 #< pseudo-op 342-43 + pseudo-op 179, 342-43 Accumulator. Sec 6502, Accumulator

Register Accumulator addressing 38 ADC 239-40 address (Program Counter) labels 7,

36-37 addressing modes. See 6502, instruction

types AND 240-41

with ASCII numbers 114, 154 Apple LADS 327-32,

BASIC wedge 331-32

Disk Operating System file manager

327-32

error byte 330

Openl 327-32 Array (subprogram) 43, 85-93

program listings 97-101 ASCII

alphabetic numbers 154-55

characters 33, 82-83

messages 182

number conversion 113-16 ASL 241-42

with ROL 115-16, 153-54 assembly 5-6, 34 Atari

CIO 299

IOCB 299

memory 300 Atari LADS 299-327, 348-55

Defs 301

Editor 301, 308-12, 350-55

program listing 312-27

Eval 301, 304-5

Getsa 302

Indisk 303

Kernal 300, 303-4

program listing 305-8

modifying the Editor 311

Openl 302

Printops 303

Pseudo 303

System 305

program listing 308

Valdec 302 ,B (.BYTE) pseudo-op 156-58 base opcodes 36, 226

BASIC

borrowing from 18-19, 105-7, 182-83

end of program mark 152

keyword table 10, 18

Sec also tokenized keywords BCC 242 BCS 242-43 BEQ 243 B group instructions. See Relative

addressing BIT 243-44

bit-moving instructions 38 BMI 244

and BPL 45-47, 83 BNE 244-45 borrow 266 BPL 245

and BMI. See BMI and BPL BRANCH OUT OF RANGE 11, 39 Brannon, Charles 108, 415 BRK 245-46 buffer 29-30, 140 BVC 246 BVS 247

carry 239, 242, 247, 266-67 chained files. See pseudo-ops, ,E; pseudo-ops, ,F character 8 CLC 247 CLD 247-48 CLI 248 CLV 248-49 CMP 249-50

and turbo-charged programming 108 cold start 18 comments 141-44 Commodore

Kernal 4

ST (status byte) 205 constant 4 CPX 250-51 CPY 251-52

.D (.DISK) pseudo-op 181, 202-5, 345 data base management. See Array (subprogram); Equate (subprogram) debugging 53-55, 149-51, 260, 339 DEC 252

decimal mode 239-40, 247-48, 261 defaults 29

changing 31 Defs (subprogram) 15-25

program listings 20-25

relocatability 15-16

transportability 16

443

delimiters 82-84

DEX 252-53

DEY 253

Dis (optional subprogram) 288-96

program listings 294-96 disk 16

assembly to disk. See pseudo-ops, .D

errors 205

padding with spacer bytes 151, 159-60

Program Counter 107 division 259, 439 documentation. See comments double-byte ML routines

addition 435

comparison 434

decrement 433

increment 433

subtraction 435 Dtables (optional subprogram) 288-96

program listings 296-98 DUPLICATED LABEL 86-88, 90 .E (.END) pseudo-op 201, 202, 343 EOR 253-54

to shift an ASCII character 83 equate labels 7, 16, 36-37

zero page 16 Equate (subprogram) 81-84

program listings 94-96 error signals 184 error traps (additional)

impossible instruction 279-80

keywords in filenames

naked mnemonic 278-79 Eval (subprogram) 29-76

calculating an opcode 226

determining addressing mode (instruction type) 43-53

program listings 55-76 expression labels 86 extensibility 277

.F (.FILE) pseudo-op 112, 199-200, 343 false target 11 fields

fixed length 79-81, 108

variable length 79-83 Findmn (subprogram) 32, 35-37, 109-11

program listings 129-30 flags 5, 9, 30 Getsa (subprogram) 32, 111-13

program listings 131-34 ,H (.HEX) pseudo-op 206 hexadecimal (hex) numbers 42-43,

152-56, 183-85 Implied addressing 37-38 INC 254

Indisk (subprogram) 32, 34, 42, 139-76

program listings 161-76 initialization 29 Input/Output (I/O)

Commodore 105-8

See also Pseudo (subprogram) instruction types. See 6502, instruction

types integer 8 interrupt

customizing 268

disabling 268

forced 245 INX 254-55 INY 255 JMP 255-56 JSR 256-57

covering with NOP 260

self-modifying indirect 311 Kernal. See Atari LADS, Kernal; Commodore, Kernal labels 40-42

storing in data base 83-85

See also address (Program Counter) labels; DUPLICATED LABEL; equate labels; expression labels; source labels;

UNDEFINED LABEL LADS

Apple. See Apple LADS

assembly 34

Atari. See Atari LADS

buffers 227-28

command summary 338-39

development and philosophy 79-81,

108-11, 150-51

disassembler. Sec Dis (optional

subprogram)

flags 228-31

how to use 335-55

modifying 184, 200-201, 277-98

object code listings 357-414

Program Counter 33, 86, 149-51

RAM-based assembly 282-86

registers 227-28

relocating 15

rules for use 345

tape use 348

zero page usage 17-18 LDA 257-58 LDX 258 LDY 258-59

looping 81-82 linked files

See pseudo-ops, .F; pseudo-ops, .E lookup tables 108-11 loop counter 252-53

LSR 259

Machine Language Editor (MLX) 415 Machine Language for Beginners 34 Machine Language (ML) routines. See

double-byte ML routines; multi-byte

ML routines Mapping the Atari 327 Math (subprogram) 179-80

program listings 186-87 "Micromon" 150 MLX. See Machine Language Editor

program listings 419-31 mnemonic instructions. See 6502, instruction set modifying LADS 184, 200-201, 277-98 monitor 3 multi-byte ML routines

addition 436

subtraction 436 multiplication 115-16, 437-38 .N (.NO) pseudo-op 204-5 NAKED LABEL 84 NOP 260

NO START ADDRESS 112 numbers 8 .0 (.OBJECT code to RAM) pseudo-op

181, 204, 344 object code 5, 181 opcodes. See 6502, opcodes Openl (subprogram) 106-8

program listings 117-29 ORA 260-61

with alphabetic numbers 154-55 output. See Input/Output; Printops

(subprogram) OVERFLOW 47 .P (.PRINTER) pseudo-op 204 parallel tables 108-11, 221-27 PHA 261

and PLA 45-47, 53 PHP 262 PLA 262

and JSR 256

and PHA. See PHA and PLA PLP 262-63 pointer 4, 30 printing

addresses 200

hex numbers 185

routines 184-85

source code 49-51 Printops (subprogram) 180-85

program listings 187-95 Program Counter. See LADS, Program

Counter

Pseudo (subprogram) 199-217

program listings 207-17 pseudo-ops

*= (Program Counter = ) 32, 111-12,

149-51, 203, 336, 339

#> 342-43

#< 342-43

+ 179, 342-43

.B (.BYTE) 156-58

,D (.DISK) 181, 202-5, 345

.E (.END) 201, 202, 343

.F(.FILE) 112, 199-200, 343

.H (.HEX) 206

.N (.NO) 204-5

.0 (.OBJECT code to RAM) 181, 204,

344

.P (.PRINTER) 204

.S (.SCREEN) 206 RAM-based assembly 282-86 range checking 179 redefined label 87 register 4, 29

Relative addressing 44-47 remarks. Sec comments ROL 263-64

with ASL. See ASL, with ROL ROR 264-65

with LSR 259 RTI 265 RTS 265-66

,S (.SCREEN) pseudo-op 206 SBC 266 SEC 266-67 SED 267 SE1 268 semicolon 341 seventh bit (bit 7) 9-10 shifted characters 82, 147, 244-45 signed arithmetic 10, 239, 244-45

branching 46-47 Simple Assembler 34 6502

Accumulator Register 257

addressing modes. See 6502, instruction

types

bug 255-56

instruction types 37-38, 43-45, 222-24

opcodes 221-22, 225

Status Register 5, 9, 30, 246

X Register 258

Y Register 258

source code 5

printout. See printing, source code source files 335 source labels 88

springboards 10, 39, 145 stack 261, 262, 265, 272 start address 32-33. See also pseudo-ops,

* =

STA 269

Status Register. See 6502, Status Register

STX 269

STY 269-70

subprogram 7-8

suction routine 140

SYNTAX ERROR 199

tables. See lookup tables

Tables (subprogram) 36, 108, 221-36

program listings 232-36 TAX 270 TAY 270-71 ;le 253

tokenized keywords 139, 142, 144-46,

160, 199-200 TSX 271

turbo-charged programming 108 TXA 271-72 TXS 272 TYA 273

types. See 6502, instruction types UNDEFINED LABEL 91-92 Valdec (subprogram) 32-33, 43, 113-16

program listings 134-36

registers 228 variable 4 vector 4 warm start 18

zero page address labels 93 Zero Page Y addressing 53

To order your copy of the LADS Disk call our toll-free US order line: 1-800-334-0868 (in NC call 919-275-9809) or send your prepaid order to:

LADS Disk

COMPUTE! Publications P.O, Box 5406 Greensboro, NC 27403

Please specify whether you want the Apple, Atari or Commodore LADS. All orders must be prepaid (check, charge, or money order). NC residents add 4.5% sales tax.

Send copies of the LADS Disk at $12.95 per copy for

(check one) □ Apple a Atari a Commodore

Subtotal $.

Shipping & Handling: $1.00/disk* $_

Sales tax (if applicable) $_

Total payment enclosed $

"Outside US and Canada, add $3.00 per disk for shipping and handling. All payments must be in US funds.

a Payment enclosed

Charge a Visa □ MasterCard a American Express

Acct. No. Exp. Date

Name

Address

City State Zip

Please allow 4-5 weeks for delivery.

ERRATA

A Note To VIC-20 Users:

To insure reliable assembly with the LADS assembler on the VIC, leave the .S (print to screen) pseudo-op active at all times.

This is the companion volume to the best seller, Machine Language for Beginners, about which the critics have said:

"Understandable" —The New York Times

"Presents the machine language novice with a very good tutorial in simple, understandable terms." —Antic

"I highly recommend Machine Language for Beginners as your first introduction to the world of machine language." —Commodore Power/Play

"This is an excellent book for anyone considering learning machine language programming. It is well written and easy to follow, and everything is presented in a logical and orderly fashion." —RUN

"Highly recommended . . . usable, understandable . . . abounds in illustrations, standards and examples." —The Midnight Gazette

"It lives up to its title. I have about six books on this subject, and this is by far the most readable of the group." —Victims Newsletter

"The best book on introducing 6502 code available." — Ian Chadwick, author of Mapping the Atari

The Second Book of Machine Language picks up where Machine Language for Beginners left off. This new book contains one of the most powerful machine language assemblers currently available. The LADS assembler is a full-featured, label-based, programming language which can greatly assist you in writing machine language programs quickly and easily.

You work in an environment with which you're already familiar: BASIC. You can use line numbers, multiple statements on a line, named variables and subroutines, remarks, error messages, and various programmers' aids like automatic line numbering, search and replace, etc.

But the book is more than a sophisticated program. It's also a clear, detailed tutorial on how large, complex machine language programs can be constructed out of manageable subprograms. Using LADS as the example, each instruction is explained, each subroutine is examined. Many sophisticated machine language techniques are illustrated and thoroughly explained—everything from data base management to communication with printers and disk drives.

There are powerful computer languages and there is good documentation, but rarely has a sophisticated language been so completely documented as it is in this book. When you finish with this book, you'll not only have a deeper understanding of machine language—you'll also have one of the most powerful machine language assemblers available.

ISBN 0-942386-5301

\odore 64, Apple (II, II+, He, and Ik, DOS 3.3), VIC-20 (8K RAM expansion required), Atari .ig XL, 40K minimum), and PET/CBM (Upgrade and 4.0 BASIC).