[comp.sys.apple2] Lyons--Writing DAs/CDevs/Inits KansasFest handout

dlyons@Apple.COM (David A. Lyons) (07/25/90)

Writing DAs, Inits, and CDevs

David A. Lyons, Apple II Developer Technical Support
Matt Deatherage, Apple II Developer Technical Support
Don Brady, Green System Software
(all of Apple Computer)
July 18, 1990


There are two kinds of desk accessories on the Apple IIgs:  Classic
Desk Accessories (which show up when you hit Apple-Ctrl-ESC) and New
Desk Accessories (which you select from the Apple menu in a desktop
program).


Classic Desk Accessories (CDAs)

Reference material:  the Desk Manager chapters of Apple IIgs Toolbox
Reference volumes 1 and 3, Apple IIgs Technical Note #71.

CDAs are executed in the text environment, and the stack is in page
1 of bank 0 ($00/01xx).  This means you only have 256 bytes of stack
space to work with, which isn't very much if you're making toolbox
calls or writing in a high-level language like C or Pascal.  Apple
IIgs Technical Note #71 shows you how to attempt to get more memory
for a stack, but the memory is not always available (under ProDOS 8,
all the bank 0 memory is already allocated for use by the application,
so you're stuck with just one page).

Reading the keyboard is tricky, becuase keyboard interrupts may or may
not be started up (the Event Manager is one thing that uses keyboard
interrupts to read the keyboard).  You can use IntSource in the
miscellaneous tools to temporarily disable keyboard interrupts, so the
Event Manager won't steal keystrokes away from you, or you can call
GetNextEvent to read keys if EMStatus indicates the Event Manager is
active.

CDAs get a chance to do special things when the system is booted,
and other times when applications call DeskShutDown.  Every CDA's
"shutdown" entry point gets called whenever DeskShutDown is called.
CDAs are not notified when DeskStartUp is called.

If you make GS/OS calls, be sure to check the OS_KIND byte at
$E1/00BC.  You'll find $00 in the 8-bit world and $01 in the 16-bit
world.  The user can enter your CDA under ProDOS 8.  With a
third-party product, a user can even load your CDA without booting
GS/OS at all--it's polite not to crash in these circumstances, so
check OS_KIND even when your "shutdown" routine is called for the
first time.

(There is no completely reliable way to make ProDOS 8 calls from a
CDA.)

There's a RemoveCDA toolbox call in the Desk Manager in System
Software 5.0 and later.  This removes a CDA from the list, but it
doesn't dispose of the CDA's memory, because there's no way to be
sure that the CDA hasn't installed hooks into the system (heatbeat
tasks, interrupt handlers, etc).  If you write an application that
calls RemoveCDA and then disposes of the CDA's memory, the
responsibility is yours to determine that it's safe.

CDAs can use the Message Center to communicate information to other
pieces of code that know how to look for specific messages you create.
See MessageCenter in the Tool Locator chapter of Apple IIgs Toolbox
Reference volume 1, and MessageByName in volume 3.

When you try to debug a CDA with GSBug, you crash in short order
because GSBug is not designed to trace code while the stack is in
page 1.  To trace your CDA code, locate its entry point, break into
GSBug from a convenient place in an application, set the Direct page
register to 0, and start Stepping from your CDA's entry point.
You'll probably have to reboot rather than resume the application,
but at least you have a way to step through the CDA code.



New Desk Accessories (NDAs)

References:  Desk Manager chapter of Apple IIgs Toolbox Reference
volume 1, Apple IIgs Technical Notes #12, 53, 71, 76, 83, and probably
others.  (Don't wait!  Memorize all the Technical Notes today.)

Notes on GS Technical Note #71:  ResourceShutDown does not have any
parameters, in spite of the note's instruction to "Call
ResourceShutDown with your NDA's user ID."  Oops.  Also, it is
advisable to call GetSysPrefs and SetSysPrefs (GS/OS calls) around
OpenResourceFile to ensure that the user gets prompted to insert the
correct disk if it happens to be offline (be sure to put the
preferences back how they were!).  It's also a good idea to call
GetLevel and SetLevel around the OpenResourceFile to minimize the
chance of an application accidentally closing your NDA's resource
fork (get the level, set it to 0, open your file, and put the level
back how it was).

Using tool sets that are not already started up is a complicated
issue.  See Apple IIgs Technical Note #53 for lots of information.
This note will be revised further in the future.

See Apple IIgs Technical Note #84 for information on using the System
Software 5.0 TaskMasterDA call to greatly simplify event handling for
your NDA.

NDAs have four entry points:  Init, Open, Close, and Action.

The Open routine creates a window, calls SetSysWindow on it, and
returns the pointer.  Starting with System Software 4.0, the Open
routine is allowed to return 0 if it doesn't want to open a window
or for some reason fails to open its window (out of memory, for
example).  

The Init entry point is notified at DeskShutDown and DeskStartUp so
that the DA can take any special actions needed at boot time or when
switching between applications.  (DeskShutDown happens once at boot
time before any application is executed.)

Note that you may get more than one consecutive DeskShutDown call
while switching between applications.  (For most NDAs this is no
problem.)

Use your NDA's existing memory ID, which you can get by calling
MMStartUp.  There is rarely a need to get another one with GetNewID.

NDAs should not call TLStartUp or TLShutDown!  (Those are for
applications.)

Where should an NDA put configuration files on disk?  Hard-coding the
pathname */System/Desk.Accs/my.settings is not a very great idea.
It's better to call LGetPathname or LGetPathname2 (in the System
Loader tool set, documented with GS/OS) to find your NDA's actual
pathname.  Then you can put your files in the same directory as your
NDA.  (Utilities exist that load NDAs from directories other than
*/System/Desk.Accs!)

