Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "Programming in the Amiga Environment"
Steven Solie (talk | contribs) |
|||
Line 451: | Line 451: | ||
* Never assume that a hardware register will be initialized to any particular value. Different versions of the OS may leave registers set to different values. Check the ''Amiga Hardware Reference Manual'' to ensure that you are setting up all the registers that affect your code. |
* Never assume that a hardware register will be initialized to any particular value. Different versions of the OS may leave registers set to different values. Check the ''Amiga Hardware Reference Manual'' to ensure that you are setting up all the registers that affect your code. |
||
− | === AmigaOS Interface |
+ | === AmigaOS Interface Style Guide === |
In order to make all programs in AmigaOS look consistent, AmigaOS provides some default thumbnails that developers must use in their GUI. These pictures are located in the '''tbimages:''' assign. They are installed in any AmigaOS installation. |
In order to make all programs in AmigaOS look consistent, AmigaOS provides some default thumbnails that developers must use in their GUI. These pictures are located in the '''tbimages:''' assign. They are installed in any AmigaOS installation. |
Revision as of 20:00, 21 June 2012
This page is not yet fully updated to AmigaOS 4.x some of the information contained here may not be applicable in part or totally. |
Contents
- 1 Programming in the Amiga Environment
- 2 About the Examples
- 3 General Amiga Development Guidelines
- 4 1.3 Compatibility Issues
- 5 Error Reports
Programming in the Amiga Environment
To program in the Amiga's dynamic environment you need to understand these special features of the Amiga's design:
- Multitasking (without memory protection)
- Shared libraries of functions
- Dynamic memory architecture (no memory map)
- Operating system versions
- Custom chips with DMA access (two kinds of memory)
Multitasking
The key feature of the Amiga's operating system design is multitasking. Multitasking means many programs, or tasks, reside in memory at the same time sharing system resources with one another. Programs take turns running so it appears that many programs are running simultaneously.
Multitasking is based on the concept that a program spends most of its time waiting for things to happen. A program waits for events like key presses, mouse movement, or disk activity. While a program is waiting, the CPU is idle. The CPU could be used to run a different program during this idle period if there was a convenient method for rapidly switching from one program to another. This is what multitasking does.
What the System Does For You
The Amiga uses preemptive multitasking which means that the operating system keeps track of all the tasks in memory and decides which one should run. The system checks hundreds of times per second to see which task should be run based on whether or not it is waiting, and other factors. Since the system handles all the work of task switching, multitasking is transparent to the application. From the application's point of view, it appears to have the machine all to itself.
The Amiga OS also manages the sharing of resources between tasks. This is important because in order for a variety of tasks to run independently in the Amiga's multitasking environment, tasks must be prevented from interfering with one another. Imagine if five tasks were allowed to use the parallel port at the same time. The result would be I/O chaos. To prevent this, the operating system provides an arbitration method (usually a function call) for every system resource. For instance you must call a function, AllocMem(), to get exclusive access to a block of memory.
What the System Doesn't Do For You
The Amiga operating system handles most of the housekeeping needed for multitasking, but this does not mean that applications don't have to worry about multitasking at all. The current generation of Amiga systems do not have hardware memory protection, so there is nothing to stop a task from using memory it has not legally acquired. An errant task can easily corrupt some other task by accidentally overwriting its instructions or data. Amiga programmers need to be extra careful with memory; one bad memory pointer can cause the machine to crash (debugging utilities such as MungWall and Enforcer will prevent this).
In fact, Amiga programmers need to be careful with every system resource, not just memory. All system resources from audio channels to the floppy disk drives are shared among tasks. Before using a resource, you must ask the system for access to the resource. This may fail if the resource is already being used by another task. Once you have control of a resource, no other task can use it, so give it up as soon as you are finished. When your program exits, you must give everything back whether it's memory, access to a file, or an I/O port. You are responsible for this, the system will not do it for you automatically.
What Every Amiga Programmer Should Know: The Amiga is a multitasking computer. Keep in mind that other tasks are running at the same time as your application. Always ask the system for control of any resource you need; some other task may already be using it. Give it back as soon as you are done; another task may want to use it. This applies to just about every computing activity your application can perform. |
Libraries of functions
Most of the routines that make up the Amiga's operating system are organized into groups called libraries. Each library then contains one or more interfaces. In order to call a function on the Amiga you must first open the library that contains the function. Next, you get the interface which contains the function you want to call. For example, if you want to call the Read() function to read data from disk you must first open the DOS library and then get the "main" interface the Read() function is defined in.
The system's master library, called Exec, is always open. Exec keeps track of all the other libraries and is in charge of opening and closing them. Exec's "main" interface contains the OpenLibrary() function which is used to open all the other libraries.
Almost any program you write for the Amiga will have to call the OpenLibrary() and GetInterface() functions. Usage is as follows:
// Global: declare this above main() struct Library *LibBase; struct Interface *LibIFace; int main() { LibBase = IExec->OpenLibrary("library.name", libversion); if(LibIFace == NULL) { // Library did not open, so exit. } else { LibIFace = IExec->GetInterface(LibBase, "main", ifaceversion, NULL); if(LibIFace == NULL) { // Could not get Interface, so exit. } else { // Interface obtained, so use its functions. } } }
- LibBase
- This is a pointer to the library structure in memory, often referred to as the library base. The library may or may not be global depending on your needs. The name of this pointer may be changed although it is proper to use the established standard name. Refer to the list below for the appropriate name.
- LibIFace
- This is a pointer to the interface structure in memory. The interface may or may not be global depending on your needs. The name of this pointer may be changed although it is proper to use the established standard name. Refer to the list below for the appropriate name.
- library.name
- This is a C string that describes the name of the library you wish to open. The list of Amiga library names is given below.
- main
- This is a C string that describes the name of the interface you wish to use. The list of Amiga interface names depends on the library and is given below.
- libversion
- This should be set to the earliest acceptable library version. A value of 0 matches any version. A value of 53 means you require at least version 53 or a later version of the library. If the library version in the system is older than the one you specify, OpenLibrary() will fail (return 0).
- ifaceversion
- This should be set to the interface version you require. Each interface has a unique version number which defines all the functions in that interface. Most often an interface will have a single interface version of 1. If the interface version in the system does not match, GetInterface() will fail (return 0).
The table listed below shows all the function libraries that are currently part of the Amiga system software.
Library Name | Library Base | Oldest Library Version In Use | Interface Name:Version |
---|---|---|---|
library.name* | LibBase | version | iface.name:version |
asl.library | AslBase | 50 | main:1 |
commodities.library | CxBase | 50 | main:1 |
diskfont.library | DiskfontBase | 50 | main:1 |
dos.library | DOSBase | 50 | main:1 |
exec.library | ExecBase | 50 | main:1 |
expansion.library | ExpansionBase | 50 | main:1 |
gadtools.library | GadToolsBase | 50 | main:1 |
graphics.library | GfxBase | 50 | main:1 |
icon.library | IconBase | 50 | main:1 |
iffparse.library | IFFParseBase | 50 | main:1 |
intuition.library | IntuitionBase | 50 | main:1 |
keymap.library | KeymapBase | 50 | main:1 |
layers.library | LayersBase | 50 | main:1 |
mathffp.library | MathBase | 50 | main:1 |
mathtrans.library | MathTransBase | 50 | main:1 |
mathieeedoubbas.library | MathIeeeDoubBasBase | 50 | main:1 |
mathieeedoubtrans.library | MathIeeeDoubTransBase | 50 | main:1 |
mathieeesingbas.library | MathIeeeSingBasBase | 50 | main:1 |
mathieeesingtrans.library | MathIeeeSingTransBase | 50 | main:1 |
rexxsyslib.library | RexxSysBase | 50 | main:1 |
translator.library | TranslatorBase | 50 | main:1 |
utility.library | UtilityBase | 50 | main:1 |
workbench.library | WorkbenchBase | 50 | main:1 |
* Other libraries may exist that are not supplied by the AmigaOS development team since it is a feature of the operating system to allow such libraries.
Opening a Library in C
Here's a brief example showing how OpenLibrary() and GetInterface() are used in C.
/* easy.c: a complete example of how to open an Amiga function library in C. * In this case the function library is Intuition. Once the Intuition * function library is open and the interface obtains, any Intuition function * can be called. This example uses the DisplayBeep() function of Intuition to * flash the screen. */ #include <proto/exec.h> #include <proto/intuition.h> struct Library *IntuitionBase; struct IntuitionIFace *IIntuition; int main() { IntuitionBase = IExec->OpenLibrary("intuition.library", 50); // Note it is safe to call GetInterface() with a NULL library pointer. IIntuition = (struct IntuitionIFace *)IExec->GetInterface(IntuitionBase, "main", 1, NULL); if(IIntuition != NULL) /* Check to see if it actually opened. */ { /* The Intuition library is now open so */ IIntuition->DisplayBeep(0); /* any of its functions may be used. */ } // Always drop the interface and close the library if not in use. // Note it is safe to call DropInterface() and CloseLibrary() with NULL pointers. IExec->DropInterface((struct Interface *)IIntuition); IExec->CloseLibrary(IntuitionBase); }
Another Kind of Function Library
The Amiga has two kinds of libraries: run-time libraries and link libraries. All the libraries discussed so far are run-time libraries. Run-time libraries make up most of the Amiga's operating system and are the main topic of this book.
There is another type of library known as a link library. Even though a link library is a collection of functions just like a run-time library, there are some major differences in the two types.
A run-time, or shared library is a group of functions managed by Exec that resides either in ROM or on disk (in the "LIBS:" directory). A run-time library must be opened before it can be used (as explained above). The functions in a run-time library are accessed dynamically at run-time and can be used by many programs at once even though only one copy of the library is in memory. A disk based run-time library is loaded into memory only if requested by a program and can be automatically flushed from memory when no longer needed.
A link library is a group of functions on disk that are managed by the compiler at link time. Link libraries do not have to be opened before they are used, instead you must link your code with the library when you compile a program. The functions in a link library are actually copied into every program that uses them. For instance the exit() function used in the C program listed above is not part of any of the libraries that make up the Amiga OS. It comes from the link library supplied with the compiler. The code that performs the exit() function is copied into the program when it is compiled.
Libraries, Devices and Resources
Most of the Amiga's OS routines are organized into groups of shared run-time libraries. The Amiga also has specialized function groups called devices and resources that programmers use to perform basic I/O operations or access low-level hardware.
Devices and resources are similar in concept to a shared run-time library. They are managed by Exec and must be opened before they can be used. Their functions are separate from the programs that use them and are accessed dynamically at run time. Multiple programs can access the device or resource even though only one copy exists in memory (a few resources can only be used by one program at a time.)
Devices and resources are managed by Exec just as libraries are. For more information on devices and resources, see the respective sections.
What Every Amiga Programmer Should Know: The functions in the Amiga OS are accessed through shared run-time libraries. Libraries must be opened before their functions may be used. The system's master library, Exec, is always open. The Exec function OpenLibrary() is used to open all other libraries. |
Dynamic memory architecture
Unlike some microcomputer operating systems, the AmigaOS relies on absolute memory addresses as little as possible. Instead the Amiga OS uses a technique (sometimes referred to as soft machine architecture) which allows system routines and data structures to be positioned anywhere in memory.
Amiga run-time libraries may be positioned anywhere in memory because they are always accessed through a jump table. Each library whether in ROM or loaded from disk has an associated Library structure and jump table in RAM.
The system knows where the jump table starts in RAM because when a library is opened for the first time, Exec creates the library structure and keeps track of its location. The order of the entries in the library's jump table is always preserved between versions of the OS but the functions they point to can be anywhere in memory. Hence, system routines in ROM may be moved from one version of the OS to another. Given the location of the jump table and the appropriate offset into the table, any function can always be found.
Not only are system routines relocatable but system data structures are too. In the Amiga's multitasking environment, multiple applications run at the same time and each may have its own screen, memory, open files, and even its own subtasks. Since any number of application tasks are run and stopped at the user's option, system data structures have to be set up as needed. They cannot be set up ahead of time at a fixed memory location because there is no way to tell how many and what type will be needed.
The Amiga system software manages this confusion by using linked lists of information about items such as libraries, tasks, screens, files and available memory. A linked list is a chain of data items with each data item containing a pointer to the next item in the chain. Given a pointer to the first item in a linked list, pointers to all the other items in the chain can be found.
Exec: The System Executive
On the Amiga, the module that keeps track of linked lists is Exec, the system executive. Exec is the heart of the Amiga operating system since it also is in charge of multitasking, granting access to system resources (like memory) and managing the Amiga library system.
As previously discussed, memory location 4 ($0000 0004), also known as SysBase, contains a pointer to the Exec library structure. This is the only absolutely defined location in the Amiga operating system. A program need only know where to find the Exec library to find, use and manipulate all other system code and data.
The diagram above shows how the entire Amiga operating system is built as a tree starting at SysBase. Exec keeps linked lists of all the system libraries, devices, memory, tasks and other data structures. Each of these in turn can have its own variables and linked lists of data structures built onto it. In this way, the flexibility of the OS is preserved so that upgrades can be made without jeopardizing compatibility.
What Every Amiga Programmer Should Know: The Amiga has a dynamic memory map. There are no fixed locations for operating system variables and routines. Do not call ROM routines or access system data structures directly. Instead use the indirect access methods provided by the system. |
Operating system versions
The Amiga operating system has undergone several major revisions summarized in the table below. The latest revision is Release 2 (corresponds to library versions 36 and above).
System library | Kickstart release version number |
---|---|
0 | Any version |
30 | Kickstart V1.0 (obsolete) |
31 | Kickstart V1.1 (NTSC only - obsolete) |
32 | Kickstart V1.1 (PAL only - obsolete) |
33 | Kickstart V1.2 (the oldest revision still in use) |
34 | Kickstart V1.3 (adds autoboot to V33) |
35 | Special Kickstart version to support A2024 high-resolution monitor |
36 | Kickstart V2.0 (old version of Release 2) |
37 | Kickstart V2.04 (current version of Release 2) |
The examples listed throughout this book assume you are using Release 2.
Many of the libraries and functions documented in this manual are available in all versions of the Amiga operating system. Others are completely new and cannot be used unless you have successfully opened the appropriate version of the library.
To find out which functions are new, refer to the SDK. The functions which are new are marked with (V50) or (V51) in the NAME line of the function Autodoc. These new functions require you to use the corresponding version number (50, 51, or higher) when opening the library.
Exit gracefully and informatively if the required library version is not available.
About Release 2
Release 2 first appeared on the Amiga 3000. This initial version corresponds to Kickstart V2.00, system library version number V36. Release 2 was subsequently revised and this older version is now considered obsolete.
Programs written for Release 2 should use only the later version corresponding to Kickstart V2.04, system library version number V37. If your system is using the earlier version of Release 2, you should upgrade your system.
What Every Amiga Programmer Should Know: Some libraries or specific functions are not available in older versions of the Amiga operating system. Be sure to ask for the lowest library version that meets the requirements of your program. |
The custom chips
The most important feature of the Amiga's hardware design is the set of custom chips that perform specialized tasks independently of the CPU. Each of the custom chips (named Paula, Agnus, and Denise) is dedicated to a particular job:
Paula (8364) | Audio, floppy disk, serial, interrupts |
Agnus (8361/8370/8372) | Copper (video coprocessor), blitter, DMA control |
Denise (8362) | Color registers, color DACs (Digital to Analog Converters) and sprites |
The custom chips can perform work independently of the CPU because they have DMA, or Direct Memory Access, capability. DMA means the custom chips can access special areas of memory by themselves without any CPU involvement. (On computer systems without DMA, the CPU must do some or all of the memory handling for support chips.) The Amiga's custom chips make multitasking especially effective because they can handle things like rendering graphics and playing sound independently, giving the CPU more time to handle the overhead of task-switching and other important jobs.
Custom Chip Revisions
The custom chips have been revised as the Amiga platform has evolved and newer models of the Amiga developed. The latest revision of the Amiga custom chips is known as the Enhanced Chip Set, or ECS. Certain features of the Amiga operating system, such as higher resolution screens and special genlock modes, require the ECS version of the custom chips. In this book, features that require ECS are noted in the accompanying text. For more details about the special features of ECS, see Appendix C of the Amiga Hardware Reference Manual.
Two Kinds of Memory
To keep the Classic Amiga running efficiently, the Classic Amiga has two memory buses and two kinds of memory. Chip memory is memory that both the CPU and custom chips can access. Fast memory is memory that only the CPU (and certain expansion cards) can access. Since Chip memory is shared, CPU access may be slowed down if the custom chips are doing heavy-duty processing. CPU access to Fast memory is never slowed down by contention with the custom chips.
The distinction between Chip memory and Fast memory is very important for Classic Amiga programmers to keep in mind because any data accessed directly by the custom chips such as video display data, audio data or sprite data must be in Chip memory.
What Every Amiga Programmer Should Know: The Amiga has two kinds of memory: Chip memory and Fast memory. Use the right kind. |
NOTE There is no such thing as Chip memory in any of new PowerPC-based Amiga systems. The two types of memory only applies to the Classic Amiga hardware platforms such as the A1200 and A4000 models.
About the Examples
For the most part, the examples in this book are written in C.
C examples have been compiled using the standard Software Development Kit (SDK) using GCC.
In general, the examples are also compatible with vbcc and other C compilers, however some changes will usually be necessary.
Specifically, all the C examples assume that the automatic Ctrl-C feature of the compiler has been disabled. For SAS C (and Lattice C revisions 4.0 and greater) this is handled with:
/* Add this before main() to override the default Ctrl-C handling * provided in SAS (Lattice) C. Ctrl-C event will be ignored */ int CXBRK ( void ) { return(0); } int chkabort( void ) { return(0); }
For Manx Aztec C, replace the above with:
/* Add this near the top */ #include <functions.h> /* Add this before main() */ extern int Enable_Abort; /* reference abort enable */ /* Add this after main(), as the first active line in the program */ Enable_Abort=0; /* turn off CTRL-C */
Other changes may be required depending on the example and the C compiler you are using. Most of the C examples in this book use the following special option flags of the SAS/C compiler (set the equivalent option of whatever compiler you are using):
-b1 | = | Small data model. |
-cf | = | Check for function prototypes. |
-i | = | Ignore #include statements that are identical to one already given. |
-s | = | Store all literal strings that are identical in the same place. |
-t | = | Enable warnings for structures that are used before they are defined. |
-v | = | Do not include stack checking code with each function. |
-y | = | Load register A4 with the data section base address on function entry. |
The -v and -y flags are are generally only needed for parts of the | ||
program that are called directly by the system such as interrupt | ||
servers, subtasks, handlers and callback hook functions. |
Except where noted, each example was linked with the standard SAS/C startup code c.o, the SAS/C linker library lc.lib and the linker library amiga.lib. The SAS/C compiler defaults to 32-bit ints. If your development environment uses 16-bit ints you may need to explicitly cast certain arguments as longs (for example 1L << sigbit instead of 1 << sigbit).
General Amiga Development Guidelines
In the earlier sections of this chapter, the basic environment of the Amiga operating system was discussed. This sections presents specific guidelines that all Amiga programmers must follow. Some of these guidelines are for advanced programmers or apply only to code written in assembly language.
Check for memory loss. Arrange your Workbench screen so that you have a Shell available and can start your program without rearranging any windows. In the Shell window type Avail flush several times (the flush option requires the Release 2 version of the Avail command). Note the total amount of free memory. Run your program (do not rearrange any windows other than those created by the program) and then exit. At the Shell, type Avail flush several times again. Compare the total amount of free memory with the earlier figure. They should be the same. Any difference indicates that your application is not freeing some memory it used or is not closing a disk-loaded library, device or font it opened. Note that under Release 2, a small amount of memory loss is normal if your application is the first to use the audio or narrator device.
Use all of the program debugging and stress tools that are available when writing and testing your code. New debugging tools such as Enforcer, MungWall, and Scratch can help find uninitialized pointers, attempted use of freed memory and misuse of scratch registers or condition codes (even in programs that appear to work perfectly).
Always make sure you actually get any system resource that you ask for. This applies to memory, windows, screens, file handles, libraries, devices, ports, etc. Where an error value or return is possible, ensure that there is a reasonable failure path. Many poorly written programs will appear to be reliable, until some error condition (such as memory full or a disk problem) causes the program to continue with an invalid or null pointer, or branch to untested error handling code.
Always clean up after yourself. This applies for both normal program exit and program termination due to error conditions. Anything that was opened must be closed, anything allocated must be deallocated. It is generally correct to do closes and deallocations in reverse order of the opens and allocations. Be sure to check your development language manual and startup code; some items may be closed or deallocated automatically for you, especially in abort conditions. If you write in the C language, make sure your code handles Ctrl-C properly.
Remember that memory, peripheral configurations, and ROMs differ between models and between individual systems. Do not make assumptions about memory address ranges, storage device names, or the locations of system structures or code. Never call ROM routines directly. Beware of any example code you find that calls routines at addresses in the $F00000 $FFFFFF range. These are ROM routines and they will move with every OS release. The only supported interface to system ROM code is through the library, device, and resource calls.
Never assume library bases or structures will exist at any particular memory location. The only absolute address in the system is $00000004, which contains a pointer to the Exec library base. Do not modify or depend on the format of private system structures. This includes the poking of copper lists, memory lists, and library bases.
Never assume that programs can access hardware resources directly. Most hardware is controlled by system software that will not respond well to interference from other programs. Shared hardware requires programs to use the proper sharing protocols. Use the defined interface; it is the best way to ensure that your software will continue to operate on future models of the Amiga.
Never access shared data structures directly without the proper mutual exclusion (locking). Remember that other tasks may be accessing the same structures.
The system does not monitor the size of a program’s stack. (Your compiler may have an option to do this for you.) Take care that your program does not cause stack overflow and provide extra stack space for the possibility that some functions may use up additional stack space in future versions of the OS.
Never use a polling loop to test signal bits. If your program waits for external events like menu selection or keystrokes, do not bog down the multitasking system by busy-waiting in a loop. Instead, let your task go to sleep by Wait()ing on its signal bits. For example:
[h]
signals = (ULONG)Wait( (1<<windowPtr->UserPort->mp_SigBit) | (1<<consoleMsgPortPtr->mp_SigBit) );
This turns the signal bit number for each port into a mask, then combines them as the argument for the Exec library Wait() function. When your task wakes up, handle all of the messages at each port where the mp_SigBit is set. There may be more than one message per port, or no messages at the port. Make sure that you ReplyMsg() to all messages that are not replies themselves. If you have no signal bits to Wait() on, use Delay() or WaitTOF() to provide a measured delay.
Tasks (and processes) execute in 680x0 user mode. Supervisor mode is reserved for interrupts, traps, and task dispatching. Take extreme care if your code executes in supervisor mode. Exceptions while in supervisor mode are deadly.
Most system functions require a particular execution environment. All DOS functions and any functions that might call DOS (such as the opening of a disk-resident library, font, or device) can only be executed from a process. A task is not sufficient. Most other kernel functions may be executed from tasks. Only a few may be executed from interrupts.
Never disable interrupts or multitasking for long periods. If you use Forbid() or Disable(), you should be aware that execution of any system function that performs the Wait() function will temporarily suspend the Forbid() or Disable() state, and allow multitasking and interrupts to occur. Such functions include almost all forms of DOS and device I/O, including common stdio functions like printf().
Never tie up system resources unless it is absolutely necessary. For example, if your program does not require constant use of the printer, open the printer device only when you need it. This will allow other tasks to use the printer while your program is running. You must provide a reasonable error response if a resource is not available when you need it.
All data for the custom chips must reside in Chip memory (type MEMF_CHIP). This includes bitplanes, sound samples, trackdisk buffers, and images for sprites, bobs, pointers, and gadgets. The AllocMem() call takes a flag for specifying the type of memory. A program that specifies the wrong type of memory may appear to run correctly because many Amigas have only Chip memory. (On all models of the Amiga, the first 512K of memory is Chip memory. In later models, Chip memory may occupy up to the first one or two megabytes).
However, once expansion memory has been added to an Amiga (type MEMF_FAST), any memory allocations will be made in the expansion memory area by default. Hence, a program can run correctly on an unexpanded Amiga which has only Chip memory while crashing on an Amiga which has expanded memory. A developer with only Chip memory may fail to notice that memory was incorrectly specified.
Most compilers have options to mark specific data structures or object modules so that they will load into Chip RAM. Some older compilers provide the Atom utility for marking object modules. If this method is unacceptable, use the AllocMem() call to dynamically allocate Chip memory, and copy your data there.
When making allocations that do not require Chip memory, do not explicitly ask for Fast memory. Instead ask for memory type MEMF_PUBLIC or 0L as appropriate. If Fast memory is available, you will get it.
Never use software delay loops! Under the multitasking operating system, the time spent in a loop can be better used by other tasks. Even ignoring the effect it has on multitasking, timing loops are inaccurate and will wait different amounts of time depending on the specific model of Amiga computer. The timer device provides precision timing for use under the multitasking system and it works the same on all models of the Amiga. The AmigaDOS Delay() function or the graphics library WaitTOF() function provide a simple interface for longer delays. The 8520 I/O chips provide timers for developers who are bypassing the operating system (see the Amiga Hardware Reference Manual for more information).
Always obey structure conventions!
All non-byte fields must be word-aligned. Longwords should be longword-aligned for performance.
All address pointers should be 32 bits (not 24 bits). Never use the upper byte for data.
Fields that are not defined to contain particular initial values must be initialized to zero. This includes pointer fields.
All reserved or unused fields must be initialized to zero for future compatibility.
Data structures to be accessed by the custom chips, public data structures (such as a task control block), and structures which must be longword aligned must not be allocated on a program’s stack.
Dynamic allocation of structures with AllocMem() provides longword aligned memory of a specified type with optional initialization to zero, which is useful in the allocation of structures.
Hardware programming guidelines
If you find it necessary to program the hardware directly, then it is your responsibility to write code that will work correctly on the various models and configurations of the Amiga. Be sure to properly request and gain control of the hardware resources you are manipulating, and be especially careful in the following areas:
- Kickstart 2.0 uses the 8520 Complex Interface Adaptor (CIA) chips differently than 1.3 did. To ensure compatibility, you must always ask for CIA access using the cia.resource AddICRVector() and RemICRVector() functions. Do not make assumptions about what the system might be using the CIA chips for. If you write directly to the CIA chip registers, do not expect system services such as the trackdisk device to function. If you are leaving the system up, do not read or write to the CIA Interrupt Control Registers directly; use the cia.resource AbleICR(), and SetICR() functions. Even if you are taking over the machine, do not assume the initial contents of any of the CIA registers or the state of any enabled interrupts.
- All custom chip registers are Read-only or Write-only. Do not read Write-only registers, and do not write to Read-only registers.
- Never write data to, or interpret data from the unused bits or addresses in the custom chip space. To be software-compatible with future chip revisions, all undefined bits must be set to zeros on writes, and must be masked out on reads before interpreting the contents of the register.
- Never write past the current end of custom chip space. Custom chips may be extended or enhanced to provide additional registers, or to use bits that are currently undefined in existing registers.
- Never read, write, or use any currently undefined address ranges or registers. The current and future usage of such areas is reserved by the AmigaOS development team and is subject to change.
- Never assume that a hardware register will be initialized to any particular value. Different versions of the OS may leave registers set to different values. Check the Amiga Hardware Reference Manual to ensure that you are setting up all the registers that affect your code.
AmigaOS Interface Style Guide
In order to make all programs in AmigaOS look consistent, AmigaOS provides some default thumbnails that developers must use in their GUI. These pictures are located in the tbimages: assign. They are installed in any AmigaOS installation. Note that something may still go wrong and these pictures may not be found. In this case, the program must provide a fallback method instead of just failing. The developer can choose to display text-only toolbars or include default pictures with their program.
1.3 Compatibility Issues
This 3rd edition of the Amiga Technical Reference Series focuses on the Release 2 version of the Amiga operating system (Kickstart V2.04, V37). Release 2 of the operating system was first shipped on the Amiga 3000 and now available as an upgrade kit for the Amiga 500 and Amiga 2000 models to replace the older 1.3 (V34) operating system. Release 2 contains several new libraries and hundreds of new library functions and features to assist application writers.
Design decisions
The latest Amiga models, including all A3000’s, are running Release 2. But many older Amigas are still running 1.3 at this time. Depending on your application and your market, you may choose to require the Release 2 operating system as a minimum platform. This can be a reasonable requirement for vertical markets and professional applications. Release 2 can also be a reasonable requirement for new revisions of existing software products, since you could continue to ship the older 1.3-compatible release in the same package. If you have made the decision to require Release 2, then you are free to take advantage of all of the new libraries and features that Release 2 provides.
Throughout this latest edition of the Amiga Technical Reference Series, features, functions and libraries that are new for Release 2 are usually indicated by (V36) or (V37) in the description of the feature. Such features are not available on Amiga models that are running 1.3 (V34) or earlier versions of the OS. Unconditional use of Release 2 functions will cause a program to fail when it is run on a machine with the 1.3 OS. It is very important to remember this when designing and writing your code.
Developers of consumer-priced productivity, entertainment and utility software may not yet be ready to write applications that require Release 2, but even these developers can enhance their products by taking advantage of Release 2 while remaining 1.3 compatible.
There are three basic methods that will allow you to take advantage of enhanced Release 2 features while remaining 1.3 compatible:
- Transparent Release 2 Extensions
- Conditional Code
- Compatible Libraries
Transparent Release 2 Extensions
To provide Release 2 enhancements while remaining compatible with the older 1.3 version of the OS, several familiar 1.3 system structures have been extended to include an optional pointer to additional information. The new extended versions of such structures are generally defined in the same include file as the original structure. These extended structures are passed to the same 1.3 system functions as the unextended structure (e.g., OpenWindow(), OpenScreen(), AddGadget(), OpenDiskFont()). The existence of the extended information is signified by setting a new flag bit in the structure. (In one case, PROPNEWLOOK, only the flag bit itself is significant). These extensions are transparent to previous versions of the operating system. Only the Release 2 operating system will recognize the bit and act on the extended information.
Flag | ||||
Original | Extended | Field | Flag Bit | Defined In |
NewScreen | ExtNewScreen | Type | NS_EXTENDED | <intuition/screens.h> |
NewWindow | ExtNewWindow | Flags | WFLG_NW_EXTENDED | <intuition/intuition.h> |
Gadget | Gadget | Flags | GFLG_STRINGEXTEND | <intuition/intuition.h> |
PropInfo | PropInfo | Flags | PROPNEWLOOK | <intuition/intuition.h |
TextAttr | TTextAttr | tta_Style | FSF_TAGGED | <graphics/text.h> |
Through the use of such extensions, applications can request special Release 2 features in a 1.3-compatible manner. When the application is run on a Release 2 machine, the enhanced capabilities will be active.
The enhancements available through these extensions include:
Screen: | Overscan, 3D Look (SA_Pens), public screens, PAL/NTSC, new modes |
Window: | Autoadjust sizing, inner dimensions, menu help |
Gadget: | Control of font, pens, and editing of string gadgets |
PropInfo: | Get 3D Look proportional gadgets when running under Release 2 |
TTextAttr: | Control width of scaled fonts |
Extensible longword arrays called TagItem lists are used to specify the extended information for many of these structures. Tag lists provide an open-ended and backwards-compatible method of growing system structures by storing all new specifications in an extendible array of longwords pairs.
Another transparent Release 2 extension is the diskfont library's ability to scale bitmap and outline fonts to arbitrary sizes and the availability of scalable outline fonts. Make sure that these new scalable fonts are available to your application by not setting the FPF_DESIGNED flag for AvailFonts() or OpenDiskFont(). Allow the user to create new font sizes by providing a way for her to manually enter the desired font size (the 1.3 OS returns the closest size, Release 2 returns the requested size).
See the Intuition and graphics library chapters, and the include file comments for additional information. See the "Utility Library" chapter for more information on TagItems and tag lists.
Conditional Code
Conditional code provides a second way to take advantage of Release 2 enhancements in a 1.3-compatible application. The basic idea is to add low overhead conditional code, based on library version, to make use of selected Release 2 features if they are available. There are some powerful and beneficial Release 2 features which are definitely worth conditional code.
The control flow for such conditional code is always based on whether a particular version of a library is available. Failure of OpenLibrary() (i.e., return value of NULL) means that the library version requested is not available. The version number of a library that successfully opened can be checked by testing LibBase->lib_Version. Always check for a version greater or equal to the version you need.
Examples of conditional library checking code:
/* Checking for presence of a new Release 2 library */ if( AslBase = OpenLibrary("asl.library", 37L) ) { /* OK to use the ASL requester */ } else { /* Must use a different method */ } /* Checking version of an existing library with new Release 2 features */ if(GfxBase->lib_Version >= 37) { /* then allow new genlock modes */}
ASL Requesters
The Release 2 ASL library provides standard file and font requesters. Allocation and use of an ASL requester can be handled by coding a simple subroutine to use the ASL requester if available. Otherwise use fallback code or a public domain requester. By now, many of you have probably coded your own requesters and you may be quite attached to them. In that case, at least give your users the option to use the ASL requester if they wish. By using the ASL requesters, you can provide a familiar interface to your users, gain the automatic benefit of all ASL file requester improvements, and stop maintaining your own requester code.
DOS System(), CreateNewProc(), and CON: Enhancements
If your program currently uses the 1.3 AmigaDOS Execute() or CreateProc() functions, then it is definitely worth conditional code to use their V37 replacements when running under Release 2. The System() function of Release 2 allows you to pass a command line to AmigaDOS as if it had been typed at a Shell window. System() can run synchronously with return values or asynchronously with automatic cleanup and it also sets up a proper stdio environment when passed a DOS filehandle for SYS_Input and NULL for SYS_Output. In combination with enhanced Release 2 "CON:" features, System() can provide a suitable execution environment on either Workbench or a custom screen. The CreateNewProc() function provides additional control and ease in process creation.
"CON:" input and output in custom Intuition screens and windows is now supported. New options in the Release 2 console handler ("CON:") provide the ability to open a "CON:" on any public Intuition screen, or to attach a "CON:" to an existing Intuition window. Additional options can add a close gadget or create an AUTO console window which will only open if accessed for read or write. Add conditional code to use these system-supported methods when running under Release 2 or later versions of the OS. Note that additional "CON:" option keywords can be easily removed under 1.3 at runtime by terminating the "CON:" string with NULL after the window title. Consult The AmigaDOS Manual for additional information on Release 2 "CON:" and DOS features.
The Display Database
The Release 2 graphics library and the Enhanced Chip Set (ECS) provide programmable display modes and enhanced genlock capabilities. Users with Release 2 and ECS may wish to use your application in one of the newer display modes. The Release 2 display database provides information on all of the display modes available with the user's machine and monitor. In addition, it provides useful information on the capabilities and aspect ratio of each mode (DisplayInfo.Resolution.x and .y). A new function named ModeNotAvailable() allows you to easily check if particular modes are available.
The ExtNewScreen structure used with Intuition's OpenScreen() function allows you to specify new display modes with the SA_DisplayID tag and a long word ModeID. The Release 2 graphics library VideoControl() function provides greatly enhanced genlock capabilities for machines with ECS and a genlock. Little conditional code is required to support these features. See the graphics library chapters and Autodocs for more information.
ARexx
Add conditional ARexx capabilities to your program. ARexx is available on all Release 2 machines, and many 1.3 users have purchased ARexx separately. ARexx capability adds value to your product and allows users and vertical market developers to create custom and hybrid applications. Add the ability to control your application externally via ARexx, and internally via ARexx macros. Allow the user to execute ARexx scripts to control other programs, including the ability to pass information from your program to other applications. For more information on adding ARexx functionality to your application, see the Amiga Programmer's Guide to ARexx.
Compatible libraries
Compatible libraries provide a third method for using Release 2 while remaining 1.3-compatible. Some Release 2 libraries are 1.3-compatible and may be distributed with your product if you have a 1.3 Workbench License and an amendment to distribute the additional library.
IFFParse Library
IFFParse is a run-time library which provides low level code for writing, reading, and parsing IFF files. Use of IFFParse library and the new IFF example code modules can significantly reduce your development and debugging time. In addition, the IFFParse code modules provide effortless handing of the clipboard device. See IFFParse Library and the IFF Standard for additional information.
Single Precision IEEE Math Libraries
The Release 2 single precision IEEE math libraries are also compatible with 1.3. These libraries provide single-precision math functions that will use a math coprocessor if available.
Third Party Compatible Libraries
Developers of new code may wish to take advantage of the ease with which a user interface can be created using the Release 2 GadTools and ASL support libraries. These new libraries are not 1.3-compatible but there are some third party development efforts towards providing 1.3-compatible versions of them. You may wish to explore this possibility.
Error Reports
All corrections and bug report should be made by posting a new topic at AmigaOS' support forum. The support forum is located at http://support.amigaos.net