Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "Programming in the Amiga Environment"
Steven Solie (talk | contribs) |
m (Fixed a typo.) |
||
(39 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
{{NeedUpdate}} |
{{NeedUpdate}} |
||
− | == Introduction to Amiga system libraries == |
||
− | |||
− | The Amiga, like other microcomputers, contains a ROM full of routines that make programming the machine easier. The purpose of this book is to show you how to use these routines. Perhaps the best way to learn Amiga programming is by following examples and that is the method used in this book. Before starting though it will be helpful to go over some Amiga fundamentals. This section presents some of the basics that all Amiga programmers need to know. |
||
− | |||
== Programming in the Amiga Environment == |
== Programming in the Amiga Environment == |
||
Line 32: | Line 28: | ||
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. |
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. |
||
+ | {{Note|title=What Every Amiga Programmer Should Know|text=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.}} |
||
− | {| class="wikitable" |
||
− | | ''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 === |
=== Libraries of functions === |
||
− | Most of the routines that make up the Amiga's operating system are organized into groups called libraries. In order to call a function on the Amiga you must first open the library that contains the function. For example, if you want to call the Read() function to read data from disk you must first open the DOS library. |
+ | 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 [[AmigaDOS Introduction|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. |
+ | The system's master library, called [[Exec]], is always open. [[Exec]] keeps track of all the other [[Exec Libraries|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() |
+ | Almost any program you write for the Amiga will have to call the OpenLibrary() and GetInterface() functions. Usage is as follows: |
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | // Global: declare this above main() |
|
+ | struct Library *LibBase; |
||
+ | struct Interface *LibIFace; |
||
− | main() |
+ | int main() |
{ |
{ |
||
− | LibBase = OpenLibrary( |
+ | LibBase = IExec->OpenLibrary("library.name", libversion); |
+ | if(LibBase == NULL) |
||
− | |||
+ | { |
||
− | if(!LibBase) { /* Library did not open, so exit */ } |
||
+ | // Library did not open, so exit. |
||
− | else { /* Library opened, so use its functions */ } |
||
+ | } |
||
+ | else |
||
+ | { |
||
+ | LibIFace = IExec->GetInterface(LibBase, "main", ifaceversion, NULL); |
||
+ | if(LibIFace == NULL) |
||
+ | { |
||
+ | IExec->CloseLibrary(LibBase); |
||
+ | // Could not get Interface, so exit. |
||
+ | } |
||
+ | else |
||
+ | { |
||
+ | // Interface obtained, so use its functions. |
||
+ | } |
||
+ | } |
||
} |
} |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
+ | ; LibBase |
||
− | This is a pointer to the library structure in memory, often referred to as the ''library base''. The library base must be global because the system uses it to handle the library's function calls. The name of this pointer is established by the system (you cannot use any name you want). Refer to the list below for the appropriate name. |
||
+ | : 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 C string that describes the name of the library you wish to open. The list of Amiga library names is given below. |
||
+ | : 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 should be set to the earliest acceptable library version. A value of 0 matches any version. A value of 33 means you require at least version 33, 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). |
||
+ | : 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 |
||
− | The table listed on the next page shows all the function libraries that are currently part of the Amiga system software. Column one shows the name string to use with OpenLibrary(); column two shows the name of the global variable you should use to hold the pointer to the library; column three shows the oldest version of the library still in use. |
||
+ | : 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 |
||
− | Parameters to use with OpenLibrary() |
||
+ | : 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 |
||
− | <table> |
||
+ | : 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). |
||
− | <tbody> |
||
− | <tr class="odd"> |
||
− | <td align="left">'''Library Name'''</td> |
||
− | <td align="left">'''Library Base'''</td> |
||
− | <td align="center">'''Oldest Version In Use'''</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">library.name<math>^\star</math></td> |
||
− | <td align="left">LibBase</td> |
||
− | <td align="center">version</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">asl.library</td> |
||
− | <td align="left">AslBase</td> |
||
− | <td align="center">36</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">commodities.library</td> |
||
− | <td align="left">CxBase</td> |
||
− | <td align="center">36</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">diskfont.library</td> |
||
− | <td align="left">DiskfontBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">dos.library</td> |
||
− | <td align="left">DOSBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">exec.library</td> |
||
− | <td align="left">SysBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">expansion.library</td> |
||
− | <td align="left">ExpansionBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">gadtools.library</td> |
||
− | <td align="left">GadToolsBase</td> |
||
− | <td align="center">36</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">graphics.library</td> |
||
− | <td align="left">GfxBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">icon.library</td> |
||
− | <td align="left">IconBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">iffparse.library</td> |
||
− | <td align="left">IFFParseBase</td> |
||
− | <td align="center">36</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">intuition.library</td> |
||
− | <td align="left">IntuitionBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">keymap.library</td> |
||
− | <td align="left">KeymapBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">layers.library</td> |
||
− | <td align="left">LayersBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">mathffp.library</td> |
||
− | <td align="left">MathBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">mathtrans.library</td> |
||
− | <td align="left">MathTransBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">mathieeedoubbas.library</td> |
||
− | <td align="left">MathIeeeDoubBasBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">mathieeedoubtrans.library</td> |
||
− | <td align="left">MathIeeeDoubTransBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">mathieeesingbas.library</td> |
||
− | <td align="left">MathIeeeSingBasBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">mathieeesingtrans.library</td> |
||
− | <td align="left">MathIeeeSingTransBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">rexxsyslib.library</td> |
||
− | <td align="left">RexxSysBase</td> |
||
− | <td align="center">36</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">translator.library</td> |
||
− | <td align="left">TranslatorBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">utility.library</td> |
||
− | <td align="left">UtilityBase</td> |
||
− | <td align="center">36</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">workbench.library</td> |
||
− | <td align="left">WorkbenchBase</td> |
||
− | <td align="center">33</td> |
||
− | </tr> |
||
− | </tbody> |
||
− | </table> |
||
+ | The table listed below shows all the function libraries that are currently part of the Amiga system software. |
||
− | * 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. |
||
+ | {| class="wikitable" |
||
− | ==== Opening a Library in C ==== |
||
+ | |+ Parameters to use with OpenLibrary() |
||
+ | !Library Name |
||
+ | !Library Base |
||
+ | !Oldest Library Version In Use |
||
+ | !Interface Name:Version |
||
+ | |- |
||
+ | | library.name<sup>*</sup> || 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 |
||
+ | |- |
||
+ | | [[Introduction to Exec|exec.library]] || ExecBase || 50 || main:1 |
||
+ | |- |
||
+ | | [[Expansion Library|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|utility.library]] || UtilityBase || 50 || main:1 |
||
+ | |- |
||
+ | | workbench.library || WorkbenchBase || 50 || main:1 |
||
+ | |} |
||
+ | <sup>*</sup> 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. |
||
− | Call OpenLibrary() to open an Amiga function library. OpenLibrary() returns the address of the library structure (or library base) which you must assign to a specific global system variable as specified in the table above (case is important). |
||
+ | ==== Opening a Library in C ==== |
||
− | If the library cannot open for some reason, the OpenLibrary() function returns zero. Here's a brief example showing how it's used in C. |
||
+ | Here's a brief example showing how OpenLibrary() and GetInterface() are used in C. |
||
+ | <syntaxhighlight> |
||
− | |||
− | <pre> |
||
/* easy.c: a complete example of how to open an Amiga function library 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. |
+ | * In this case the function library is Intuition. Once the Intuition |
− | * function library is open, any Intuition function |
+ | * function library is open and the interface obtains, any Intuition function |
− | * example uses the DisplayBeep() function of Intuition to |
+ | * can be called. This example uses the DisplayBeep() function of Intuition to |
+ | * flash the screen. |
||
− | * With SAS/C (Lattice), compile with lc -L easy.c |
||
*/ |
*/ |
||
+ | #include <proto/exec.h> |
||
− | /* Declare the return type of the functions we will use. */ |
||
+ | #include <proto/intuition.h> |
||
− | struct Library *OpenLibrary(); /* These Exec library functions can be */ |
||
− | void CloseLibrary(); /* called anytime (Exec is always open). */ |
||
+ | struct Library *IntuitionBase; |
||
− | void DisplayBeep(); /* Before using this Intuition function, */ |
||
+ | struct IntuitionIFace *IIntuition; |
||
− | /* the Intuition library must be opened */ |
||
− | struct IntuitionBase *IntuitionBase; /* Get storage for the library base */ |
||
− | /* The base name MUST be IntuitionBase */ |
||
int main() |
int main() |
||
{ |
{ |
||
− | IntuitionBase= |
+ | IntuitionBase = IExec->OpenLibrary("intuition.library", 50); |
+ | // Note it is safe to call GetInterface() with a NULL library pointer. |
||
− | if(IntuitionBase) /* Check to see if it actually opened. */ |
||
+ | IIntuition = (struct IntuitionIFace *)IExec->GetInterface(IntuitionBase, "main", 1, NULL); |
||
− | { /* The Intuition library is now open so */ |
||
− | DisplayBeep(0L); /* any of its functions may be used. */ |
||
− | + | 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. */ |
||
− | else /* The library did not open so return an */ |
||
+ | } |
||
− | { /* error code. The exit() function is */ |
||
+ | |||
− | exit(20); /* not part of the OS, it is part of the */ |
||
+ | // Always drop the interface and close the library if not in use. |
||
− | } /* compiler link library. */ |
||
+ | // Note it is safe to call DropInterface() and CloseLibrary() with NULL pointers. |
||
+ | IExec->DropInterface((struct Interface *)IIntuition); |
||
+ | IExec->CloseLibrary(IntuitionBase); |
||
} |
} |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | |||
− | ==== Opening a Library in Assembler ==== |
||
− | |||
− | Here's the same example written in ''68000'' assembler. The principles are the same as with C: you must always open a library before using any of its functions. However, in assembler, library bases are treated a little differently than in C. In C, you assign the library base you get from OpenLibrary() to a global variable and forget about it (the system handles the rest). In assembler, the library base must always be in register A6 whenever calling any of the functions in the library. |
||
− | |||
− | You get the library base for any library except Exec, by calling OpenLibrary(). For Exec, you get the library base from the longword in memory location 4 ($0000¬†0004). Exec is opened automatically by the system at boot time, and its library base is stored there. |
||
− | |||
− | <pre> |
||
− | ********************************************************************** |
||
− | * A complete ready-to-assemble example of how to open an Amiga function |
||
− | * library in 68000 assembler. In this case the Intuition function library |
||
− | * is opened and one of its functions, DisplayBeep() is called. |
||
− | * |
||
− | * When calling an Amiga function, the library base pointer *must* be in |
||
− | * A6 (the library is free to depend on this). Registers D0, D1, A0 |
||
− | * and A1 may be destroyed by the library, all others will be preserved. |
||
− | * |
||
− | _AbsExecBase EQU 4 ;System pointer to Exec's library base |
||
− | |||
− | XREF _LVOOpenLibrary ;Offset from Exec base for OpenLibrary() |
||
− | XREF _LVOCloseLibrary ;Offset from Exec base for CloseLibrary() |
||
− | XREF _LVODisplayBeep ;Offset from Intuition base for DisplayBeep() |
||
− | |||
− | move.l _AbsExecBase,a6 ;Move exec.library base to a6 |
||
− | lea.l IntuiName(pc),a1 ;Pointer to "intuition.library" string |
||
− | moveq #33,d0 ;Version of library needed |
||
− | jsr _LVOOpenLibrary(a6) ;Call Exec's OpenLibrary() and |
||
− | tst.l d0 ;check to see if it succeeded |
||
− | bne.s open_ok |
||
− | moveq #20,d0 ;Set failure code |
||
− | rts ;Failed exit |
||
− | |||
− | open_ok move.l d0,a6 ;Put IntuitionBase in a6. |
||
− | suba.l a0,a0 ;Load zero into a0 |
||
− | jsr _LVODisplayBeep(a6) ;Call Intuition's DisplayBeep() |
||
− | |||
− | move.l a6,a1 ;Put IntuitionBase into a1 |
||
− | move.l _AbsExecBase,a6 |
||
− | jsr _LVOCloseLibrary(a6) ;Call Exec's CloseLibrary() |
||
− | moveq #0,d0 ;Set return code |
||
− | rts |
||
− | |||
− | IntuiName: dc.b 'intuition.library',0 |
||
− | END |
||
− | </pre> |
||
− | |||
− | The Amiga library functions are set up to accept parameters in certain ''68000'' registers and always return results in data register D0. This allows programs and functions written in assembler to communicate quickly. It also eliminates the dependence on the stack frame conventions of any particular language. |
||
− | |||
− | Amiga library functions use registers, D1, A0 and A1 for work space and use register A6 to hold the library base. Do not expect these registers to be the same after calling a function. All routines return a full 32 bit longword unless noted otherwise. |
||
==== Another Kind of Function Library ==== |
==== Another Kind of Function Library ==== |
||
Line 295: | Line 187: | ||
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. |
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 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 |
+ | 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 ==== |
==== Libraries, Devices and Resources ==== |
||
Line 303: | Line 195: | ||
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. |
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 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.) |
− | [[File:LibFig1-1a.png|center]] |
+ | [[File:LibFig1-1a.png|frame|center|'''Figure 1-1: Amiga System Software Hierarchy''']] |
− | <center> |
+ | <center></center> |
− | Devices and resources are managed by Exec just as libraries are. For more information on devices and resources, see the respective sections. |
+ | Devices and resources are managed by [[Exec]] just as libraries are. For more information on devices and resources, see the respective sections. |
+ | {{Note|title=What Every Amiga Programmer Should Know|text=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.}} |
||
− | {| class="wikitable" |
||
− | | ''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 === |
=== Dynamic memory architecture === |
||
− | Unlike some microcomputer operating systems, the |
+ | 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. |
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. |
||
− | [[File:LibFig1-1b.png]] |
+ | [[File:LibFig1-1b.png|frame|center|Amiga Library Structure and Jump Table]] |
+ | 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. |
||
− | Figure 1-1: Amiga Library Structure and Jump Table |
||
+ | 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 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. |
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. |
||
Line 333: | Line 221: | ||
==== Exec: The System Executive ==== |
==== 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. |
+ | 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 |
+ | 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. |
− | [[File:LibFig1-2.png]] |
+ | [[File:LibFig1-2.png|frame|center|[[Exec]] and the Organization of the Amiga OS]] |
+ | 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. |
||
− | Figure 1-2: Exec and the Organization of the Amiga OS |
||
+ | {{Note|title=What Every Amiga Programmer Should Know|text=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.}} |
||
− | 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. |
||
− | |||
− | <sub>b</sub>oxWhat 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 === |
=== Operating system versions === |
||
− | The Amiga operating system has undergone several major revisions |
+ | The Amiga operating system has undergone several major revisions. The latest revision is Release 4.1 (corresponds to library version 53). |
+ | See the table in the [[Introduction_to_Exec#Libraries_and_Devices|Libraries and Devices]] section for details on which version corresponds to which OS release. |
||
− | {| class="wikitable" |
||
− | ! 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 |
+ | The examples listed throughout this wiki assume you are using Release 4.0 or higher. |
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. |
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. |
||
Line 380: | Line 245: | ||
Exit gracefully and informatively if the required library version is not available. |
Exit gracefully and informatively if the required library version is not available. |
||
− | ==== About Release |
+ | ==== About Release 4 ==== |
− | Release |
+ | Release 4 first appeared on the ''AmigaOne XE''. This initial version corresponds to Kickstart 4.0, system library version number V50. |
+ | {{Note|title=What Every Amiga Programmer Should Know|text=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.}} |
||
− | 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. (Upgrade kits may be obtained from an authorized Commodore service center.) |
||
+ | ==== Two Kinds of Memory ==== |
||
− | {| class="wikitable" |
||
− | |''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. |
||
− | |} |
||
+ | 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 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''. |
||
− | 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: |
||
{| class="wikitable" |
{| class="wikitable" |
||
+ | | ''What Every Amiga Programmer Should Know'': The Classic Amiga has ''two'' kinds of memory: ''Chip'' memory and ''Fast'' memory. Use the right kind. |
||
− | | 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 |
||
|} |
|} |
||
+ | '''NOTE''' |
||
− | 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. |
||
+ | 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. |
||
− | |||
− | |||
− | ==== 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 Amiga running efficiently, the 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 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''. |
||
− | |||
− | {| class="wikitable" |
||
− | | ''What Every Amiga Programmer Should Know'': The Amiga has ''two'' kinds of memory: ''Chip'' memory and ''Fast'' memory. Use the right kind. |
||
− | |} |
||
== About the Examples == |
== About the Examples == |
||
− | For the most part, the examples in this book are written in C |
+ | For the most part, the examples in this book are written in C. |
− | C examples have been compiled |
+ | C examples have been compiled using the standard Software Development Kit (SDK) using GCC. |
− | In general, the examples are also compatible with |
+ | 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: |
||
− | [h] |
||
+ | <pre> |
||
− | <pre>/* Add this before main() to override the default Ctrl-C handling |
||
+ | /* Add this before main() to override the default Ctrl-C handling |
||
* provided in SAS (Lattice) C. Ctrl-C event will be ignored */ |
* provided in SAS (Lattice) C. Ctrl-C event will be ignored */ |
||
int CXBRK ( void ) { return(0); } |
int CXBRK ( void ) { return(0); } |
||
− | int chkabort( void ) { return(0); } |
+ | int chkabort( void ) { return(0); } |
+ | </pre> |
||
− | For Manx Aztec C, replace the above with: |
||
+ | Other changes may be required depending on the example and the C compiler you are using. Most of the C examples do not require any special options and rely on the defaults provided by the SDK. |
||
− | [h] |
||
+ | Except where noted, each example was linked with the standard newlib startup code which provides the following interfaces: IExec, IDOS and IUtility. |
||
− | <pre>/* 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 */</pre> |
||
− | 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): |
||
− | |||
− | <table> |
||
− | <tbody> |
||
− | <tr class="odd"> |
||
− | <td align="right">-b1</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Small data model.</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="right">-cf</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Check for function prototypes.</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="right">-i</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Ignore #include statements that are identical to one already given.</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="right">-s</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Store all literal strings that are identical in the same place.</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="right">-t</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Enable warnings for structures that are used before they are defined.</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="right">-v</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Do not include stack checking code with each function.</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="right">-y</td> |
||
− | <td align="center">=</td> |
||
− | <td align="left">Load register A4 with the data section base address on function entry.</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="right"></td> |
||
− | <td align="center"></td> |
||
− | <td align="left">The -v and -y flags are are generally only needed for parts of the</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="right"></td> |
||
− | <td align="center"></td> |
||
− | <td align="left">program that are called directly by the system such as interrupt</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="right"></td> |
||
− | <td align="center"></td> |
||
− | <td align="left">servers, subtasks, handlers and callback hook functions.</td> |
||
− | </tr> |
||
− | </tbody> |
||
− | </table> |
||
− | |||
− | 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 Commodore 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). |
||
− | |||
− | The ''68000'' assembly language examples have been assembled under the Innovatronics CAPE assembler V2.x, the HiSoft Devpac assembler V1.2, and the Lake Forest Logic ADAPT assembler 1.0. No substantial changes should be required to switch between assemblers. |
||
== General Amiga Development Guidelines == |
== General Amiga Development Guidelines == |
||
− | + | This sections presents specific guidelines that all Amiga programmers must follow. Some of these guidelines are for advanced programmers. |
|
+ | * 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. |
||
− | <ul> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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).</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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 $F0¬†0000¬†-¬†$FF¬†FFFF 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.</p></li> |
||
− | <li><p>Never assume library bases or structures will exist at any particular memory location. The only absolute address in the system is $0000¬†0004, 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.</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>Never access shared data structures directly without the proper mutual exclusion (locking). Remember that other tasks may be accessing the same structures.</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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:</p> |
||
− | <p>[h]</p> |
||
− | <pre> signals = (ULONG)Wait( (1<<windowPtr->UserPort->mp_SigBit) | |
||
− | (1<<consoleMsgPortPtr->mp_SigBit) );</pre></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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().</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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).</p> |
||
− | <p>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.</p> |
||
− | <p>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.</p> |
||
− | <p>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.</p></li> |
||
− | <li><p>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).</p></li> |
||
− | <li><p>Always obey structure conventions!</p> |
||
− | <ul> |
||
− | <li><p>All non-byte fields must be word-aligned. Longwords should be longword-aligned for performance.</p></li> |
||
− | <li><p>All address pointers should be 32 bits (not 24 bits). Never use the upper byte for data.</p></li> |
||
− | <li><p>Fields that are not defined to contain particular initial values must be initialized to zero. This includes pointer fields.</p></li> |
||
− | <li><p>All reserved or unused fields must be initialized to zero for future compatibility.</p></li> |
||
− | <li><p>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.</p></li> |
||
− | <li><p>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.</p></li></ul> |
||
− | </li></ul> |
||
+ | * 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). |
||
− | === For 68010/68020/68030/68040 compatibility === |
||
+ | * 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. |
||
− | Special care must be taken to be compatible with the entire family of ''68000'' processors: |
||
+ | * 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. |
||
− | <ul> |
||
− | <li><p>Do not use the upper 8 bits of a pointer for storing unrelated information. The ''68020'', ''68030'', and ''68040'' use all 32 bits for addressing.</p></li> |
||
− | <li><p>Do not use signed variables or signed math for addresses.</p></li> |
||
− | <li><p>Do not use software delay loops, and do not make assumptions about the order in which asynchronous tasks will finish.</p></li> |
||
− | <li><p>The stack frame used for exceptions is different on each member of the ''68000'' family. The type identification in the frame must be checked! In addition, the interrupt autovectors may reside in a different location on processors with a VBR register.</p></li> |
||
− | <li><p>Do not use the MOVE SR,<dest> instruction! This ''68000'' instruction acts differently on other members of the ''68000'' family. If you want to get a copy of the processor condition codes, use the Exec library GetCC() function.</p></li> |
||
− | <li><p>Do not use the CLR instruction on a hardware register which is triggered by Write access. The ''68020'' CLR instruction does a single Write access. The ''68000'' CLR instruction does a Read access first, then a Write access. This can cause a hardware register to be triggered twice. Use MOVE.x¬†#0,¬†<address> instead.</p></li> |
||
− | <li><p>Self-modifying code is strongly discouraged. All ''68000'' family processors have a pre-fetch feature. This means the CPU loads instructions ahead of the current program counter. Hence, if your code modifies or decrypts itself just ahead of the program counter, the pre-fetched instructions may not match the modified instructions. The more advanced processors prefetch more words. If self-modifying code must be used, flushing the cache is the safest way to prevent troubles.</p></li> |
||
− | <li><p>The ''68020'', ''68030'' and ''68040'' processors all have instruction caches. These caches store recently used instructions, but do not monitor writes. After modifying or directly loading instructions, the cache must be flushed. See the Exec library CacheClearU() Autodoc for more details. If your code takes over the machine, flushing the cache will be trickier. You can account for the current processors, and hope the same techniques will work in the future:</p> |
||
− | <pre> CACRF_ClearI EQU $0008 ;Bit for clear instruction cache |
||
− | ; |
||
− | ;Supervisor mode only. Use this only if you have taken over the |
||
− | ;machine. Read and store the ExecBase processor AttnFlags flags at |
||
− | ;boot time, call this code only if the "68020 or better" bit was set. |
||
− | ; |
||
− | ClearICache: dc.w $4E7A,$0002 ;MOVEC CACR,D0 |
||
− | tst.w d0 ;movec does not affect CC's |
||
− | bmi.s cic_040 ;A 68040 with enabled cache! |
||
− | ori.w #CACRF_ClearI,d0 |
||
− | dc.w $4E7B,$0002 ;MOVEC D0,CACR |
||
− | bra.s cic_exit |
||
− | cic_040: dc.w $f4b8 ;CPUSHA (IC) |
||
− | cic_exit:</pre></li></ul> |
||
+ | * 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. |
||
− | === Hardware programming guidelines === |
||
+ | * 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. |
||
− | 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: |
||
+ | * 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. |
||
− | * 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 Commodore 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. |
||
+ | * Never access shared data structures directly without the proper mutual exclusion (locking). Remember that other tasks may be accessing the same structures. |
||
− | === Additional assembler development guidelines === |
||
+ | * 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. |
||
− | If you are writing in assembly language there are some extra rules to keep in mind in addition to those listed above. |
||
+ | * 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: |
||
− | * Never use the TAS instruction on the Amiga. System DMA can conflict with this instruction‚Äôs special indivisible read-modify-write cycle. |
||
+ | signals = (ULONG)Wait( (1<<windowPtr->UserPort->mp_SigBit) | |
||
− | * System functions must be called with register A6 containing the library or device base. Libraries and devices assume A6 is valid at the time of any function call. Even if a particular function does not currently require its base register, you must provide it for compatibility with future system software releases. |
||
+ | (1<<consoleMsgPortPtr->mp_SigBit) ); |
||
− | * Except as noted, system library functions use registers D0, D1, A0, and A1 as scratch registers and you must consider their former contents to be lost after a system library call. The contents of all other registers will be preserved. System functions that provide a result will return the result in D0. |
||
− | * Never depend on processor condition codes after a system call. The caller must test the returned value before acting on a condition code. This is usually done with a TST or MOVE instruction. |
||
+ | * 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. |
||
− | == 1.3 Compatibility Issues == |
||
+ | * 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(). |
||
− | 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. |
||
+ | * 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. |
||
− | === Design decisions === |
||
+ | * 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). |
||
− | 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. |
||
+ | * 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. |
||
− | 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. |
||
+ | * 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. |
||
− | 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.'' |
||
+ | * 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. |
||
− | There are three basic methods that will allow you to take advantage of enhanced Release 2 features while remaining 1.3 compatible: |
||
+ | * 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). |
||
− | * Transparent Release 2 Extensions |
||
− | * Conditional Code |
||
− | * Compatible Libraries |
||
+ | Always obey structure conventions! |
||
− | ==== Transparent Release 2 Extensions ==== |
||
+ | * All non-byte fields must be word-aligned. Longwords should be longword-aligned for performance. |
||
− | 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. |
||
+ | * 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. |
||
− | [h] |
||
+ | * 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. |
||
− | <table> |
||
− | <tbody> |
||
− | <tr class="odd"> |
||
− | <td align="left"></td> |
||
− | <td align="left"></td> |
||
− | <td align="left">'''Flag'''</td> |
||
− | <td align="left"></td> |
||
− | <td align="left"></td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">'''Original'''</td> |
||
− | <td align="left">'''Extended'''</td> |
||
− | <td align="left">'''Field'''</td> |
||
− | <td align="left">'''Flag Bit'''</td> |
||
− | <td align="left">'''Defined In'''</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">NewScreen</td> |
||
− | <td align="left">ExtNewScreen</td> |
||
− | <td align="left">Type</td> |
||
− | <td align="left">NS_EXTENDED</td> |
||
− | <td align="left"><intuition/screens.h></td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">NewWindow</td> |
||
− | <td align="left">ExtNewWindow</td> |
||
− | <td align="left">Flags</td> |
||
− | <td align="left">WFLG_NW_EXTENDED</td> |
||
− | <td align="left"><intuition/intuition.h></td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">Gadget</td> |
||
− | <td align="left">Gadget</td> |
||
− | <td align="left">Flags</td> |
||
− | <td align="left">GFLG_STRINGEXTEND</td> |
||
− | <td align="left"><intuition/intuition.h></td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">PropInfo</td> |
||
− | <td align="left">PropInfo</td> |
||
− | <td align="left">Flags</td> |
||
− | <td align="left">PROPNEWLOOK</td> |
||
− | <td align="left"><intuition/intuition.h</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">TextAttr</td> |
||
− | <td align="left">TTextAttr</td> |
||
− | <td align="left">tta_Style</td> |
||
− | <td align="left">FSF_TAGGED</td> |
||
− | <td align="left"><graphics/text.h></td> |
||
− | </tr> |
||
− | </tbody> |
||
− | </table> |
||
+ | * 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. |
||
− | 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. |
||
+ | === Hardware programming guidelines === |
||
− | The enhancements available through these extensions include: |
||
+ | 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: |
||
− | [h] |
||
+ | * 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. |
||
− | <table> |
||
− | <tbody> |
||
− | <tr class="odd"> |
||
− | <td align="left">Screen:</td> |
||
− | <td align="left">Overscan, 3D Look (SA_Pens), public screens, PAL/NTSC, new modes</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">Window:</td> |
||
− | <td align="left">Autoadjust sizing, inner dimensions, menu help</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">Gadget:</td> |
||
− | <td align="left">Control of font, pens, and editing of string gadgets</td> |
||
− | </tr> |
||
− | <tr class="even"> |
||
− | <td align="left">PropInfo:</td> |
||
− | <td align="left">Get 3D Look proportional gadgets when running under Release 2</td> |
||
− | </tr> |
||
− | <tr class="odd"> |
||
− | <td align="left">TTextAttr:</td> |
||
− | <td align="left">Control width of scaled fonts</td> |
||
− | </tr> |
||
− | </tbody> |
||
− | </table> |
||
+ | * All custom chip registers are Read-only or Write-only. Do not read Write-only registers, and do not write to Read-only registers. |
||
− | 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. |
||
+ | * 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. |
||
− | 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). |
||
+ | * 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. |
||
− | 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. |
||
+ | * 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. |
||
− | ==== Conditional 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. |
||
− | 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: |
||
− | |||
− | [h] |
||
− | |||
− | <pre>/* 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 */}</pre> |
||
− | ==== 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'', a publication by Commodore Applications and Technical Support (CATS). Contact your local Commodore support organization for information on ordering this book. |
||
− | |||
− | === 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|IFFParse Library]] and the [[IFF_Standard|IFF Standard]] for additional information. |
||
− | + | === 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. |
||
− | 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. |
||
+ | 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. |
||
+ | While the look of windows and gadgets can be changed by the user, the default look is similar to this: |
||
− | ==== Third Party Compatible Libraries ==== |
||
+ | [[File:About.png]] |
||
− | 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 == |
== Error Reports == |
Latest revision as of 20:00, 20 March 2016
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
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(LibBase == NULL) { // Library did not open, so exit. } else { LibIFace = IExec->GetInterface(LibBase, "main", ifaceversion, NULL); if(LibIFace == NULL) { IExec->CloseLibrary(LibBase); // 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. The latest revision is Release 4.1 (corresponds to library version 53).
See the table in the Libraries and Devices section for details on which version corresponds to which OS release.
The examples listed throughout this wiki assume you are using Release 4.0 or higher.
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 4
Release 4 first appeared on the AmigaOne XE. This initial version corresponds to Kickstart 4.0, system library version number V50.
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. |
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 Classic 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); }
Other changes may be required depending on the example and the C compiler you are using. Most of the C examples do not require any special options and rely on the defaults provided by the SDK.
Except where noted, each example was linked with the standard newlib startup code which provides the following interfaces: IExec, IDOS and IUtility.
General Amiga Development Guidelines
This sections presents specific guidelines that all Amiga programmers must follow. Some of these guidelines are for advanced programmers.
- 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:
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.
While the look of windows and gadgets can be changed by the user, the default look is similar to this:
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