home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Crawly Crypt Collection 1
/
crawlyvol1.bin
/
program
/
books
/
68k_book
/
arp_doc
/
chap_02.doc
< prev
next >
Wrap
Text File
|
1985-11-20
|
87KB
|
2,308 lines
Atari ST Machine Specific Programming In Assembly
Chapter 2: The Editors/Assembler/Debugger
My recommendations concerning an assembly language
programming environment for the Atari ST are based on the
results of a comparative analysis which are so
overwhelmingly lopsided that I can present conclusions
without evidence of those results. I have used three
MC68000 assemblers on the Atari ST; and I have concluded
that AssemPro is the only one worth discussing. AssemPro,
written by Peter Schulz in 1986 and produced by Data Becker,
is distributed by Abacus. Comparing this superior assembly
programming package to the others would serve no purpose.
Abacus products can be purchased from Atari ST dealers, but
if you can't find what you want, there is an order form in
the back of the Internals book. The Abacus phone number is
(616)698-0330.
Establishing a Programming Environment
Your programming environment will be composed of
computer hardware and software, furniture and ambiance. If
you are accustomed to lengthy sessions with a computer, you
probably know that provisions for anatomical comfort during
those sessions are prerequisites for success. Proper
lighting, comfortable furniture and an appropriate level of
seclusion create an ambiance that is conducive to
productivity. A productive ambient environment can be
comprised of nothing more than a $45.00 computer desk, a
$16.00 swivel monitor stand, a $29.00 typing chair, a $9.99
lamp and the quietest corner of a room.
Within the computer system environment, appropriate
computer hardware and software amenities will contribute to
the proliferation of algorithmic ideas and programming
statements. A hard disk drive and/or a second floppy drive
will permit faster file transfers; as will software designed
to format floppy disks in a more efficient manner than does
the ST operating system. A hardware or software print
buffer permits one to continue programming while output is
being routed to the printer. Additional ram allows the use
of a ram disk and other software amenities, such as a more
sophisticated file selector. A programmer's calculator, a
hardware or software model, is an asset also.
I use the Compute! recoverable ram disk (Recoverable
RAM Disk, Schweitzer, K., Compute!'s Atari ST Disk &
Magazine, June, 1987). My RAMDISK.INF file contains the
information shown in figure 2.1. This file is stored on the
C partition of the hard disk. I usually boot up with
RAMDISK.PRG in the AUTO folder, which is also on the C
partition. A program called AUTOBOOT.PRG, written by Gordon
Moore, available from ST Informer (909 NW Starlite Place,
Grants Pass OR 97526) on disk PDM 887 (stored in
BOOTMAKR.ARC on that disk), makes this possible. The
RAMDISK.PRG creates the 400K ram disk and automatically
loads the indicated files therein. The magazine article
explains the usage of the ram disk. If you understand the
article, then you should realize, by looking at the
information file, that I store the AssemPro and TEMPUS
files, as well as any source programs on which I may be
working, in a folder named ASSEMPRO on hard disk partition
D.
Figure 2.1. RAMDISK.INF file.
* Configuration file for COMPUTE!'s June 1987
* recoverable RAM disk.
*
SIZE=400K
DISK=H
LOAD=D:\ASSEMPRO\ASSEMPRO.INF
LOAD=D:\ASSEMPRO\ASSEMPRO.PRG
LOAD=D:\ASSEMPRO\ASSEMPRO.RSC
LOAD=D:\ASSEMPRO\ASSEMPRO.TAB
LOAD=D:\ASSEMPRO\TEMPUS.PRG
LOAD=D:\ASSEMPRO\TEMPUS.INS
LOAD=D:\ASSEMPRO\PRESERVE.TTP
LOAD=D:\ASSEMPRO\*.S
You may choose to use a smaller ram disk. With no
source file in the ram disk, the memory occupied by the
AssemPro and TEMPUS files is 222,279 bytes. You must have
enough additional memory in the ram disk to accommodate your
source file and its backup, if you choose to have TEMPUS or
AssemPro back it up. In addition, there must be room enough
to accommodate assembled files. Whenever I work with very
large source programs, I don't use the ram disk at all; I
just execute TEMPUS and AssemPro from the hard disk. But,
let me warn you about that. There have been times when
AssemPro did not replace a file on disk with an updated
version; instead it stored the updated file with the same
name as that of the original file. See figure 2.2A. I
don't know whether or not this is caused by a system fault.
Figure 2.2A. Updated and original files with identical
names.
No specific sequence of events has precipitated the
incident described. In trying to pin down the cause of the
problem, I have found that the severity of the problem
depends on the presence of certain load and stay resident
(LSR) programs. Furthermore, the problem seems to worsen
with directory depth, and, although files with PRG and S
extensions are affected, those files with TOS and TTP
extensions are most often duplicated. I had never
experienced this problem with any other ST application until
I began to use Supra Drive utilities to format my Atari
SH204 hard disk with 11 partitions. Then I began to
experience the same problem with the Tempus editor. See
figure 2.2B.
Figure 2.2B. Same problem shown in figure 2.2A, but these
files were saved from the Tempus editor.
For a time, whenever this mishap occurred, if the
duplicated files were machine code, I simply discarded the
files and reassembled. If a source file was involved, I
examined the files on disk and discarded those that were
erroneous. Of course, the best protection against such
mishaps is backups on floppies. I back up my files on two
separate floppies. Eventually I found a better way to avoid
the problem. If the AssemPro editor Back-up copy option,
under the File menu is selected, each time you attempt to
save a file that is already present on disk, a dialog box,
similar to that shown in figure 2.2C appears. You can
choose to rename either of the files and avoid the
duplication problem. I suggest that you change the
extension of eldest source files to .DUP because that is the
backup file extension used by the editor TEMPUS. I suggest
that you alter the extension of eldest object files to BAK
or DIS (for discard).
Figure 2.2C. AssemPro dialog box which appears when a file
to be saved already exists on disk, if the Back-up copy
editor option is selected. To avoid the file
duplication problem, backspace over the TOS in the
extension of the old file and type BAK. Then press the
Return key, or click on the OK button.
To avoid the file duplication problem with TEMPUS, you
can choose the Save with backup option. When you choose
that option, TEMPUS automatically alters the extension of
the eldest file to DUP before it writes the new file to
disk. Figure 2.2D shows the appearance of the backup disk
file icon when that option is used. Pages 29 and 30 of the
TEMPUS manual discusses all of the Save options, as well as
their effects, from which you can choose.
Figure 2.2D. Avoiding the file duplication problem with the
TEMPUS Save with backup option. The back up file has a DUP
extension.
I use the Atari SH204 hard disk drive, the star NX-10
dot matrix printer and the Supra Corporation MicroStuffer
print buffer. I have been very pleased with the performance
of all three items; but I have discovered that my star NX-10
prints at 68 characters per second in draft pica mode, not
at the specified 120 characters per second; and I have
learned that the quick response of a hard disk drive
degenerates rapidly when there are only four partitions;
that's why I now use Supra's hard disk utilities to format
my disk with eleven partitions.
I also consistently use the Hewlett-Packard HP-16C
programmer's calculator. Occasionally I use the
programmer's calculator that is available from the TEMPUS
menu. This type of calculator is required for the many
hexadecimal computations and number system conversions
involved in assembly language programming.
Finally, I have virtually every book and magazine ever
written in English (or translated thereto) about the ST. I
have found that access to convenient tools and references
tends to keep frustration at a minimum level. Furthermore,
even though I make statements about the inadequacies of some
portions of the references and the software I use, let me
make this clear: I could not have achieved any level of
success with the Atari ST without them. I truly owe all
that I know to the people that took the time to learn and to
communicate their knowledge via the many books, magazine
articles and public domain disks. Most of all, I owe to
Peter Schulz, the author of AssemPro, my gratitude for the
many productive hours I have spent with the ST. I know that
I cannot match the efforts of so many, but I'm going to do
what I can to perpetuate interest in this fine machine.
The Editors
I usually write my source programs with the TEMPUS
editor, then transfer the source to AssemPro for assembly,
reediting and final assembly. When the amount of reediting
to be done is extensive, I go back to TEMPUS. The number of
trips back and forth between TEMPUS and AssemPro depends on
the complexity of the program. The TEMPUS editor is so much
faster than the AssemPro editor that any inconvenience
because of the switching between them is overlooked.
You will discover the many differences between the two
editors as you use them. I only want to point out the less
obvious. The AssemPro editor begins counting text on a line
in column 1; TEMPUS begins counting in column 0. This is
the kind of, seemingly, non-critical information that one
can omit during the daily grind of writing. In my work, I
have often found just such an item of information to be the
crucial key to solving a problem. When I begin to discuss
the basepage command line and the manner in which it is
accessed by AssemPro, you will see just how critical an
offset of one character position can be.
The major difficulty I had experienced in switching
between the two editors is line deletion. TEMPUS uses
function key F8; AssemPro uses Alternate - Delete.
Therefore, I redefined the AssemPro F8 key, according to the
instructions in section 1.6.1.3 on page 17 of the manual.
The command to be used in redefining the F8 key to a Delete
key is listed in the table in section 1.8.6 on page 32. The
command of interest is LZ. Figure 2.3 illustrates the
appearance of the the Function key redefinition dialog box
for this operation, just before you press the OK button.
Figure 2.3. Redefining the AssemPro F8 function key. This
is a compressed representation of the dialog box.
The TEMPUS Editor
This is an editor! TEMPUS was written by M. Schuelein
in 1987. The only caution I should give concerning the
TEMPUS editor is this: when I received the editor, the
single quote key was not functioning, therefore, I had to
return the disk for corrections. The distributor, Eidersoft
USA Inc (P.O. Box 288, Burgettstown, PA 15021), explained
that the inconvenience was due to the difference between
European and American keyboards. I accept that. If you
purchase TEMPUS and find that the single quote key does not
function, you can send the original back to Eidersoft and
work with your backup disk, using double quotes, until the
corrected disk returns. Or you can try the TMPUSFIX.ARC
file that has been posted to GEnie's ST User's Library as of
December 12, 1988.
TEMPUS requires a setup that involves the execution of
the program PRESERVE.TTP. You can calculate the value that
must be entered into the parameter line, also known as (aka)
the command line, in various ways. But since it is kind of
hokey to have to do this, I just usually use 307200. Then,
if I don't have enough room in TEMPUS for the source on
which I am working, I execute PRESERVE.TTP again and enter a
smaller number on the command line (the value must be some
multiple of 1024). The amount of memory assigned to TEMPUS
is the total amount of memory available minus the value
entered on the command line.
The only other complaints that I have concerning the
TEMPUS editor involves its custom file selector. In the
first place, it displays only 7 disk buttons, enough for
floppies A and B and hard disk partitions C through G; it
should display enough for all possible hard disk partitions.
In the second place, TEMPUS does not allow one a choice
between one's standard file selector and its custom
selector; therefore, I cannot use the Universal Item
Selector; and furthermore, TEMPUS clashes with the operation
of the Universal Item Selector when it is being used as a
desk accessory. These TEMPUS flaws are serious. I hope
that someone will fix them soon, or I shall be forced to do
it myself.
The AssemPro Editor
The first thing I want to mention is the error on pages
7, 52, 119 and 130 of my AssemPro manual, and, unless
someone has corrected the documentation, it will be in your
manual also. On those pages, reference to a menu called
Help is made. This item actually appears as Table on the
menu line. If necessary, make corrections to your
documentation, then execute ASSEMPRO.PRG. With the
assembler window activated, (See pages 5 and 6 of the
AssemPro manual. The assembler window is activated by
moving the mouse arrow to the assembler window and clicking
the left mouse button; the editor window is activated by
moving the arrow to the editor window and clicking.), move
the mouse arrow to the Assembler menu. Now, activate the
following options, and only the following options, by
clicking on them (some may already be activated):
* Optimize backward Bcc's
* Flag undef. variables
* PC-relative
* Original line
When you are done, there should be four check-marks,
one for each of the options selected. Refer to figure 2.4A.
All of the options are discussed in the AssemPro manual, and
I will be discussing each as its usage becomes appropriate.
The PC-relative and Relocatable assembly modes will be
discussed and compared in the next chapter, along with the
pc-relative addressing mode. For now, if any other items
are checked, deactivate them. Next, click on the Search
menu and deactivate the Replace selection if it is checked.
Figure 2.4A. Setting Assembler and Search Options
Next, activate the Editor window and the Editor menu.
Click on the Function keys option; when the dialog box
appears, click on the F8 key button. On the first line
under the label Program :, type LZ; on the line under the
label Explanation : type the following, or something
similar: Delete Line. Then click on the OK box. Refer to
figure 2.3.
Now, activate the File menu and click on the Back-up
copy option. See figure 2.4B. Activate the File menu
option again and click on the Save defaults option. All of
the options you have selected will be saved in the
ASSEMPRO.INF file. If you are working from a RAM disk,
leave AssemPro with the Quit option under the File menu and
copy the ASSEMPRO.INF file to the disk or partition on which
ASSEMPRO.PRG resides. Thereafter, whenever you execute the
ASSEMPRO.PRG, your options will be read in from the INF file
and set automatically.
Figure 2.4B. Setting the File option.
As you receive the AssemPro package, each of the
function keys will most likely be assigned a default
program. Redefining the F8 key to function as a Delete Line
key will hasten your adaptation to the switches from TEMPUS
to AssemPro and back. You can explore other key
reassignments to suit your taste. In my version of
AssemPro, the function keys had been assigned the following
default functions:
F1 = BA = Mark blockstart
F2 = BE = Mark blockend
F3 = BV = Move block
F4 = BK = Copy block
F5 = BL = Delete block
F6 = BD = Unmark block
Functions defined with unprintable characters were
assigned to the F7, F8, F9 and F10 function keys. You can
view those assignments by selecting the Function keys menu
option and by clicking the appropriate function key button
in the dialog box. The program assigned to function key F10
is of special interest, only because it brings up the idea
of converting files prepared by resource construction sets
for use in assembly language programs. I will return to
this subject in an appropriate, later chapter, however, I
want to point out here that the *.I file discussed on page
34 of the AssemPro manual would be a *.H file for some
construction sets. Furthermore, since the layout of such
files varies with the particular construction set used, the
default program assigned to F10 is not compatible with all
resource construction sets.
The Editor's Search Function
AssemPro has two Search functions; one for the editor;
one for the debugger. The editor's Search function,
discussed on page 18 of the manual, is particularly
inconvenient. Using this function requires two, sometimes
three, trips to the Search menu. First you must select the
For what ? option and define the search criteria in the
dialog box (see figure 2.5). Then you must deactivate the
Replace selection if it is activated (Don't forget to do
this if you are only searching, not replacing.) Finally,
you must click on the appropriate Search direction option
(see figure 2.4A). Of course, the Replace function is
equally inconvenient.
The Search/Replace function has one more annoying
attribute. When executing multiple replaces (enabled with
the all button), even though it has successfully completed
any number of them, it will end the event with the Not
found!, Sorry alert box, therefore, you must manually search
the file to determine whether or not the Replace operation
was successful.
Figure 2.5. The Editor's Search/Replace Dialog Box; a
slightly compressed representation.
The First Program
Before we begin to look at the debugger, we should
discuss and prepare a program. I have stated that the
minimum amount of code necessary is a primary requirement of
any example program that I choose to discuss. In order to
fulfill my desire to begin with the rudimentary, it seems
reasonable to attempt to construct the first program from a
single statement. Consider the one statement which must
appear in every assembly language source program, the
instruction end. However necessary it may be to every
program, end is not an instruction for the MC68000
processor; it is an instruction for the assembler. This
instruction produces no executable code. End belongs to a
group of such instructions called assembler directives,
pseudo-ops, or by any other name that differentiates them
from processor instructions.
Furthermore, because every program executed on the ST
must prohibit execution beyond its boundaries, the task of
declaring which instruction or function constitutes the most
rudimentary possible is preempted by necessity. The single
executable algorithm that represents a minimum ST program is
the GEMDOS function number $0, which is one of many system
functions designed to transfer processor control.
Identified by various names such as p_term_old, term or
terminate, this function causes processor control to be
transferred to the program that invoked the program which is
executing p_term_old. Normally, the final goal of every
user program should be the return of control to the
operating system, but there times when control is returned
to another user program instead.
Referring back to the name of GEMDOS function $0, at
times the names given to ST operating system functions may
seem strange. Actually, I am being facetious; the
names will seem to be totally undescriptive of the function
being performed most of the time. I use the names that are
commonly seen in the references so that I can match them up,
but I always try to add my own descriptive label. You will
find that I tend to use labels such as the following:
this_label_attempts_to_completely_describe_the_algorithm_below:.
In addition to serving as the example for a short
tutorial on the AssemPro debugger, the first program will
help me to simultaneously illustrate several concepts
involving the relative execution speed and memory
requirements of similar processor instructions. At the
conclusion of the discussion, the first program, the
concepts and a program that justifies their relevance will
be convincingly juxtapositioned (unless I screw it up in the
telling).
Program 1. A minimum ST program.
; Program Name: PRG_1AP.S
; Version: 1.001
; Assembly Instructions:
; 1. Assemble in AssemPro PC-relative mode and save the assembled
; program with a PRG extension, then save it with a TOS extension.
; 2. Click on the PC-relative option under the Assembler menu to activate
; the Absolute assembly mode. The Absolute mode is active when there
; is no check mark in front of the PC-relative option and no check mark
; in front of the Relocatable option. Change the name of the source
; file to PRG_1AA. Do this by clicking on the File option under the
; File menu and, in the dialog box that appears, backspace over the
; letter P and type an A. Do not save the new source file, but
; assemble it and save the assembled program with a PRG extension,
; then save it with a TOS extension.
; 3. Click on the Relocatable option under the Assembler menu.
; Change the name of the source file to PRG_1AR. Do not save the
; source file, but assemble in the Relocatable mode and save with
; PRG and TOS extensions.
; Program Function:
; This program invokes a single GEMDOS function. That function performs
; three services. It relinquishes processor control, prohibits execution
; beyond the program's upper boundary and removes the program from ram.
; Program Purpose:
; To introduce the smallest possible ST program. Every ST program must
; accomplish at least two functions. It must relinquish processor control,
; and it must prevent execution beyond its boundaries. Processor control
; must be relinquished at some time during the life of each program, if for
; no other reason, then because all programs must eventually finish the
; task for which they were invoked. Without some sort of demarcating code
; within an executing program, the processor will continue to try to execute
; whatever is in memory, until a fatal error eventually occurs.
; GEMDOS function $0, also known as (aka) p_term_old, term or terminate,
; may be invoked to perform these two functions. But, in addition, this
; function also removes the program from memory.
; When GEMDOS $0 is invoked, processor control is returned to the
; agent that initiated the execution of the program that is now invoking
; GEMDOS $0. That agent may be the Desktop or some other program.
; An additional purpose of this exercise is to compare the sizes of
; the code files produced by the three assembly modes. Furthermore, if
; you are able to carefully observe the length of time it takes to execute
; each type of program, where the mode of assembly indicates "type", you
; will see that the time required to execute the Relocatable types is longer
; than the time required to execute the other types.
terminate: ; My descriptive label.
move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
trap #1 ; GEMDOS call.
end ; Assembler pseudo-op.
Upon invocation, p_term_old performs three functions.
It prohibits execution beyond the boundaries of the program;
it removes this program from ram; and it returns processor
control back to the program that invoked this one, which in
this case is the system program Desktop. Without some sort
of termination code within an executing program, the
processor would continue to execute instructions until a
fatal error occurred. I use the phrase termination code to
refer to code which sets an execution boundary, I do not use
it to connote execution termination. I make this
distinction because both phenomena coincidentally occur when
the p_term_old function is used, however, these phenomena
are not mutually inclusive.
You should type program 1 into the AssemPro editor,
with or without the comments, and assemble it at this time.
Before you type the file into the the editor, name it by
clicking on the File : option under the File menu, then by
entering PRG_1AP on the parameter line of the dialog box
which subsequently appears. If you have not yet read the
portion of the manual that describes assembling, the
following few sentences should contain enough information to
insure success. Assemble PRG_1AP by activating the
assembler window, then the Assembler menu, and finally, by
clicking on the Assembling selection. Now, save the source
file by clicking on the Save option under the File menu, and
by clicking on the S button in the dialog box after it
appears. Next, save the object file by clicking on the TOS
button, after you activate the dialog box a second time.
Unfortunately, however routine it may be to save both
files, the saves must be done individually. Finally, save
the object file again, but, this time, click on the PRG
button, after you activate the dialog box a third time. For
each save that you perform, the filename extension button
that you choose tells AssemPro to affix that extension to
the basic filename before writing the file to disk. Figure
2.6 is a picture of the Save dialog box with the name of the
program typed therein.
Figure 2.6. The dialog box used to save files.
Click on the File : option under the File menu and change
the file name to PRG_1AA. There is no reason to save this
source file. Click on the PC-relative option under the
Assembler menu to active the Absolute assembly mode; the
Absolute mode is active when there is neither a check in
front of the PC-relative option, nor in front of the
Relocatable option. Assemble this new source and save the
object code with TOS and PRG extensions. Finally, change
the file name to PRG_1AR and click on the Relocatable option
under the Assembler menu to activate the Relocatable
assembly mode. Do not save the source file, but assemble
and save the object code with TOS and PRG extensions.
Comparisons between the size of each of the six assembled
programs and their execution speeds will be accomplished
shortly.
Although it may not be obvious, there are algorithmic
choices to be made for even so elementary a program as
PRG_1AP. Only two requirements are necessary for successful
execution of the function. The number of the function, $0,
must be pushed onto the stack, and a trap #1 call must be
invoked. No choices are available as far as the trap
instruction is concerned (at this elementary stage). It
must appear as shown. However, there is more than one way
to push the $0 onto the stack. Table 2.1 lists three
algorithms that accomplish the task. The number of clock
periods and memory requirements for each are indicated. For
this table, the clock periods were calculated from
information in appropriate charts provided in the Motorola
M68000 Programmer's Reference Manual, fifth edition.
Table 2.1. Three ways to store 0 on the stack. Comparison
of the execution speed and memory requirements of three ways
to push zero onto the stack. Although -(sp) is used as the
destination operand in the table, the data applies to any
-(An).
Instruction execution time is measured in clock
periods; memory requirement is measured in bytes. The
method used to calculate the execution times as they are
shown in table 2.1 is discussed in a later chapter. In
addition, I shall present programs that accomplish the task,
in the sense that relative execution speeds are calculated,
with much less bother and with precision that is tied to the
ST's operating system and internal hardware.
The data in the table illustrate the classic speed
versus memory requirement tradeoff so often discussed in
computer literature. To wit: if we are willing to sacrifice
2 more bytes of memory, we can take advantage of the faster
execution speed exhibited by either of the second or the
third algorithm. Of course, in PRG_1AP there is only a
single incident of the algorithm involved, therefore, one
might think that it doesn't matter which of the three
algorithms is used. I insist that the choice should be made
deliberately. If a conscious effort is being made to
sacrifice speed for conservation of memory, then the first
algorithm should be chosen, otherwise one of the others
should be used, because I prefer that the default
consideration always be speed. Let me illustrate how much
just one casual choice costs in overall machine performance.
The ST has an 8mhz clock. Each clock period is 1.25 X
10-7 seconds. The 14 clock period instruction takes 14 X
1.25 X 10-7 = 1.75 X 10-6 seconds = 1.75 microseconds.
Neglecting any extraneous delays, in one second, the ST can
execute 1,000,000 / 1.75 = 571,428 instructions of this
type. An instruction that requires 12 clock periods takes
1.5 microseconds. Neglecting extraneous delays again, in
one second, the ST can execute 666,666 instructions of this
type. By choosing the instruction that requires 14 clock
periods, we lose an execution capability of 666,666 -
571,428 = 95,238 instructions per second.
When you start doing things such as moving zeroes into
32000 bytes of memory to clear a video screen or two every
so often in a program, such loses begin to pile up and
performance becomes unsatisfactory. The insidious thing
about choosing unconsciously is that it becomes a habit. By
habitually using slower, albeit more familiar, instructions
we risk using them as the worst choice for an algorithm in
which we are trying to emphasize speed.
Executing Program 1 From the Desktop
Exit AssemPro by clicking on the Quit option under the
File menu. Then execute PRG_1AA.PRG from the desktop. The
operating system will briefy display a gray screen (assuming
a monochrome monitor) with the program name at the top and a
busy bee cursor. Now, execute PRG_1AA.TOS. The operating
system will briefly display a blank white screen with a
black, blinking cursor in the upper left hand corner of the
screen. The difference in screens occurs because programs
with a PRG extension are expected to use windows, while
those with a TOS extension are expected to be keyboard
driven.
The quickest way to understand the difference is to
change the extension of a program that uses windows, such as
a word processor, from PRG to TOS and execute it. There
will be no mouse activity, so you will not be able to make
any selections. The only way out will be to reset the
computer with the reset switch. On the other hand, if you
alter the suffix of a TOS program to PRG and execute, the
mouse pointer will be present, but the cursor will be
absent. And, of course, the screen will not be clear.
The PRG_1AA programs were assembled using AssemPro's
Absolute mode. Execute PRG_1AP.PRG and PRG_1AP.TOS from the
desktop also. These programs were assembled using
AssemPro's PC-relative mode. The behavior of these programs
will be identical to that of the programs which were
assembled using the Absolute mode.
Now execute PRG_1AR.PRG and PRG_1AR.TOS. The same
types of screens will appear, but they will remain longer
than they did when the PRG_1AA and PRG_1AP programs were
executed. The time differential, as well as the reason it
exists, will be explored in the next chapter. For now, I
just want you to observe that the difference is noticeable.
To observe another difference in the way the operating
system processes these programs, click on PRG_1AA.PRG, then
click on the Show Info option under the File menu. Note the
file size in bytes. Do this for each of the other five
files. You will see that the Absolute and PC-relative files
consume 34 bytes of disk space, while the Relocatable files
consume 42 bytes. To my knowledge, the only reference that
explains the 8-bytes difference (two longwords) is the
AssemPro manual, on page 80. These 8 bytes contain
information for the relocator. However, in the next chapter
we will see that this 8-byte differential is just the
minimum size difference incurred by a program assembled in
the Relocatable mode.
An Executable File's Disk Image
Each executable ST file has a distinctive header which
identifies it as such. An executable file's header (and the
entire file) can be examined using a utility such as The
Disk Doctor, copyright 1985, 1986 by Daniel Matejka, or File
Viewer, by Richard Smereka, COMPUTE!'s Atari ST, August
1987. A disk image for the file with the PRG extension from
each of the three assembled sets is shown in file 2.1. The
file header extends from word $0 of the file through word
$1B. Of particular interest during this discussion is the
word at location $1A. If this word is $0000, then the file
has been assembled in AssemPro's Relocatable mode, otherwise
the file has been assembled in one of the other two modes.
When a program is loaded into ram, the ST's relocator will
adjust addresses as necessary, if this word is $0000, as
explained on page 79 of the AssemPro manual. It is this
extra step which is partly responsible for the increase in
execution time that is experienced when a Relocatable
program is executed. If the overall execution time is
divided into two parts, one of which is a loading time
component, then the relocation step clearly adds to this
component.
File 2.1. A hex dump of programs PRG_1AA.PRG, PRG_1AP.PRG
and PRG_1AR.PRG. The disk images of the three executable
files are shown.
PRG_1AY.DMP
Hex dump of programs PRG_1AA.PRG, PRG_1AP.PRG and PRG_1AR.PRG.
The first word of each of the three files is a branch instruction.
The branch is to the location that is the sum of the 8-bit displacement,
1A, plus 2: that is, to location 1C. In each file that location is marked
as "start of text segment".
The word immediately preceding the "start of text segment" marks the
program as relocatable if the word is $0000. In files that are assembled in
AssemPro's Absolute and PC-relative modes, this word will be $FFFF.
The longword beginning at location $02 is the size of the text segment;
for each of the three programs the size of the text segment is 6 bytes. The
longword beginning at location $06 is the size of the data segment, which is
zero for the three programs. The longword beginning at location $0A is the
size of the BSS segment, which is zero for the three programs.
Note that the size of the relocatable program, PRG_1AR.PRG, is 42 bytes,
eight bytes longer than the size of the other two programs.
ADDRESSES CONTENTS OF FILE PRG_1AA.PRG
FROM TO
00000 00000F 601A 0000 0006 0000 0000 0000 0000 0000
00010 00001F 0000 0000 0000 0000 0000 FFFF 3F3C 0000
00020 000021 4E41 ^start of text
segment
CONTENTS OF FILE PRG_1AP.PRG
00000 00000F 601A 0000 0006 0000 0000 0000 0000 0000
00010 00001F 0000 0000 0000 0000 0000 FFFF 3F3C 0000
00020 000021 4E41 ^start of text
segment
CONTENTS OF FILE PRG_1AR.PRG
00000 00000F 601A 0000 0006 0000 0000 0000 0000 0000
00010 00001F 0000 0000 0000 0000 0000 0000 3F3C 0000
00020 000029 4E41 0000 0000 0000 0000 ^start of text
segment
The AssemPro Debugger
To prepare for this discussion, load PRG_1AP.S in the
editor by clicking on the Load option under the File menu,
then by clicking on the S button when the dialog box
appears. The file selector will appear after you click the
S button. After the source is loaded into the editor,
assemble it using either the PC-relative mode or the
Relocatable mode.
Now, activate the debugger by clicking on the Debugger
option under the Debugger menu. When the debugger screen
appears, if the program was assembled with the Relocatable
option checked, click on the relocate button. You will be
able to see the two statements of the program in the output
field of the debugger window. The output field is
displaying memory in normal disassembled mnemonic format.
See figure 2.7. In this format, memory addresses are
followed by their content in both hexadecimal digits and,
where possible, the assembly language translation of those
digits. In this figure, and in all future figures and
discussions, the memory addresses that you will see in your
debugger window will most likely never match those of mine,
except when we are both viewing locations assigned to the
system. That is so because the addresses we see depend on
the desk accessories and other load and stay resident
programs we have previously loaded into ram.
Figure 2.7. Normal disassembled mnemonic format.
Note the appearance of the program statements, then
click on the symbolic button. The output field now displays
the TERMINATE: label also. Refer to figure 2.8. This
display, called the symbolic disassembled mnemonic format,
eliminates the hexadecimal representation of instructions
after the addresses.
Figure 2.8. Symbolic disassembled mnemonic format.
Displaying the source text labels in the debugger
output field is one of AssemPro's most prominent features.
You enable this option by doing two things, if the program
is assembled in PC-relative mode, three things if it is
assembled in Relocatable mode. First you must load the
source code into the editor and assemble it (of course, if
you create the source code as new text, loading it is not
necessary). If the program is assembled in the Relocatable
mode, you must click on the relocate button in the debugger
window. Then you must click on the symbolic button in the
debugger window.
The dark line that you see in the output field is the
program counter cursor; therefore, it indicates the next
instruction to be executed, if execution is to take place.
The address that you see in the output field for the first
instruction of the program will depend on the exact
configuration of your machine. In any case, the address
that you see for the second instruction will be the address
of the first instruction plus four. The memory occupied by
the first instruction is the address of the second
instruction minus the address of the first instruction, that
is, four bytes. You should be able to deduce the memory
occupied by the second instruction; it is two bytes.
Click on the disassembled button; the state of the
symbolic button is of no consequence. The output field will
display memory in the hexadecimal/ASCII dump format. See
figure 2.9. In this format, you can view the content of
memory in hexadecimal digits, and, if any sequence of those
digits forms a valid ASCII string, you will be able to
observe that string. The most significant feature available
via this format (aside from viewing data in ASCII) is the
ability to directly alter the content of memory. Refer to
page 85 of the AssemPro manual. This feature will be
explored, in detail, in a later chapter.
Figure 2.9. Hexadecimal/ASCII Dump Format. In this mode,
the symbolic button has no effect of the display.
Click on the disassembled button to return to a
mnemonic formatted output field, then click on the Execute
program button; it is located just above the => erase button
(called => delete in the AssemPro manual), which is located
just above the relocate button. Clicking the Execute
program button will not execute the program. In the dialog
box that appears, click on the OK button. Refer to figure
2.10. You will be presented with the file selector box;
choose PRG_1AP.PRG or PRG_1AR.PRG, one of the assembled
files previously saved.
A Special Note: To remove a program from memory,
after it has been loaded with the Execute program
button, click on the => erase button while pressing
the Control key on the keyboard.
Figure 2.10. The Execute program dialog box; a slightly
compressed representation.
AssemPro will load in the object code for the program
and you will be able to see the program's two instructions
in the output field again. Activate the symbolic button, if
it has been deactivated; in this mode, the label TERMINATE:
can't be seen in the output field. When you load a program
into memory using the Execute program button, AssemPro knows
nothing about labels in the source code. It is only when a
program has just been assembled that such information is
available to the debugger.
There are two memory configuration differences existing
between this object code and that which you had been
viewing; this object code has a basepage (See pages 145 and
146 of the Internals book.), the other did not. Any program
that is in debugger memory because it has just been
assembled does not have a basepage. The significance of the
distinction is this: if the basepage is not present, then
you will not be able to execute instructions which depend on
information stored in the basepage.
In addition, this program has claimed all of available
memory for its own use. You can verify this by clicking on
the box in the upper left corner of the debugger screen to
get back to the Assemble/Edit screens. There you will see
that there are only 50 bytes of available memory. The rest
of it is being consumed by PRG_1AP. These important
distinctions will be explored later, when I discuss the
function which returns excess memory back to the operating
system.
Executing the Program
As you can see, there are many other debugger options
present. We will cover them all in time. For now, you can
simply execute the program by clicking on the Run program
option. After the program has executed, address $000000
appears in the output field as the next instruction to be
executed. See figure 2.11. Another alteration of interest
has also taken place. Observe the ten status register bits
that are shown as buttons under the label Statusregister,
just to the left of the second output field instruction
line.
Figure 2.11. Output field and status register after
executing program 1.
There you will see that three of the status register
bits have been set (the S bit and two bits of the interrupt
mask), apparent because of their dark color (reverse video).
The new state of these bits will not interfere with the
repeated execution of this program, however, their state
could be a matter of importance, of which we are unaware, to
AssemPro. Furthermore, such a change in the status register
bits could have drastic effects on a program that utilized
the state of those bits. Therefore, it is a good idea to
put things back in order whenever we can, so reset those
bits by clicking on them. The reset condition of a bit is
indicated by a white color in the bit button.
In addition to the changes in the microprocessor status
register caused by a program's execution, there are
alterations in data register and address register contents.
Figure 2.12 illustrates typical alterations. You can look
at the register field of your debugger screen to view
similar changes.
Figure 2.12. Changes in register content after program
execution.
Now look to the top right corner of the debugger
screen. There you see the label Program start :, followed
by a hexadecimal number. That number is the starting memory
address of the your program. At the moment, the PC (program
counter) cursor is located at memory location $000000. If
you were to click on the Run program button now, you would
be greeted by a warning box, telling you that a Bus error
has occurred at address 000000. When this type of error
occurs, it is sometimes possible to get back to the debugger
by clicking on the Debugger button in the warning box.
Sometimes you will simply get another warning box, or the
system may freeze. The only way to be sure that everything
is as it should be after a Bus error is to reset the machine
by pressing the reset button, holding it for at least 10
seconds.
To execute the program again, you must, as step one of
two steps, get the first executable instruction of your
program back in the debugger output field (This is not
always the first instruction of the program.). To do this,
you rapidly double click on the from address button in the
lower right hand corner of the debugger screen. If you
don't click rapidly enough, the output field will not be
altered; instead, an editable line will open at the Change
register field, located at the very bottom of the debugger
window.
In this particular case, you would see *=$___________.
When the $ is present, you can enter a hexadecimal address.
The address you would enter now, if the editable line has
indeed appeared, is that which is labeled Program start at
the top of the screen. Never leave a space between the $
and the first digit of the address. Conclude the dialog by
pressing the Return key on the keyboard.
When the correct address appears in the output field,
perform step two, which is placing the PC cursor at the
appropriate instruction. Do this by moving the mouse
pointer to the instruction line, then hold down the Control
key on the keyboard, while clicking the left mouse button.
Now you may execute the program again, by clicking on the
Run program button.
Referring to the from address button and the Change
register editable parameter line again, if the program in
debugger memory is there because it was recently assembled,
and if it has been relocated as described previously
(required only if the program was assembled in the
relocatable mode), then you can go to any labeled line by
backspacing over the $ on the parameter line, and by typing
the label on the line. When done correctly, the line will
read *=label________. The $ is permitted and necessary only
if it is followed by a hexadecimal number.
As an example of the proper use of this function, if
you have just assembled program 1 and are now in the
debugger mode; and, if you have just executed the program,
you can get back to the program's start address by clicking
once on the from address button, then by backspacing over
the $ on the change register parameter line, and, finally,
by typing the name of the label terminate on the line. When
you press the return key, the first line of program 1 will
appear as the first line in the output field.
Loading Programs With Other Extensions
The Execute program function, by default, looks for
programs with the PRG extension. To load a program that has
any other extension, you must edit the path specification in
the file selector when it appears. Loading programs with a
TOS extension incurs no special considerations; but AssemPro
does not handle TTP programs correctly.
Recall the Execute program dialog box shown in figure
2.10. The Command line evident in that figure invites TTP
parameters that are to be stored at the program's basepage
address plus $80. Although AssemPro accepts the data typed
on the command line, it does not store the data in an ST
specific format. Figure 2.13 illustrates the manner in
which the operating system stores TTP command line data.
The program start address for the program which
produced the results shown in the figure was $25956. The
basepage address was $25956 - $100 = $25856. The command
line address was $25856 + $80 = $258D6, as shown in figure
2.13. There you see that the first byte of the command
line, $0D, is the number of characters typed on the TTP
program's input parameter line (command line). Following
that you see the ASCII codes for the 11 characters,
PRG_1AP.TOS, which were typed as input. Then you see the
code for a carriage return, 0D, which is placed at the end
of the command line input, whether the mouse or the Return
key was used to terminate the dialog.
Figure 2.13. Command line data stored by the ST operating
system.
In figure 2.14 you can see why the manner in which
AssemPro stores the input parameters creates two major
problems. AssemPro does not store the input parameter
character count; therefore, any program that processes the
command line in a loop which uses that count is doomed.
And, to compound the problem, AssemPro stores the first
character at basepage address plus $80 instead of at
basepage address plus $81; therefore, any program which
processes the command line by looking for the first
character at basepage plus $81 is also doomed. Although not
as critical, AssemPro does not store a carriage return at
the end of the command line string.
Figure 2.14. Command line data stored by AssemPro.
My solution to the problem is this: when the Execute
program dialog box appears, type a leading space on the
command line, then follow with the input parameters. This
will place the first character of the input at basepage
address plus $81, where it belongs. Remember, the stored
command line parameters (as they are placed in the basepage
by the operating system) are a mixture composed of a one-
byte binary number and ASCII characters, so you can't just
type in a number in place of that leading space. After the
TTP program has been loaded by AssemPro, manually replace
the leading space with the ASCII character count. To do
that, click on the from address bar at the bottom of the
debugger screen; and, on the parameter line which appears,
type the address of the command line, which is basepage
address + $80 or program start address - $80. When you
press the Return key, the command line address will appear
as the first line in the output field window.
Click on the disassembled bar located just above the
from address bar at the bottom of the screen. When the
hexadecimal characters appear in the output field, the first
two digits will be the ASCII code for the leading space that
you typed on the command line. Using the mouse, move the
cursor to the first digit, which is 2, and type the number
of characters in the command line. The number you type must
be in hexadecimal and you must include a leading 0 if the
number you type is less than hexadecimal $10.
Referring to that dialog box again, the load option
should always be selected (reverse video). The parameter
line labeled Environment: refers to something called an
environment string. Just so you'll know what it is, I'll
tell you. It is used to pass an ordinary string as a
parameter to be stored at offset $2C from the start of the
program's basepage. The program can access the string at
that location. I will show you how this can be done in a
later program. The address of a block of environmental
strings can be passed, on the stack, to a spawned process,
using the GEMDOS $4B (p_exec) function.
A Program With User Interaction
Let's add something to PRG_1AP that will prevent the
program from returning control to the operating system until
we permit it to do so. For now, I am relying on the GEMDOS
(trap #1) functions, even though these are the farthest from
the hardware, because they are the least complex. As a
matter of fact, none of the operating system functions
should be considered taboo simply because they are not the
fastest. The important thing is to choose deliberately,
with a knowledge of which is which and what does what, to
whom, when.
All of the GEMDOS functions, as well as the BIOS and
XBIOS functions are discussed in the Abacus Internals book;
and, in general, the functions are adequately described;
therefore, I shall not have much to say about most of them.
However, there are some errors present in some descriptions.
Charles F. Johnson has written an article, Errors in Abacus,
ST-LOG, June, 1988, describing some of the more critical
errors in the Internals book, as they exist in revision two.
Some of these errors were corrected in revision three. I
suggest that you obtain a copy of the article mentioned
above to use as a reference for functions that I won't have
a reason to use.
Program 2. Halting program execution.
; Program Name: PRG_1BP.S
; Assembly Instructions:
; Assemble in AssemPro PC-relative mode and save the assembled program
; with a PRG extension, then save it with a TOS extension.
; Program Function:
; This program waits until a key is pressed on the keyboard. When
; that event occurs, it returns control to the operating system.
; NOTE: This program cannot be executed repeatedly in the debugger unless
; the S bit of the status register is reset after each execution.
; If the S bit is not reset as stated, the system will freeze, and
; must be reset with the computer's reset switch.
wait_for_keypress:
move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
trap #1 ; GEMDOS call.
addq.l #2, sp ; Reposition stack pointer at top of stack.
terminate: ; My descriptive label.
move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
trap #1 ; GEMDOS call.
end ; Assembler pseudo-op.
After you have installed PRG_1BP in the editor,
assemble it, save the source with an S suffix, save the
object code with both a PRG and a TOS suffix, then activate
the debugger. On the debugger screen, click the symbolic
button. You should see the wait_for_keypress label as the
first instruction in the debugger output field; it should be
shown in reverse video, indicating that the PC counter
contains the address of this instruction.
Observe the condition of the status register bits; they
are all reset. Also, notice that the content of all
registers shown in the register field is zero (except for
register A7). These are the initial conditions of the
registers at the start of the program run. As we shall soon
discover, these initial conditions are forced by AssemPro,
and they are not identical to those imposed by the operating
system when a program is loaded from the desktop. You
should notice that the content of A7 matches neither that of
the SSP nor that of the USP. This should seem unreasonable,
and it is. The AssemPro manual provides an explanation, of
sorts, on p.90. This discrepancy will not interfere with
our work.
Click on the Run program button to execute the program.
There will be no change in the AssemPro screen, however, the
mouse pointer will disappear; an indication that the program
is running. In fact, the program will be waiting for an
event to occur; that being the press of a keyboard key.
Press the Return key. As you have come to expect, address
000000 is now the topmost line in the debugger output field,
and the program counter now contains that address. Look at
the status register and notice the same alterations that you
observed after executing program 1. Also, look at the
register output field and notice that the content of many
registers have been changed. Our initial conditions have
been altered by AssemPro and the operating system.
By the way, the L that precedes each register label
indicates that you are viewing long word data. Get in the
habit of noticing that prefix. It can be set to B, W or L,
and what you see as a register's content varies greatly
depending on that prefix. Furthermore, the lines at which
register contents are displayed may be altered to display
the contents of memory locations or their addresses.
Without resetting anything, get the program start
address back in the output window, move the program counter
cursor to that line (Move the mouse pointer to the line,
hold down the Control key and press the left mouse button.),
then execute the program again. Things seem to be as they
were during the first execution. But they are not. Press
the Return key and notice the lack of response. The system
is frozen because the alteration of the S bit is of
consequence to the proper execution of this program.
Furthermore, it is safe to assume that all initial
conditions must be reset between repeated executions of any
program in the debugger. You must manually reset the
computer.
Load in the source code for program 2 again, assemble
it and go to the debugger. Click the symbolic button, as is
usual, then execute the program. This time, reset all
initial conditions before the second execution. Click on
the dark status register bits to set them to zero. For each
register (except A7) shown in the register window, with a
nonzero content do the following:
1. Click on the Change register bar at the bottom of
the debugger screen.
2. On the parameter line which appears, type the
register label, an equal sign and a zero.
example: d0=0 or D0=0
There must be no spaces between the characters.
3. Press the Return key.
Execute the program and press the Return key. The
execution should be normal. Reset all initial conditions
again, then click on the Save screen button. With the
content of PC as it should be, execute the program. Now,
you see that a blank white screen appears. The program is
still waiting for a keypress, but AssemPro has presented a
screen for program usage. Terminate the execution by
pressing the Return key.
Once again, reset all initial conditions, but leave the
Save screen feature checked. Features such as this and
symbolic are not included in the term initial conditions.
Set a breakpoint at the TERMINATE label. To do this, move
the mouse pointer to the Breakpoint button. Press the left
mouse button and, while holding it pressed, move the pointer
to the instruction line containing the label, before
releasing the mouse button. You should see the word
BREAKPOINT appear on the line just to the right of the
label. Refer to figure 2.15. Run the program and press the
Return key. Execution will stop at the breakpoint, apparent
by the instruction line being highlighted by the program
counter cursor. Notice that the S bit has not yet been set
and notice that only the D0 and A0 registers have been
altered (ignoring A7, as we should). Refer to the
compressed register field, also shown in figure 2.15.
Figure 2.15A. Installing a breakpoint to halt execution.
Figure 2.15B. Content of Registers at the breakpoint.
These are the only two registers we expect to be
altered, according to the Internals book p.106. We must be
cognizant of this type of information so that we may know in
which registers data is safe during operating system
function calls. Remember, we have learned (via the
references and an experiment) that all registers but D0 and
A0 are safe during GEMDOS (trap #1) calls; this example
provides no information concerning register alteration
during other calls.
Of course, we have not seen conclusive proof that all
GEMDOS calls behave identically, and we will be looking out
for deviations from the norm. The aforementioned page also
informs us that any values returned by the function call
will be in D0. Looking at the register field, you should
see the ASCII code for a carriage return, the hexadecimal
character D, in the lower word of that register. For this
example, we are not interested in the content of register
A0, nor are we interested in the upper word of D0. But we
do need to remember that the character received from the
keyboard is returned in the lower byte of the lower word of
D0, because there will be times when we want to process that
input.
Remove the breakpoint. To do this, move the mouse
pointer to the line containing the breakpoint, press the
left mouse button, and, while holding the button down, move
the pointer to the Breakpoint button, then release the mouse
button. Click on the Run program button to conclude the
execution.
Exit AssemPro without saving the object code. Back at
the desktop, execute PRG_1BP.PRG. You will see the PRG type
screen, as you did when you executed program 1; but, this
time, the program will be waiting for the keyboard event.
You will notice that you can move the busy bee cursor to any
position on the screen without affecting anything.
Terminate the program by pressing any key.
Now, execute PRG_1BP.TOS. You will see the clear
screen with the blinking cursor in the upper left hand
corner of the screen. Terminate the program at any time, by
pressing any key. Note this please: there are times when
you will exit from the debugger after doing many strange
things as far as the computer is concerned. Occasionally,
after you are back at the desktop, you will execute some
program that you know is bug-free (If there is such a
thing.), yet the program will bomb. This happens. You just
have to reset the machine and try again, that's all.
Writing to an AssemPro Screen
When a program is to write text or graphics to a screen
without destroying the debugger screen, the debugger option
Save screen must be selected before the program is loaded
into the debugger; or before a just assembled Relocatable
program is relocated; or just after the function which
returns excess memory to the operating system has been
executed. Unfortunately, even though the option is
selected, the very first line of output usually overwrites a
critical portion of the debugger screen. The location of
overwrite is random unless the program being executed
actually positions the cursor at some selected position on
the screen. The problem is introduced by program 3.
I am going to assume that, unless I say otherwise, you
will know that, as each program is introduced, you must get
it into the editor, assemble it and save everything in the
manner described for the programs previously introduced,
unless the new program instructs you to do something
different. Further, I will assume that you will go to the
debugger screen and select Save screen, relocate (if the
program was assembled in Relocatable mode) and symbolic. In
addition, I trust you to reset initial conditions between
repeated executions.
You should realize that you will receive the warning
message about saving object code every time you load a
source file and assemble, then attempt to quit AssemPro
without saving the assembled file. Having previously saved
the object code, you will be able to simply click on the NO
button.
Program 3. A program that contaminates the debugger screen.
; Program Name: PRG_1CP.S
; Version: 1.002
; Assembly Instructions:
; Assemble in AssemPro PC-relative mode and save the assembled program
; with PRG and TOS extensions.
; Program Function:
; Prints a string to the video screen. If the program is executed
; within the AssemPro debugger, the string should overwrite the debugger
; screen, even if the AssemPro "Save Screen" option has been selected. To
; verify this phenomenon, power the computer down, then back up. Execute
; ASSEMPRO.PRG, then load or type this program into the editor. Assemble,
; then activate the debugger. Select "Save Screen" and "symbolic". Finally,
; select "Run Program". The debugger screen will be overwritten in an
; unpredictable area.
; Watch the debugger screen carefully, during execution, so that you
; will be able to see the writeover occur.
; Depending on the location of the overwritten area, AssemPro may
; redraw a portion of its screen after the event.
; The overwrite is likely to occur only once, the first time a program
; which writes to the screen is executed within the debugger.
; After the string has been written, the program waits until a key
; is pressed on the keyboard. When that event occurs, it returns control
; to the debugger.
; Note - We have not defined a program stack. We are using the default
; system stack located at $4DB8, according to Internals page 276.
print_string:
pea string ; Push address of the string onto stack.
move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
trap #1 ; GEMDOS call
addq.l #6, sp ; Reset stack pointer to top of stack.
wait_for_keypress:
move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
trap #1 ; GEMDOS call.
addq.l #2, sp ; Reposition stack pointer at top of stack.
terminate: ; My descriptive label.
move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
trap #1 ; GEMDOS call.
string: dc.b 'This string will overwrite the AssemPro debugger screen.'
dc.b $D,$A,0 ; The string is continued on this line. Here, we
; declare a carriage return and linefeed, then
; terminate the entire string with a NULL = 0.
end ; Assembler pseudo-op.
When you execute this program in the debugger, the
program screen will appear, but the line which was printed
will not be on that screen. Terminate execution and go back
to the AssemPro screen by pressing the Esc key on the
keyboard. As the debugger screen appears you will see, just
before the output fields of the debugger screen are redrawn,
that the line has overwritten the debugger screen. Any
portion of the line that had overwritten the output fields
of the debugger screen will be cleared by the redraw. On my
screen the line was written at a location that caused a
portion of the Single step button to be overwritten. See
figure 2.16. Execute the program a second time; the
sentence will be written on the program screen. Use the Esc
key to terminate execution and get back to the debugger
screen; it will be as it was previously.
Figure 2.16. Text improperly written on the debugger
screen.
Now deselect the Save screen option and execute the
program again. Probably, nothing will be written on the
debugger screen this time. Press any key to terminate
execution. Select Save screen and execute again. You
should see the clear program screen, and when you press Esc
to terminate and go back to the debugger screen, you will
probably see that the program's sentence has overwritten the
debugger screen in a different location. By the way, the
Esc key is the key which permits flipping between the
program's screen and the debugger screen.
Go to the desktop and execute PRG_1CP.PRG several
times. Then execute PRG_1CP.TOS several times. You should
notice that each time the program with the TOS suffix is
executed, the operating system prints the sentence on the
first line of the screen. This is because the cursor is
initialized to the upper left corner of screen when a
program has a TOS suffix. When a program with a PRG suffix
is executed, the sentence is printed with the cursor
positioned wherever the previous program execution happened
to leave it. Occasionally, you may not even see the results
of GEMDOS function $9 when the program has a PRG suffix,
unless the program initializes the cursor position.
If you execute the TOS program, then immediately
execute the PRG program, the PRG program's output will
appear on the first line of the screen. Do that a few times
so that you understand what I mean. Now you see that
execution of a program can be influenced by a previous
program execution. To drive this point deeper, execute the
PRG program several times. Notice that, with each
execution, the sentence is printed one line lower on the
screen. Via these experiments, I want you to understand
that the way in which the operating system processes
programs varies according to the suffix attached to the
program name. As a programmer, you must know when you can
relinquish control to the system and when you must provide
control of even the most basic of functions within your
program.
The next program sends a newline (carriage return +
line feed) to the screen as an initialization character. I
believe that this eliminates the debugger screen overwrite
problem because the character forces AssemPro to switch
logical screen bases. Refer to page 168 of the Internals
book: XBIOS function 3. You should assemble this program
and execute it from the debugger to confirm that it
functions as I have specified.
Program 4. Eliminating debugger screen overwrite.
; Program Name: PRG_1DP.S
; Version: 1.002
; Assembly Instructions:
; Assemble in PC-relative mode and save with PRG and TOS extensions.
; Program Function:
; Prints a newline (combination of a carriage return and a linefeed)
; to the video screen before it prints a string. This added feature
; prevents the string from overwriting the AssemPro debugger screen, if
; the Save Screen debugger option is chosen before the assembled program
; is relocated with the Relocate option or before it is loaded with the
; Execute Program option.
; After the string has been written, the program waits until a key is
; pressed on the keyboard. When that event occurs, it returns control to
; AssemPro.
; Note - We have not defined a program stack. We are using the default
; system stack located at $4DB8, according to Internals p. 276.
print_newline: ; Prints a carriage return and linefeed.
pea newline ; Push address of string onto stack.
move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
trap #1
addq.l #6, sp
print_string:
pea string ; Push address of the string onto stack.
move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
trap #1
addq.l #6, sp
wait_for_keypress:
move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
trap #1
addq.l #2, sp
terminate:
move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
trap #1
data
newline: dc.b $D,$A,0 ; All strings must be NULL terminated because the
; function we are used to print them requires it.
string: dc.b 'This string will not overwrite the AssemPro debugger screen.'
dc.b $D,$A,0 ; The string is continued on this line. Here, we
; declare a carriage return, linefeed and terminate
; the entire string with a NULL = 0.
end
Declaring Data Within the Text Area of a Program
Notice that the all of the data of program 4 has been
declared in a more conventional location within the program.
There is no reason to declare the data at the end of the
program, other than that programmers find it convenient to
do so. There are times, however, when it is more convenient
to declare data within a program's text area. Program 5
illustrates a method of doing that.
Program 5. Declaring data within a text area.
; Program Name: PRG_1EP.S
; Version: 1.002
; Assembly Instructions:
; Assemble in AssemPro PC-relative mode and save the assembled program
; program with PRG and TOS extensions.
; Program Function:
; Illustrates a method of program organization which places declarations
; at the beginning of a program.
; Note - We have not defined a program stack. We are using the default
; system stack located at $4DB8, according to Internals p. 276.
bra.s print_string ; Jump over the declarations below.
string: dc.b 'This string will not overwrite the AssemPro debugger screen.'
dc.b $D,$A,0 ; The string is continued on this line. Here, we
; declare a carriage return and linefeed, then
; terminate the entire string with a NULL = 0.
newline: dc.b $D,$A,0 ; All strings must be NULL terminated because the
; function we are used to print them requires it.
align ; This is the assembler pseudo-op that forces the next
; instruction to start at a word boundary. It is necessary
; because we have declared byte data above and we don't care to
; count the bytes to confirm that there is an even number of
; byte-size data.
text ; This is an assembler pseudo-op. dc.b is a pseudo-op also. We
; can define the string above the source code if we put this
; pseudo-op between the declaration and the text of the source.
; Of course, since the processor will attempt to execute the
; first thing it sees, whether it be an instruction or data, we
; must jump over the data. You don't jump when the data is
; actually an instruction you want to execute, such as when an
; a-line (illegal) instruction is declared as data.
print_string:
pea newline ; Prints a carriage return and linefeed.
move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
trap #1
addq.l #6, sp
pea string ; Push address of the string onto stack.
move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
trap #1 ; GEMDOS call
addq.l #6, sp ; Reset stack pointer to top of stack.
wait_for_keypress:
move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
trap #1 ; GEMDOS call.
addq.l #2, sp ; Reposition stack pointer at top of stack.
terminate: ; My descriptive label.
move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
trap #1 ; GEMDOS call.
end ; Assembler pseudo-op.
Don't execute the program yet, but when you do, the
first thing you will notice is that, although it does output
correctly to the program's screen, the sentence is not
likely to appear on the top line of the screen. This is
true because AssemPro does not initialize the cursor on that
screen; at least, not for this program. After you look at
the output on the program's screen, you can go back to the
debugger by pressing the Esc key.
For now, notice that the first instruction of the
program is a branch to the label print_string. Under that
instruction you see the label string followed by the
instruction
ADDQ.W #2,$6973(A0)
which is certainly not one of the instructions we included
in our program. We declared a string at that point in the
program. Figure 2.17 depicts the situation.
Figure 2.17. PRG_1EP in symbolic disassembled format.
It so happens that our declared data is just that which
would be the binary representation of that instruction. You
should convince yourself that this is true by going to the
editor and typing in that instruction just before the branch
instruction. Then assemble, and return to the debugger.
After relocating, click the symbolic button so that it is
unchecked. Now you can see that the normal disassembled
format of both the first and third lines in the output field
are identical. Refer to figure 2.18.
Figure 2.18. PRG_1EP with added statement in normal
disassembled format.
Now click the disassembled button so that you can view
the content of memory in hexadecimal and ASCII. You can see
that the same sequence of hexadecimal digits (54586973) and
ASCII characters are formed by both the instruction ADDQ.W
#2,$6973(A0) and the string THIS. See figure 2.19. We are
tipped that something is not legitimate about the sequence
of "instructions" directly following the branch instruction
by the line DC.W $2073 which is definitely not a legitimate
instruction. You will learn to be suspicious of any
instruction whose hexadecimal representation forms a neat
ASCII string.
Figure 2.19. PRG_1EP with added statement in
hexadecimal/ASCII dump format.
Some Other Debugger Functions
After eliminating the addq.w statement that you added
to the program for the demonstration, assemble and go back
to the debugger. Click on the appropriate buttons to get
back to the symbolic format. Notice that the label to which
the branch is to be taken is not visible in the output
field. You will be able to see it if you click on the
window's right hand scroll bar. After doing that, double
click on the from address button to go back to program
start. Now, go over to the from address button with the
mouse pointer and click once. When the parameter line
appears in place of the Change register bar, backspace over
the $ and type PRINT_STRING, then press the Return key.
The line containing the PRINT_STRING label will replace
the line containing the branch instruction as the first line
in the output field. Use this method to branch to labels in
your programs; do not use the Search function. The AssemPro
manual indicates, on page 95, under the Search heading, that
you can use labels with this function, however, if you
attempt to do this, you will sit waiting while an exhaustive
search through all of memory is conducted, then you will be
greeted with a Not found, sorry message.
Use the Search function to search (forward) for a
declared string or a numerical quantity. The string for
which you are searching must be typed on the parameter line,
within single or double quotes, as indicated in the AssemPro
manual, however, do not type the word Byte: before the
string as is indicated in the manual. Instead, as shown in
figure 2.20, you must click on the .B button which appears
in the Search function's dialog box.
Figure 2.20. The debugger Search function's dialog box; a
slightly compressed representation.
When searching for a numerical quantity, you must use
the appropriate base indicator ($ for hexadecimal, % for
binary, or nothing for decimal), and select the appropriate
button (.B, .W or .L), depending on the size of the data you
are looking for. You do not type anything else in front of
your search criteria. The manual does not properly describe
the use of the Search function.
The Register Field
Below the debugger register field there is a button
labeled Show register. If you click on this button, the
register field disappears. I can't imagine why anyone would
want to do that, but the manual tells us that displaying the
register field is optional. Figure 2.21 is a representation
of the register field. The dotted lines shown in the figure
are not actually visible on the AssemPro screen. I have
included them so that you can understand the manner in which
the content of the field is divided. It is the area between
a set of those dotted lines on which you must double click
to activate the dialog box discussed on page 89 of the
manual.
Figure 2.21. The debugger register field. If you double
click on an area between a set of dotted lines, a dialog box
will appear.
The dialog box permits you to specify an effective
address to be flagged. Usually, you will want to view the
content or address of a variable or a pointer. In that
case, you would press the Esc key to clear the parameter
line, then you would type in a label or some other effective
address such as (A0), 6(A1) or $72FB0. You must also select
a button to indicate whether the content or the address of
the effective address is to be shown in the register field.
Finally, you must chose .B, .W, or .L to indicate the length
of the data that will be shown. However, the manual does
not properly describe the manner in which a label must be
typed on the parameter line. Figure 2.22 shows a label
correctly specified on the parameter line.
Figure 2.22. Flagging a string for the register field.
In the discussion to follow, I will be using figures
that indicate specific addresses and the content of those
addresses. These are the values which appeared on my
debugger screen while I was preparing the example. When you
run through the example on your computer, the values will
probably be different because they depend on each system's
particular configuration.
With PRG_1EP in the debugger, figure 2.23 shows the
register field after four sessions with the dialog box. All
we care to see is the content of memory at the location
labeled STRING. The address at which the label STRING is
located is $72FBO. But when STRING is typed on the
parameter line, with the button options Content and .L
selected, the long word $2B540000 is shown in the field as
the content of that location. There are two reasons why we
know this information is not correct: we did not store that
information after the STRING label, and we can see the true
data in the disassembled field. The register field is
showing the content of memory location $2FB0. It is showing
that because it has incorrectly recorded the memory location
of STRING.
Figure 2.23. Appearance of the register field when the
parameter line of the dialog box has been prepared according
to the instructions in the AssemPro manual.
Fortunately, I have discovered a solution to this
problem. When you are specifying a label on the parameter
line, type .L after the label name. Both the label and the
extension may be in lower case or upper case. For example,
STRING.L and string.l are equivalent. Figure 2.24
illustrates the effectiveness of this solution.
Figure 2.24. The appearance of the register field when the
label has been followed by the suffix .L. Note that a ^
replaces the = when an address is being shown.
In this figure you are able to see the .L suffix in the
register field. Note that it will only be visible when the
label name is short enough so that truncation is not
required. Figure 2.25 shows what happens when a label
composed of more than seven characters is flagged.
Figure 2.25. Register field showing the truncation
of a flagged label.
Conclusion
I have suggested an assembly language programming
environment for the Atari ST with a great deal of
confidence. I was able to do that because the AssemPro and
TEMPUS packages are, respectively, the most powerful
assembler and programming editor available for the ST,
beyond question. Furthermore, both of the packages are
reasonably priced.
However, although I recommend the packages without
hesitation, I have had to point out certain undesirable
effects noticed during their use, and I have suggested
methods of dealing with those faults. I assign no
responsibility for the faults because the environment in
which AssemPro and TEMPUS are forced to operate is not
without flaws itself. I have simply reported my
observations and the solutions that I have found to be
satisfactory.