NDAs that want to be friendly to users who boot their Apple IIgs's
over a network from an AppleShare file server have a little more
work cut out for them.  You can do a GetFileInfo GS/OS call on the
pathname "*/" and examine the fileSysID result field to determine
whether the user booted from an AppleShare volume.  If so, you can
make the AppleShare FST-specific call GetUserPath to find the pathname
of the user's private folder on the server.

NDAs can communicate with other pieces of code by using MessageByName.
See Apple IIgs Toolbox Reference volume 3.

Apple does not currently guarantee that future system software will
allow NDAs to receive keypresses with the Apple key down.
In current system software, NDAs do receive Apple keys, but there are
advantages to having those keys always handled by the current
application.  If you have an opinion on this, let's hear it!  It's
not too late.  [Opinion at the session was pretty clearly "don't
change it--my NDAs want Apple keys.  Discussion of alternatives is
still needed. --DAL]

RemoveNDA was added to the Desk Manager new for System Software 5.0.
(Be careful removing DAs, though.  If you actually UserShutDown them
and they're still trapping vectors or toolbox calls or have heartbeat
or run-queue tasks installed, etc, you'll crash the machine.)

Writing a NDA to call install another NDA is tricky--you need to use
SchAddTask to make it happen after your DA code exits back to the Desk
Manager.  See Apple IIgs Technical Note #71.

Debugging NDAs with GSBug used to be nearly impossible if the NDA
accepted keyDown events.  With GSBug 1.5, you can put the CapsLock
key down to cause GSBug to receive keystrokes instead of letting 
the NDA munch them.

Here are a few of things applications should do for maximum
compatibility with NDAs:

--Call DeskStartUp after all the other toolbox startups, and call
DeskShutDown first, before shutting down other tools.

--Enable the Undo/Cut/Copy/Paste/Clear items in the Edit menu when
an NDA is the front window (see GetSysWFlag or GetWKind in the Window
Manager chapter of volume 2).

--Support the Scrap Manager, and have the application's main loop
watch for GetScrapCount to change.  This way an NDA can change the
contents of the clipboard and the application can invalidate its Show
Clipboard window.  (A CDA could change the scrap, too, so don't avoid
keeping a "private scrap" and converting it to the public formats
only when an NDA comes to the front or goes away!)


Control Panel Devices (CDevs)

The only place CDevs are documented is File Type note $C7.  DTS Sample
code available in several languages.

Be sure to read Apple IIgs Toolbox Reference volume 3 for information
about extended controls and resources.  These make CDevs pretty easy
to write.  Apple IIgs Technical Note #81 contains more information on
extended controls.

It's difficult to have any of your CDev's code stay resident in
memory while your CDev is not the one currently selected in the
control panel.  If you need to keep code around all the time,
consider writing an Initialization file or an NDA instead, or having
an init file communicate with your CDev through the Message Center.


Initialization Files (Inits)

Inits are executed by the system at boot time, after the OS is
present but before the first application is launched.

There are two kinds of Initializaton files, Permanent Init Files
(PIFs, with file type $B6) and Temporary Init Files (TIFs, with file
type $B7).  Watch for File Type Notes to be released for these soon.
[This probably means the mid-September batch.]

Inits are documented poorly (apparently not at all, except for page
266 of Programmer's Introduction to the Apple IIgs.

Permanent inits generally stay in memory until the system is shut
down, but temporary inits execute and are then immediately removed
from memory.

The environment when an Init gets control is this:
--Processor is in 16-bit native mode.
--There is a 4K stack/direct-page space.  D points to the bottom,
and S points near the top.
--The A register contains the init's memory ID
--The Bank register is not defined.  If you use absolute addressing,
start with PHB PHK PLB and end with PLB.
--You can't use the desktop tools.  User interaction is not generally
a good idea at boot time (because the desktop environment may or may
not be there--we don't guarantee it one way or the other; and because
users with long boot times, for whatever reason, want to be able to
turn on the machine and go get a cool drink while it boots, without
finding the thing only part-way booted when they come back, waiting
for them to hit OK).  If you need user interaction, consider using a
CDev or an NDA and, if necessary, having it communicate with your init
through the Message Center (MessageByName, volume 3).

The init must execute an RTL when it's finished.  A permanent init
can store a $0001 in the word just above the RTL address (LDA #1,
STA 4,S) to ask the system to unload it (as if it were a temporary
init instead).  A PIF that is useless without certain nonstandard
hardware might want to do this, for example.

PIFs may want to install HeartBeat tasks (using SetHeartBeat and
IntSource in the Miscellaneous tools), or they may wanto to install
RunQueue tasks (AddToRunQ in the Desk Manager).

[end]
-- 
David A. Lyons, Apple Computer, Inc.      |   DAL Systems
Apple II Developer Technical Support      |   P.O. Box 875
America Online: Dave Lyons                |   Cupertino, CA 95015-0875
GEnie: D.LYONS2 or DAVE.LYONS         CompuServe: 72177,3233
Internet/BITNET:  dlyons@apple.com    UUCP:  ...!ames!apple!dlyons
   
My opinions are my own, not Apple's.