TUTORIAL: Assembler/Editor, pt. 1
From: Michael Current (aa700@cleveland.Freenet.Edu)
Date: 01/18/92-12:42:42 PM Z
From: aa700@cleveland.Freenet.Edu (Michael Current)
Subject: TUTORIAL: Assembler/Editor, pt. 1
Date: Sat Jan 18 12:42:42 1992
Reprinted from A.C.E.C. BBS (614)-471-8559
The Atari Assembler Editor Reference
By Matthew J. W. Ratcliff
Released 20-NOV-89
I teethed on the Atari Assembler Editor (Asm/Ed) cartridge way
back in 1982. Now, nearly seven years later, it is becoming more
popular than ever. Why? Well, I have seen several mail order
ads over the past year offering Asm/Ed for only $10 or even as
little as $5. Here's the hitch, no documentation. A lot of
Atari cartridges were left over from the Warner days, when the
Tramiel family took over the company. It seems that thousands of
cartridges were sold "by the pound", with no boxes or
documentation, just to clear out the warehouses. Now many of you
Atarians have decided that Asm/Ed was too good to pass up at such
a low price, because you just know that someday you will learn
assembly language programming.
This article is for you, the Atarian who took the plunge and
bought the Asm/Ed cartridge without documentation. I won't
pretend to teach you how to write your own assembly language
programs (although I'm bound to toss in a bit of free advice
along the way), our Boot Camp series will do that job well
enough. I hope to give you a quick comparison between BASIC and
assembly language, to illustrate the major speed differences. I
will also cover the mechanics of writing a USR routine to help
speed up BASIC programs and a stand alone assembly program that
may be executed from DOS.
The Asm/Ed cartridge will work with any DOS (disk operating
system) from Atari DOS 2.0s all the way through DOS-XE and even
the SpartaDOS X cartridge. The Asm/Ed cartridge itself is made
up of three basic components, the Editor, Assembler, and
Debugger. I will present a quick reference for all the commands
of each section of the cartridge, and then lead you through the
creation of your first program.
After booting your Atari with Asm/Ed installed, and control is
sent to the cartridge, the EDIT prompt will appear. From here
you can begin entering your assembly "source code", with line
numbers, assembly mnemonics, and comments. The editor is line
oriented, requiring line numbers. They are used for reference
while editing only, and are not used as part of the program
itself, as in BASIC.
Editor
NEW
The NEW command clears all assembly source code from memory,
providing a clean slate for entry of a new program. NEW will
irrevocably erase your program from memory, so always be certain
to save important code before using this command to start
something else. I highly recommend using a comment as the first
line of every program, similar to the following:
10 ;LIST#D:USRTEST1.ASM
It is easy to forget what file you are working on, or make a
typographical error when using the LIST command. Saving a file
to the wrong place can ruin a lot of work in a big hurry. By
placing the LIST command, followed by the correct file name, in a
comment, you may simply displasy that line and use the full
screen editor to eliminate the line number and comment character
(the semicolon), press RETURN, and execute the proper save
command every time. This good habit has saved me countless hours
of frustration. Use it!
DEL
This command DELetes lines of code. The format is:
DEL xx - Delete line xx from the program.
DEL xx,yy - Delete lines xx through yy from the program.
NUM
NUMber lines automatically, for fast entry of source code. If no
starting number is specified, the program begins with the last
line number currently in your program, plus 10. Type your code
and comments, pressing RETURN when each line is complete. Press
RETURN only to stop auto-entry of code. Do NOT use full screen
editing functions to change lines previously entered, while still
in the NUM mode. This will give unpredictable results. The
command format follows:
NUM - Increments by the number 10 after each line.
NUM nn - Begin line entry at line nn,increment by 10.
NUM nn,mm - Forces the next statement number to be nn and
the increment to be mm.
The last command format may be used to insert new lines between
code that already exists (e.g. NUM 11,1 to enter up to 9 lines of
code between lines 10 and 20).
REN
RENumber the file. It will resequence all the line numbers of
the source file. The command format is below:
REN - Renumber starting with 10, increment by 10.
REN nn - Renumber in icrement of nn, start with 10.
REN nn,mm - Renumber by nn, starting with mm.
You might think that you can load a LISTed BASIC program, and use
this to renumber it. In fact you can, except that all the GOTO
and GOSUB statements will not be resolved. For very small BASIC
programs you can do this, but you are better off using a renumber
utility. When inserting a lot of new code, REN may be used to
space the line numbers wide apart, thus allowing entry of more
new code with the NUM entry method between consecutive lines of
source code. This is a bit archaic by today's standards, but
works well. Should you move on to IBM PC based assemblers, or
other sophisticated machines, you will find no such animal as a
line number. You simply create and edit your source code in a
word processor like environment. The assembler can easily
resolve all the source code and labels without the need for those
pesky line numbers. Asm/Ed and Mac/65 users must continue to
deal with them, however.
FIND
The FIND command can be used to help locate any string of text
anywhere in the program. This is the command format:
FIND/THIS/ - Find the first occurrence of the characters
"THIS", and display the line it is found on.
FIND/THIS/A - Find all occurrences of "THIS". Each line
is listed to the display as it is located. Pressing
control-1 will throttle screen scrolling.
FIND/THIS/xx - Find the string "THIS" on line number xx.
The line is listed, if found.
FIND/THIS/xx,yy,A - Find all occurrences of "THIS" from
lines xx through yy, inclusive.
Note that when searching, the line numbers themselves are ignored
(you don't search the line numbers, when looking for a particular
number). In the examples above the string of interest was
delimited by the slash (/) character. Any matched pair of
characters may be used as delimeters. The following would be
used to find all occurrences of the slash character in your
program:
FIND */*,A
REP
REPlace strings in the file:
REP/OLD/NEW/ - Replace the first occurrence of "OLD" with
"NEW".
REP/OLD/NEW/,A - Replace ALL occurrences of "OLD" with
"NEW". Use the All option with care.
REP/OLD/NEW/xx,yy - Replaces the first occurrence of "OLD"
with "NEW", in the line number range xx to yy.
REP/OLD/NEW/xx,yy,A - Replace all occurrences of "OLD" by
"NEW" in lines xx through yy.
REP/OLD/NEW/xx,yy,Q - Replace with query. You will be
prompted to press Y for each replace.
File Commands
The following commands may all be associated with files:
LIST - Display or save lines of source code
PRINT - Same as list but omit line numbers
ENTER - Retrieve source program
SAVE - Save an object program (assembled code)
LOAD - Load an object program
In BASIC filenames are enclosed by quotes, such as
SAVE"D:MYFILE.BAS". In Asm/Ed the filename is preceded by the
pound sign (#); no quotes are used. Filenames you may use are
shown here:
#E: - The screen editor, used by default with
some commands such as LIST.
#P: - Refers to the printer.
#C: - This is used in reference to the Atari
program recorder.
#Dn:filename.ext - This is a disk file. The 'n' refers to
drive number, which may be from 1 to 8, depending on the DOS
and drive configuration you employ. If no drive number is
specified, drive number 1 is defaulted. The name of the
file may be up to 8 alphanumeric characters, followed by a
period and optional 3 character extender. The extender may
be anything you wish. ASM or SRC is generally used for
assembly source files, and OBJ or COM for executable object
files.
LIST
The LIST command is used to display, print, or save to a file
assembly source code. The formats are:
LIST - List the entire program to the screen.
LIST nn,mm - Display lines nn through mm.
LIST #P: - Print the entire source program.
LIST #C: - List the source code to cassette.
LIST #D:FL.ASM - List the entire program to FL.ASM on disk
drive number 1.
LIST #D:FT.SRC,10,100 - List lines 10 through 100 to file
FT.SRC.
Any filename or device specification may be followed by the line
number range specification. The PRINT command functions exactly
like LIST, except that the line numbers are not output. Since
assembly source files REQUIRE line numbers, it won't be very
useful to PRINT your program to disk and attempt to ENTER it
later. This would hopelessly confuse Asm/Ed; always LIST source
code to disk.
ENTER
The ENTER command is used to retrieve a previously LISTed source
file. A valid input device must be specified such as:
ENTER#D:MYFILE.ASM
ENTER#C:
If you wish to merge a program, append a ",M" to the ENTER
command, such as:
ENTER #D:ROUTINES.ASM,M
This merge works the same as the following sequence would in
Atari BASIC:
LOAD "D:MYPROG.BAS"
ENTER "D:NEWSUBS.LST"
The lines are merged. If any line numbers in the file to be
merged match those of the file already in memory, the merge file
takes precedence. If you wish to append a file to a current
working program it may be best to ENTER the merge file first,
RENumber it with some large range such as 20000,1 and then LIST
it out to a temporary file. Then ENTER your main program, and
finally enter with the merge option this renumbered file.
SAVE
Use the SAVE command to write a block of memory, such as an
object program, to a file. Let's say your program begins at
$4000 (with an *=$4000 at the top of your assembly code). After
the ASM command, you see the final address was $41FE. Then, to
create a binary image of this file, which may be loaded and run
later, enter the following command:
SAVE #D:MYFILE.OBJ < 4000,41FE
Note that the addresses are always assumed to be in hexadecimal,
and you do not specify a dollar sign ($) to indicate this on the
SAVE command line. You may also SAVE to the cassette (#C:).
With the proper ASM command, your object files may be created
automatically, as we will see. Note that you may assemble your
program in memory, and then go to DOS and use the DOS memory save
command to create this object file as well. (The advantage of
the DOS memory save command is that you can specify the RUN
address as well, so that your program automatically executes when
you perform a binary load. There are ways to set this up with
the ASM command as well.)
ASM
Once you have created your assembly source code and LISTed it to
a file for safe keeping, it is time to assemble it. This is
Asm/Ed's primary function, to convert your source code into
executable object code. When you issue the ASM command, the
current file in memory is scanned for syntax errors. If it
understands all your source code, all the assembly mnemonics are
converted into equivalent binary codes and written to memory or a
file.
Care must be taken that your code assembles to an area of memory
which does not conflict with your source code. Before assembling
a program for the first time always enter the SIZE command.
Three hexadecimal numbers will be displayed, such as:
10F4 1345 9C1F
The first number indicates where in memory your source code
begins, just above DOS's basic memory requirements. The second
number is where, in memory, your source code ends. The final
address is the top of useable RAM. At the top of your program
will always be a statement similar to the following:
0 *=$4000
This tells the assembler to start building your object code at
memory location 4000 hexadecimal, the program origin. This
address may be any number between the second and third numbers
reported by the SIZE command, with some notable limitations, when
assembling to memory. It may be any value above the first
number, so long as you assemble to a file.
If you wish for your program to assemble into lower memory, you
may use the LOMEM command. This must be the first command
entered, once you start up Asm/Ed and receive the EDIT prompt.
The format is: LOMEM xxxx, where xxxx is the hexadecimal address
to set the new low memory value. This is the first address value
reported by the SIZE command detailed above. For example, if you
want your program to load at address $2400, and you know the
object code will be 4K or less, then use LOMEM $3400 ($1000 is 4K
bytes). Then ENTER your program, and use *=$2400 at the top of
the file to set the origin. Then the program may be assembled in
RAM to RAM safely, so long as your object code does not grow
beyond 4K.
If you plan to write stand alone assembly programs, which may be
loaded from DOS with the binary 'L'oad command, I recommed an
origin of $3400. This will set the start address of your code
above both DOS.SYS and DUP.SYS RAM in Atari DOS, any version
through DOS-XE, as well as any version of SpartaDOS.
Unlike BASIC, you must manage memory yourself. If your program
origin is too close to the second number from the size command
the assembler may get confused. The assembler must build a
symbol table and assign some temporary storage as it processes
your source code. It starts building this information from the
end of your source code, and grows upward. If the symbol table
runs into the area where the object code is being stored in RAM,
the assembler is likely to generate a lot of erroneous PHASE
errors. If your origin is set too high, your object code will
run into display memory and eventually out of room.
These problems may be avoided in several ways. The general form
of the ASM command is:
ASM #D:SOURCE, #D:LIST, #D:OBJECT
The first filename in the ASM command represents the file your
assembly source code is stored in. This allows you to assemble
from disk (but not cassette, since Asm/Ed requires multiple
passes through the file). If this field is empty (simply place a
comma immediately after the ASM command), then the source code is
assumed to be in memory. The second filename specifies a listing
file, where a complete "assembled listing" is routed. This will
usually be the printer (#P:). If this field is left empty, the
listing goes to the screen. The listing always goes somewhere;
it cannot be turned off as it can in Mac/65. However, assembler
"directives" may be used to control the output of a listing, as
we shall see. The third field is the filename where the object
code will be stored. If this field is not specified, your
program is assembled to memory. Always make a current listing of
your program on disk or cassette before issuing the ASM command.
If you have set up memory mapping improperly, the source code
will get clobbered in a big hurry.
Assembler Directives
Directives, or pseudo operations (pseudo-ops), are special
instructions to the assembler. They can be used to control
listing format, program title for listing, allocation of memory,
and more. In general, assembler directives begin with a period
(.), followed by some key word and associated parameters.
OPT
The OPTions directive controls assembler output. They are as
follows:
.OPT NOLIST - Suppress output of listing during assembly.
.OPT LIST - Output assembly listing, default.
.OPT NOOBJ - Do not generate any object code during
assembly.
.OPT OBJ - Output object code, default.
.OPT NOERR - Do not display error messages while
assembling. There is no good reason to ever use this
option.
.OPT ERR - Display error messages when assembling,
default.
.OPT NOEJECT - Do not skip 4 lines at the bottom of each
page, when outputting the listing.
.OPT EJECT - Skip 4 lines at the end of each page,
default.
More than one option may be placed on a single line, such as .OPT
NOLIST,NOOBJ. Note that the Mac/65 assembler defaults to .OPT
NOOBJ; it does not generate object code unless explicitly told to
with the .OPT OBJ directive. Asm/Ed is just the opposite.
Whenever you are assembling your program frequently, working out
syntax and undefined label errors, it is generally wise to have a
.OPT NOOBJ near the top of your program. When you are ready to
generate code and start test running it, then change it back to
.OPT OBJ.
Title and Page
The title and page directives are designed to make your assembly
listings easier to read. The title directive is generally used
to specify the name of your program, revision, and date. The
page directive can be used to force a page break and optionally
output some text. For example:
10 .TITLE "Attack Of The Killer Dweebies, Version 1.0"
20 .PAGE "Program equates"
...
...
200 .PAGE "Graphics Routines"
...
...
300 .PAGE "High Score Routine"
Tab
The TAB directive is used to set the spacing of the fields of
your assembly code for listings. The command format is:
10 .TAB 12, 17, 27
The above example illustrates the defaults used by Asm/Ed. These
may be set to any position you find most suitable for your
printer listings. The first number indicates the field where the
mnemonics (assembly opcodes) will appear, the second for the
operands, and the third for the comment field. For example,
suppose your program has a lot of long labels with a maximum of
15 characters. Then you may wish to set the tabs as follows:
10 .TAB 20, 25, 40
which would make for a prettier listing on the printer.
BYTE, DBYTE and WORD
The BYTE, DBYTE, and WORD directives are used to reserve storage
in memory, similar to variables in BASIC. Labels may be
associated with these directives for easy reference. For
example:
100 LDA STORAGE
110 LDX STORAGE+1
...
500 STORAGE .BYTE 34, $45
In the above the statement at line 100 will fetch the first value
at location STORAGE, which is the number 34 following the BYTE
directive. In line 110 the X register will receive the data
value 45 hexadecimal. Note that the assembler will perform the
address arithmetic "STORAGE+1" automatically. The BYTE directive
may also be used to reserve storage for strings, such as:
100 LDA #STRING/$100
110 LDX #STRING&$FF
...
320 STRING .BYTE "This is a test", 155
In line 320 the BYTE directive reserves storage for a string
initialized to "This is a test" followed by a 155 (ATASCII
carriage return). The code in lines 100 and 110 fetches the
address of the label STRING, placing its address high byte in A
and low byte in X. This technique will be commonly used to pass
the address of data collections (such as strings or data tables)
to subroutines.
The DBYTE directive reserves two consecutive memory locations,
generally used for numbers greater than 256, in high byte, low
byte order. For example:
1000 DATA .DBYTE 258
The above will result in two bytes of memory being reserved at
location DATA, with the values 1 and 2 respectively (1*256 + 2
equals 258). Addresses are stored in low byte, high byte format
as expected by the 6502 microprocessor. The WORD directive is
used for this, such as:
100 *=$3400
110 START LDA #45
...
290 RTS ;End of program
300 *=$2E0
310 .WORD START
In line 100 the origin of the program, or program counter, is set
to 3400 hexadecimal. The first line of code, with the label
START, will then be assembled into your computer's memory at
$3400. At line 300 the program counter is reset to $2E0. At
line 310 we have the WORD directive, immediately followed by the
label START. The assembler will "backtrack" as it processes your
source code, realize that START refers to memory location $3400,
and place this value (low byte, high byte sequence) in memory at
$2E0, $2E1 respectively. This is a special location, $2E0,
commonly referred to by name as RUNAD in Atari memory maps. When
you assemble a program to disk, which will be loaded and run from
DOS, you use the above technique to set the run address of your
program. When the program ends with an RTS, control is returned
to DOS. Most game programs do not end, but you will use this
technique for many utilities. As we will see later, a BRK
instruction is used, instead of RTS, when testing programs from
Asm/Ed's debugger.
Label Directive
You do not have string, integer, and floating point variables in
assembly language, the way you do with Atari BASIC. As we saw
above, you must set up your own storage and interpret it
properly. There are no automatic mechanisms in assembly language
for managing variables. To make life easier, you will want to
attach meaningful labels to constant values, such as:
10 RUNAD = $2E0
...
1000 *=RUNAD
1010 .WORD START
It is much easier to tell from this example that the intended run
address of our program is defined at the label START. In the
previous example for the WORD directive, we simply had the number
$2E0. Unless you want to memorize a lot of memory locations,
employ meaningful labels wherever practical.
Labels are used for reference when you want to GOTO (JMP) or
GOSUB (JSR) in assembly language. For example:
10 PROMPT .BYTE "PRESS RETURN TO CONTINUE",155
...
100 LDA #PROMPT/$100
110 LDX #PROMPT&$FF
120 JSR PRINTSTRING
...
500 PRINTSTRING STA ICBADR+1 ;Print the string pointed to
510 STX ICBADR ;by A & X registers
...
Labels may also be used in branch instructions such as:
50 CONTINUE LDA TABLE,X
...
100 DEX ; Decrement our loop counter, x register
110 BEQ EXITLOOP
120 BNE CONTINUE
130 EXITLOOP STA RESULT
...
In the above example we have set up a loop, similar to a BASIC
FOR/NEXT loop, between the labels CONTINUE and EXITLOOP. In line
100 the X register is decremented by 1 (we assume it was
initialized by some code previous to line 50). If the result of
the DEX instruction is zero (BEQ) then control will be passed to
EXITLOOP. If the X register has not gone to zero (BNE) the
control is sent back up to CONTINUE. As a result of DEX the zero
flag can only be set (BEQ) or cleared (BNE), so we have exhausted
the possibilities. It would have been equally valid to use:
120 JMP CONTINUE
Generally, whenever you have the choice between a JMP and Bxx
(branch) instruction, use the branch. It will require less
memory and work faster. The problem is that a branch is limited
to plus or minus about 127 bytes from the current position. If
you try to branch too far, you will get an assembly error. Then
JMP instructions, or combinations of JMP and branch instructions
may be required.
Origin Directive
We have already used the "*=" directive in many of the previous
examples. This tells the assembler to "set the program counter
to the following address". The address may be some number, or a
label, or some expression (so long as the assembler may resolve
it to a fixed value). Some examples are:
100 *=$3400 ; Always a safe place to start a program
...
300 START = $4400
310 *=START
...
500 *=START + 439
...
600 HERE *=*+45 ; Reserve 45 bytes of storage at HERE
Take note of the spacing used in all of our examples. Any label
always begins one space after the line number, referred to as the
label field. The opcode field begins at least one space after
the start of the label field. If a line of code has no label on
it, then your assembly mnemonics may begin two spaces after the
line number. At least one space after the op code field will
begin the operand field. This field is optional since not all
assembly mnemonics have an opcode (such as DEX, or INY).
Anything after the operand field is ignored by the assembler, and
assumed to be the comment field. A comment can take up an entire
line, when the label field begins with a semicolon. Below is a
sample line of code with all four fields:
LABEL OP OPERAND COMMENT
CODE
--|-----|-----|-------|------------------------------
10 START LDA #155 ;Get Carriage return character
IF Directive
The IF directive is used for "conditional assembly". This may be
used to enable, or disable the generation of some "test code",
for example, based on the value of a number, label, or
expression. For example:
10 DEBUG = 0 ;Enable my debugging test code
...
300 .IF DEBUG @ENDOFDEBUG
310 ; Debugging test routines start here
...
500 ENDOFDEBUG ; End of debugging code
If the expression (DEBUG above) is equal to zero, then everything
from the line following the IF directive to the specified label
(ENDOFDEBUG) is assembled. When you are satisfied that your code
works, don't throw away all that useful testing code. Simply
change line 10 to DEBUG = 1 and reassemble your program. If you
do not understand the use of conditional assembly, don't worry.
I have only used it a few times in the past seven years, and
generally you don't need it at all.
END Directive
The Asm/Ed manual recommends that every program have one .END
directive, as the last line. It really isn't necessary, since
the assembler knows when to stop (it runs out of source code to
assemble). If you place a .END in the middle of your program
inadvertently, all code after it will be ignored and not be
assembled. I seldom use a .END in any of my assembly code.
(continued in asmed2.txt)
--
Michael Current, Cleveland Free-Net 8-bit Atari SIGOp -->> go atari8 <<--
The Cleveland Free-Net Atari SIG is the Central Atari Information Network
Internet: currentm@carleton.edu / UUCP: ...!umn-cs!ccnfld!currentm
BITNET: currentm%carleton.edu@interbit / Cleveland Free-Net: aa700
-----------------------------------------
Return to message index