BACKGROUND
When we talk about assemblers, generally we are speaking about software packages which allow us to write programs in assembly language and get them to run. Such software packages usually contain three parts: an editor, used to actually write the source code programs; an assembler, used to convert the source code program into machine language, which will actually run; and a debugger, used to find errors and correct them, so that your finished product works the way you intended.
There are currently six assembler packages available for ATARI computers:
- The Assembler/Editor Cartridge, from ATARI, Inc.
- ATARI Macro Assembler (AMAC), MEDIT (an editor), and DDT (a debugger), all from the APX, ATARI, Inc.
- MAC/65, from Optimized Systems Software, Inc.,
10379 Lansdale Avenue, Cupertino, California 95014. - The SYNASSEMBLER, from Synapse Software,
5327 Jacuzzi, Suite 1, Richmond, California 94804. - The Macro Assembler/Text Editor (MAE), from Eastern House Software,
3239 Linda Drive, Winston-Salem, North Carolina 27106. - Edit 6502, from LJK Enterprises,
P. O. Box 10827, St. Louis, Missouri 63129.
It is not the purpose of this book to endorse, either directly or indirectly, any of these products. These assemblers, and particularly the differences between them, are described here to enable you to work with the examples in this book and use the routines for your own, no matter which of the products you have purchased.
THE ATARI ASSEMBLER/EDITOR CARTRIDGE
First, let's discuss syntax. The Assembler/Editor Cartridge requires that every line be prefaced with a line number, as do any BASIC programs you have written. Using the Cartridge, these line numbers must be integers between 0 and 65535. Each line number must be followed by at least one blank space. The fields which are present in a line of an assembly language program are:
line number label mnemonic operand comment
For an example, we'll look at one typical line of a such a program:
10
LOOP LDA $0342 ;start by getting hi byte of variable X
Let's take one part of this line of assembly language code at a time. The first field, the label field, may or may not be present. If it is present, you can tell the assembler to address this line by using its label, in this case, LOOP Therefore, we could subsequently write another line of code which branched to LOOP, and the assembler would know where we wanted to go. Generally, labels are used only when we know we will later need to reference this line from another portion of the program. As mentioned above, the label field, if present, must have exactly one blank space between the last digit of the line number and the first character of the label. The first character of the label must be a letter from A to Z, and the other characters must be either letters or the digits 0 through 9. The label may be as short as 1 character or as long as (106 minus the number of digits in the line number). Since some of the assemblers for the ATARI limit the number of characters which may be used in the labels, all label names used in the programs in this book will contain six or fewer characters.
The mnemonic, often called the op code, is the 6502 instruction that we wish the computer to execute at that point in the program. In the example given above, we want the computer to load the accumulator, and the mnemonic for this is LDA, as we learned in Chapter 5. This instruction must appear either with one blank space between itself and the label, if there is a label, or with two blank spaces between the last digit of the line number and the first letter of the mnemonic, if there is no label. For example:
10
LABEL LDA $0342 or 10 LDA $0342
The reason for this should be apparent: if only one such blank space were left after the line number, the assembler would try to apply the mnemonic as a label, and you'd end up with a label called LDA. The assembler would then try to interpret the operand, $0342, as a 6502 instruction, without any success whatsoever.
The operand is the conclusion of the 6502 instruction, and specifies the address or number we would like to operate on. For instance, in this case the operand defines the absolute addressing mode, in which the accumulator is to be loaded with the number stored in memory location $0342. The operand could have been #$24, in which case the addressing mode would have been immediate, and the accumulator would have been loaded with the hexadecimal number $24, instead of a number from someplace in the computer's memory. The operand starts with at least one blank space between its first character and the last character of the mnemonic, although more blank spaces are permitted. In fact, you can tab over to the operand field if you so desire. In this book, we'll use one blank space.
With any mnemonic which uses the accumulator addressing mode, the operand must be the capital letter A to be properly interpreted by the Cartridge. Therefore, an instruction to rotate the contents of the accumulator to the right it must be written like this:
130
ROR A ;note the A
The comment field is the final field of a line of assembly language code, and it should describe the operation being performed in terms of program function. That is, the comment should not describe the operation (that LDA $0342 means to load the accumulator from $0342); rather the comment should remind you what that particular line of code is doing, so that you can go back to it 6 months later and not spend 10 hours wondering what in the Sam Hill that stupid line was for. In our first example above, the comment tells us that we're getting the high byte of a variable we're calling X, from memory location $0342.
Comments can be set off from code in two ways. First, if at least one blank space follows the operand field, anything else following on that line will be interpreted by the assembler as a comment. A second way is to denote an entire line as a comment. As we shall see, this often makes your code much more readable and will be a big help in keeping your sanity. To so designate a line, follow the line number with one space and place a semicolon in the next space. Anything else on that line will be interpreted as a comment at assembly time. Examples of each of these methods are:
100
; This entire line is a comment line
100 LDA $0343 ;This is a comment also
100 LDA $0343 ;This is a comment also
For the purposes of this book, all comments, either full-line or not, will be preceded by a semicolon, so if you see a semicolon before some text, you'll know that you're reading a comment.
Now we know the structure of a line of assembly language code and there are a few other conventions that we'll need to know as well.
Directives
Most assemblers have available for the programmer's use a series of instructions which can be interpreted by the assembler, essentially extending the instruction set of the 6502. These are called directives, or, sometimes, pseudo-ops, since they are used just like op codes but are not part of the 6502 instruction set. The most important of these for the Assembler/Editor Cartridge are described below, with a brief description of each.
One of the most important is the origin statement. Since the assembler creates machine language code which will reside in a specific place in memory, we need to tell the assembler where this place is. To do this, we use the origin statement. With the Cartridge, the format of this statement is as follows:
10 *= $0600 ;the beginning
Note that there are two spaces between the last digit of the line number and the asterisk, no spaces between the asterisk and the equal sign, and one space between the equal sign and the first character of the address. This line tells the assembler that we want our code to begin assembly at hexadecimal address $0600, or page 6. Such an origin statement will usually be the first, or one of the first, statements in our programs. When the assembler sees the *= directive, it assigns the program counter the value of the expression following this directive. It is perfectly feasible to have more than one *= directive in any program if different regions of code are to be assembled in different areas of memory.
Other pseudo-ops include:
.BYTE reserves at least one location in memory for future use. The operand can place information into this space. For instance, the instruction
110 .BYTE 34
opens one location in memory at the current position of the program counter, and stores the number #$22 (decimal 34) in that location. It is also possible to store a series of bytes using one .BYTE instruction, as shown below:
125 .BYTE "HELLO",$9B
This will store the hexadecimal numbers $48, $45, $4C, $4C, $4F, and $9B in consecutive locations. These numbers are the ATASCII (ATari ASCII) codes for the letters of the word HELLO.
.DBYTE reserves two locations for each value in the operand. This instruction is used for data in which the numbers are larger than 256, and so require 2 bytes to be stored. The number is stored with the high-order byte first, followed by the low-order byte. For example,
115 .DBYTE 300
stores 2 hexadecimal numbers in consecutive memory locations. The first is $01 and the second is $2C, since 300 decimal equals $012C hexadecimal.
.WORD is identical to the .DBYTE directive, except that the low-order byte is stored first, followed by the high-order byte.
LABEL = is used to assign a value to a label. For instance, if we write a program which requires the frequent use of the address $9F, we can assign this address to a named variable, as follows:
112
FREQ = $9F
Since the label in the LABEL = directive is a real label, it must begin with exactly one blank space between the last digit of the line number and the first character of the label. Now, whenever we need the address, we can call the label instead; for instance,
245 LDA FREQ
The assembler now knows to load the accumulator from the address $9F.
.END tells the assembler that it has completed the assembly and that it should stop right there. Obviously, it should be the last line of your program. The Assembler/Editor Cartridge assumes that if there are no further lines of code and no .END directive is included, the program is finished; this makes the .END directive optional, much as the END statement in an ATARI BASIC program is optional.
There are many other directives available for the Cartridge, but we have discussed the most important ones and for the moment they are the only ones we'll discuss. Please refer to the Assembler/Editor manual for a further discussion of all of the pseudo-ops available.
OPERAND FIELD MATH
One further note on the Cartridge is that it supports addition, subtraction, multiplication, and division in the operand field. For instance, if we would like to break up the address of the label LOOP into a high and a low byte, we can write the following section of code:
135 LDA #LOOP&255 ;get low byte of LOOP
140 STA DEST ;store it in DEST
145 LDA #LOOP/256 ;get high byte
150 STA DEST+1 ;and we're done.
140 STA DEST ;store it in DEST
145 LDA #LOOP/256 ;get high byte
150 STA DEST+1 ;and we're done.
Line 135 takes the address of LOOP and ANDs it with #$FF, giving us the low-order byte. Line 145 divides this address by 256, giving us the high-order byte of the address. Note that line 150 will store this byte in the address DEST plus 1, or 1 byte higher in memory than DEST.
THE ATARI MACRO ASSEMBLER
We will now discuss the differences between the other available assemblers and the Assembler/Editor Cartridge.
A macro assembler allows you to write, cleverly enough, macros, which are generally short segments of assembly language code that you plan to use frequently within a program. An example of a macro might be JMI, which would contain the code to implement a Jump on Minus instruction, which, as we now know, is not present in the 6502 instruction set. Using a macro assembler, we could code this instruction, and then whenever we want to jump on minus, we could use JMI very much like a normal instruction of the 6502 set. At assembly time, the assembler will find the right macro and insert it properly everywhere the JMI instruction occurs.
The ATARI Macro Assembler is unique among the available assemblers in its ability to assemble one single file many times larger than the entire memory space of the ATARI! It does this by reading code from your disk drive, assembling it, and writing the assembled object code back to another disk file. It has another powerful feature – the use of separate files, called SYSTEXT files. These can include all label references, so that such a SYSTEXT file can be constructed once, with all of the equates for the ATARI contained in it. This file can then be used for all programs you'll ever write, without the need for laboriously constructing this table again.
This assembler is also different in one other respect – it uses no line numbers. Lines are simply inserted or deleted in the appropriate order, and your program scrolls through memory. To see the beginning of your program, you scroll the text down until the beginning appears, and vice versa for the end of your code. When using this assembler, you should take care to place the lines in the correct order, or else you'll have trouble at run time.
The label begins in column 1, and the mnemonic is generally tabbed over about eight spaces. The operand and comment fields follow the mnemonic. Labels may be of any length, but only the first six characters are significant; longer labels are used at your own risk. Octal numbers, in addition to binary, decimal, and hexidecimal, are supported, and when used, are prefaced by the @ sign. Strings, such as the HELLO used as a previous example, are enclosed in single, rather than double, quotation marks. The AMAC assembler also supports addition, subtraction, multiplication, and division, as well as a number of logical operations. An address may be broken into its high- and low-order bytes simply by using the words HIGH and LOW, without the need for division as in the example above. Macros are, of course, allowed. For any instructions which utilize the accumulator mode, the letter A should follow the mnemonic. The pseudo-ops are virtually all different from those of the Cartridge. The ATARI Macro Assembler (AMAC) and the Assembler/Editor Cartridge versions of the pseudo-ops are outlined below:
AMAC | Cartridge | Comment |
DB |
.BYTE |
(.BYTE also acceptable for AMAC) |
DW |
.WORD |
(.WORD also acceptable for AMAC) |
END |
.END |
(.END also acceptable for AMAC) |
EQU LOC |
= |
|
ORG |
*= |
sets location counter for assembly |
MAC/65
MAC/65 is also a macro assembler, with features similar to those already described. This assembler is the only one which tokenizes your source code, just as BASIC does. In addition, it checks the syntax of your line as soon as you hit RETURN after typing it. This feature is not quite as important in assembly language programming as it is in BASIC, since most assembly language errors are not a result of syntax errors, but rather logic errors. However, for the beginning assembly language programmer, this is a nice feature which may eliminate some simple, common errors. Line numbers in the range of 0 to 65535 are required for each line. As described for the Assembler/Editor Cartridge, the line number must be followed by a single space before a label, and by 2 spaces before the mnemonic in lines with no labels. Strings within a program must be enclosed in double quotation marks, as when using the Cartridge. Comments begin at least 1 space beyond the operand field, and need not be prefaced by a semicolon. Comments which take an entire line must be prefaced by either a semicolon or an asterisk. Any instructions using the accumulator mode of addressing require the mnemonic to be followed by the capital letter A, as with the Cartridge. Labels may be up to 127 characters long, with all characters significant.
MAC/65 supports addition, subtraction, multiplication, and division. However, whereas the Cartridge has no operator precedence and simply evaluates a complex arithmetic expression from left to right, this assembler has the usual multiplication > division > addition > subtraction operator precedence. It also uses the > and < symbols to designate the high and low bytes, respectively, of an address.
This assembler can be used with files created by another assembler: an ENTER command will allow such files to be read into the editor. In fact, unnumbered lines such as those produced by AMAC can even be numbered automatically using the ENTER command. Then minor changes in syntax will allow the program to be modified and assembled using MAC/65.
The pseudo-ops discussed above for the Cartridge are used in exactly the same way for MAC/65, and the *= symbol for the origin statement is also used in both assemblers. Macros are supported, and the debugger which comes with MAC/65 is a separate program which must be loaded separately, much as AMAC.
THE SYNASSEMBLER
The SYNASSEMBLER also requires line numbers, which must be followed by a blank space before entering a label. The acceptable line number range is from 0 to 63999. Tab stops are built into the editor to allow easy formatting of lines of code, and in fact, the automatic line-numbering mode requires the use of the tab to print the new line number to the screen. Labels may be up to 32 characters long, and all characters are significant. The accumulator addressing mode does not use the letter A following the mnemonic. Comments require semicolons only when whole lines are used for comments.
The SYNASSEMBLER supports only addition and subtraction. Therefore, you'll need to write code to support other operations, or use calculated numbers rather than complex arithmetical expressions. Operators are included, however, to separate a number into high and low bytes – the # and / symbols, respectively. For example,
124
LDA #STOR1 ;indicates low byte of STOR1
128 STA STOR3
132 LDA /STOR1 ;indicates high byte
128 STA STOR3
132 LDA /STOR1 ;indicates high byte
The pseudo-ops of the SYNASSEMBLER are considerably different from those of the Cartridge, as you can see in the following chart:
SYNASSEMBLER | Cartridge | Comment |
.AS |
.BYTE |
for ASCII literals only |
.BS |
.BYTE |
|
.DA |
.Word |
|
.EN |
.END |
|
.EQ |
= |
|
.OR |
*= |
The SYNASSEMBLER also has an ENTER command, which will allow you to use it to assemble code produced by one of the other packages discussed in this chapter. One use you might make of this feature is to have the SYNASSEMBLER assemble code originally written using the Cartridge, since the SYNASSEMBLER is from 50 to 100 times faster in assembling code than is the Cartridge. For short programs this difference is relatively insignificant, but for long programs, the time needed to assemble code is a substantial portion of the debugging process. Cutting this time substantially will yield a much more productive editing session.
Finally, strings may be surrounded by any delimiter, so either single or double quotation marks can be used.
THE MACRO ASSEMBLER/TEXT EDITOR (MAE)
MAE is another macro assembler available for the ATARI computers. It requires line numbers in the range of 0 to 9999. Any label in a line must immediately follow the line number with no intervening space! From the label on, fields are separated by spaces. Semicolons are required at the beginning of full line comments only; comments at the end of a line need only be separated from the operand by a space. The accumulator mode requires that the letter A follows the mnemonic.
MAE supports only addition and subtraction, but two symbols are included for calculating the high and low bytes of a number - #H and #L, respectively. Labels may be up to 31 characters long, with each character significant.
The pseudo-ops supported by MAE, with their Cartridge equivalents, are charted below:
MAE |
Cartridge |
.BA |
*= |
.BY |
.BYTE |
.EN |
.END |
Single quotation marks are required around strings. One important difference between MAE and all of the other assemblers is that with MAE, any reference to zero page addressing must begin with an asterisk. For instance, if STOR1 and STOR2 are both defined to reside on page zero, then the code to load the accumulator from STOR1 and then to store this value in STOR2 would need to be written like this:
105
LDA *STOR1
110 STA *STOR2
110 STA *STOR2
EDIT 6502
This assembler does not require line numbers for the assembler code. It supports addition, subtraction, multiplication, and division, with no precedence; complex arithmetic expressions are evaluated from left to right. The program counter can be referenced by the use of the asterisk. Strings can be surrounded by either single or double quotation marks. If single marks are used, the high bit of each byte is cleared (set equal to zero), and if double marks are used, the high bit of each byte is set (made equal to 1). The accumulator addressing mode does not use the letter A, just the mnemonic, as here:
LDA
$4235
ASL
ASL
The > symbol can be used to generate the high byte of a number, and the symbol will generate the low byte.
Edit 6502 uses a number of pseudo-ops which are different from those of the ATARI Assembler/Editor Cartridge, as described below:
Edit 6502 | Cartridge |
EQU |
= |
ORG |
*= |
DFB |
.BYTE |
DFW |
.WORD |
END |
.END |
Now that we've explored some of the differences you'll need to know about to use any of these assemblers for the ATARI computers, we can begin to write some useful assembly language programs. The next chapter explores some subroutines which can be used from BASIC to substantially speed up a program.
Return to Table of Contents | Previous Chapter | Next Chapter