Category Archives: Developer Info

Yet Another AmigaOS Core Developer

Please welcome Fredrik Wikström as our newest AmigaOS core developer.

Fredrik has been busy on the Amiga scene for many years and has produced a lot of very useful software. More information about Fredrik’s work can be found on his web site.

Given Fredrik’s exemplary track record, I think we can all expect some very good things from him.

If you have used Fredrik’s software in the past, please also consider a donation to show your appreciation. If bounties are more your thing, he has been working on a few at Open Amiga.

Steven Solie
AmigaOS Development Team Lead

New AmigaOS Core Developers

Please welcome our newest AmigaOS core developers: Alex Carmona and Lyle Hazelwood.

Alex has been a dedicated beta tester and programmer for many years. He has decided to take the leap to OS development. Now he can fix all those bugs he has been filing…

Lyle took the plunge and purchased an AmigaOne X-1000 system. He has been a very active developer ever since. More information about Lyle can be found at his web site.

Their names have already been added to the credits page on the AmigaOS web site. You can also find both of them actively helping people on the AmigaOS support forum.

Memory Page Locking Confusion

With the introduction of the AmigaOS Documentation Wiki and the uploading of the latest Amiga ROM Kernel Reference Manuals we have been given the opportunity to bring the Amiga Operating System’s documentation up to date. Although not complete, several dedicated volunteers have been slowly improving on that documentation.

One of the most confusing changes to the API has proven to be Exec’s new LockMem() and UnlockMem() function calls. These functions are use to lock and unlock memory pages. Normally, a programmer should not have to worry about memory pages nor worry about what the swapper is doing. Such details should be left to the operating system. However, in special circumstances (e.g. device drivers) it may be necessary to use LockMem() and UnlockMem().

What really confused programmers is the fact that we advocated the use of UnlockMem() even when the programmer did not explicitly call LockMem(). The issue was primarily centered on MEMF_SHARED memory which, by default, is automatically locked. That in itself isn’t really a problem. Add in the fact MEMF_ANY memory is translated to MEMF_SHARED memory and you have the potential for a lot of locked memory pages and thus ineffective use of system memory.

AmigaOS programmers are always trying to be helpful. Pretty much everyone jumped on the bandwagon and declared this API change as a travesty of enormous consequence. Why didn’t the FreeVec() function unlock the memory for them? Why must UnlockMem() be provided the size of the memory to unlock? They were very close to rioting.

After much discussion and fact gathering, the AmigaOS development team have officially changed our minds. The new advice is to never call UnlockMem() unless you explicitly called LockMem() in the first place. The wiki has already been updated to reflect this.

AmigaOS programmers are also rather attentive. “What happens to those implicitly locked memory pages when I don’t call UnlockMem()?” The truth of the matter is that some pages will actually be leaked. That is, some of those memory pages will not be moveable and thus not swappable. This does not mean the memory is not available to the system. This means the underlying memory pages cannot be swapped.

Now before resuming the riots, please take a moment and think about this. The system has been running fine for years now without programmers calling UnlockMem() on implicitly locked memory. We only recently explained this on the new documentation wiki. Something deeper is indeed going on here.

Without going into all the internal details, the system will automatically unlock memory that has been freed even if you forgot to call UnlockMem() on that memory. This is not true for every memory allocation but it is true for the majority of the memory in the system. This is why everything has been running fine for years now.

So what is going to happen to those lonely locked pages that can’t be automatically unlocked? The plan is to modify Exec’s discrimination between MEMF_SHARED and MEMF_PRIVATE memory. They will be moving to their own page caches. This effectively eliminates the problem entirely without a single line of code changed in your applications.

We are very sorry for the confusion. In the end, we figured it all out and I think this new advice (only call UnlockMem() if you called LockMem()) is very easy for programmers to follow.

Steven Solie
AmigaOS Development Team Lead

Xena – Signs of Life

The first tools for programming Xena (no, not the Warrior Princess) are now ready for distribution.

The XMOS XS1-L2 chip built into each AmigaOne X1000 with a unique local bus connected directly to the PA6T.

These tools provide the ability to load programs directly into the the Xena chip as well as monitor most of the internal registers as the code is executed.

Unlike other XTAG devices, no adapter board, USB port, or JTAG cables are needed. We can access all of this directly from the AmigaOS command line.

In addition to the new tools, a new AmigaOS resource named xena.resource is included to arbitrate access to the new hardware features.

I have also included a simple .xe file that will flash two LEDs on the AmigaOne X1000 board, as a “proof of programming” demonstration.

These tools were ported from code provided by Segher Boessenkool. Please be sure to thank him if you see him around the XCore Exhange Forums.

The tools are available now and can be downloaded directly from Hyperion’s web site by all registered X1000 users. The new tools and documentation will be added to a future release of the AmigaOS SDK as well.

Information on XMOS chips, compilers and other tools are available at their web site.
Excellent community resources may be found at the XCore Exchange.

I hope these tools provide a beginning into the exploration of these Xciting new features.

For more information about me please visit my web site.

LyleHaze

Introducing the AmigaOS Documentation Wiki

The AmigaOS Documentation Wiki is now available.

This new wiki is meant to fill a gap which we have been struggling with since the launch of the AmigaOS 4.0 project: proper user and developer documentation.

This wiki is using the most up to date, original AmigaOS documentation we could obtain permission to use as a baseline. It is true some of the information is rather dated and/or obsolete. But a majority of the information still applies to AmigaOS 4.0 and beyond. In my opinion, it is better to start with something rather than be staring at a mostly blank wiki.

We are also seeking to reestablish AmigaOS related standards and registries. The official SANA-II standard can now be found here. The IFF FORM and chunk registry is now here. The classic Amiga Manufacturer ID registry is here. Special thanks to Neil Cafferkey for the SANA-II wireless extension and James Jacobs for taking care of the registries all these years.

Editing rights to the wiki are currently limited to registered users only. We are looking for volunteers from the Amiga community to help out. If you would like to contribute in any way, please contact us using the contact form on the AmigaOS web site. Any help would be greatly appreciated!

Steven Solie
AmigaOS Development Team Lead

Where Credit is Due

The Amiga’s Operating System (AmigaOS) has been around a long time and dates back to the release of the Amiga 1000 in 1985.

Many people have contributed to AmigaOS and you can see a (hopefully) complete list of those individuals here.

Special thanks goes out to Olaf Barthel for providing the initial list based on the actual source code commits. Recent contributions have come in from Robert “RJ” Mical and Bryce Nesbitt.

A lot of the source code has been changed and/or replaced over the years but many components and their original designs still live on in AmigaOS 4.1 to this day. Expect this list of credits to keep on growing.

The Right Tool for the Job

For any task, using the right tool for the job is always a crucial matter. This applies to driving a nail into a wall as much as developing software. And while nobody would ever try to use a glass bottle for the nail, the tools of the trade of the software developer are a bit more abstract (and sometimes, more brittle too).

Shared != Shared

On AmigaOS the word “shared” is used in two major contexts: Shared Library, and Shared Object. Both are tools for sharing code between applications. However, they have very different methods for doing this, and with that comes a very different approach to using them.

Let’s first look at what they are.

Shared Libraries

Since the early days of AmigaOS, shared libraries have been a means of sharing code and, to a certain degree, data between multiple users. A shared library is, essentially, a structure in memory called the Library Base, and one or more jump tables to functions that are to be shared. Since Version 4.0 of the OS, these jump tables are called Interface, and although their use differs slightly from their setup in AmigaOS 3.9 and earlier, the principles are the same. A program intending to use a library has to do two steps in order to perform any calls into that library:

  • It has to open the library by calling Exec’s OpenLibrary call.
  • It has to obtain at least one interface from the library by calling GetInterface.

The latter step was not needed on the classic AmigaOS 3.x, but has opened up a host of new possibilities on AmigaOS 4.0 (we’ll talk more about that in a later article).

Interfaces, like the classic AmigaOS 3.x jump tables, are a collection of function pointers in a structure. Calling a function in an interface usually involves knowing the offset of that function. We typically call a function like this:

struct Library *library = IExec->OpenLibrary("foo.library", 0);

The variable IExec contains the interface. The OpenLibrary call is a member of the interface. During compile time, the compiler will calculate the offset of the member and generate appropriate code for that. The code will load the IExec interface pointer into a register, load the CTR register with the address at the specified offset, and branch into the routine using the bctrl mnemonic.

The process relies on a few factors. First of all, it requires to have the library open and have the interface ready. It also relies on the fact that an interface, once written, will at most be extended at the end. It will never be possible to remove functions from the interface (at least it will always have to have a ‘dummy’ entry) nor will it be possible to re-order functions.

Data access is done via the library’s base pointer. The implementer might chose to store user-accessible data within the library base and document (at least part) of it for public access. Since this is a compile-time decision to make, again, the organization of data, just like the organization of functions, must not change once it has been published (unless the library base data is private).

Shared Objects

Shared Objects are a relatively recent addition to AmigaOS 4. They work radically different from the traditional shared libraries. A shared object is, as its name implies, somewhat reminiscent of an object file that is used during compilation of a program. In essence, a shared object allows a program to defer complete and final linking of the program until such time as the program is actually executed on the target user’s machine. This means that some symbols in the program remain unresolved until such time as the program is run.

As a matter of fact, even then the symbols might still remain undefined if a feature called Lazy Binding is active. In essence, Lazy Binding delays the linkage of a function until the very moment it is called for the first time. So, suppose you have a function MyGreatFunc that you call. If Lazy Binding is active, the call will jump into a routine called .resolv within elf.library which performs a symbol look-up on the name of the function and, if it finds it, overwrites the call to .resolv with a call to MyGreatFunc which is located somewhere in the shared objects that are bound to this program.

From the programmer’s point of view, though, there is no difference in whether the program was linked completely (statically) during the development cycle, or dynamically during load time or run time. In essence, the programmer can use a shared object like he uses any other object file in his program. This includes referencing data in the shared object file. Even more, the shared object file can reference data and code in any other shared object file bound to the program, and in the program itself.

In fact, this is a common case. If a program uses a shared object implementation of the standard C library, the start-up code in the shared object file will usually reference and call the program’s main function. This works completely transparent for both; there is no need to open the shared object, it just needs to be specified during linking.

Shared Objects can do even more. There is a set of functions to dynamically load shared objects during runtime of the program, and to look up symbols in the program or any of the loaded shared objects, including those that have been loaded at runtime.

Don’t use the Bottle!

Looking at the above, it seems natural to want to use shared objects instead of shared libraries. The shared objects seem to have so many advantages over the traditional AmigaOS shared libraries, so why would you want to use shared libraries in the first place?

The answer is simple: The flexibility and power of shared objects comes at a cost. Depending on what you do, these costs can be quite substantial:

  1. Shared Objects, in their current implementation as of AmigaOS 4.1 Update 3, are not really shared. Each program using them will load the file again into memory, completely, including all code and data associated with the file. This means that e.g. a shared object version of libc will load all math functions, all system functions, in fact, everything, even if the program only ever uses printf.
  2. Shared Objects are not versioned. While you can specify a minimum library version when you open a shared library to make sure that you get a guaranteed set of functionality, shared objects are provided “as-is” and do not have this kind of information.
  3. Shared Objects require a certain amount of infrastructure up and running. Therefore they cannot be used for Kickstart modules, or device drivers.

Especially point 2 is an issue, in theory and in practice. Some libraries that are implemented as shared objects carry their own versioning information, or provide a call to find out what version a program is using, but this is not standardized in any way, and therefore a shared object might or might not implement such a mechanism. A lot of systems using shared object exclusively end up with something commonly referred to as “Dll Hell” – meaning the system has to provide a wide variety of different versions of the same library, mostly encoded in their file name. If you look at the average Linux distribution, you will see what this means. This here is an example from my local network server:

-rw-r--r-- 1 root root  9156 2009-04-10 18:29 libgmodule-2.0.a
lrwxrwxrwx 1 root root    26 2010-05-04 13:05 libgmodule-2.0.so -> libgmodule-2.0.so.0.2000.1
lrwxrwxrwx 1 root root    26 2009-06-02 12:48 libgmodule-2.0.so.0 -> libgmodule-2.0.so.0.2000.1
-rw-r--r-- 1 root root 13644 2009-04-10 18:29 libgmodule-2.0.so.0.2000.1

As you can see, there are four “versions” of the libgmodule library, two of which are soft links to provide somewhat generic names. If this were AmigaOS, you would simply have one gmodule.library file, and nothing more.

Another issue is different “builds” of the same shared object library. For example, suppose a library that can be used to load image files. Depending on the facilities of the original compiler of the library, he might have decided to compile the library to include support for PNG and JPEG but not GIF. They end up with a shared object file called libImageLoad.so which, for all intend and purpose, looks like libImageLoad.so from another programmer. The other programmer might however have decided to use GIF as an image format and has compiled his copy of libImageLoad.so with GIF support enabled. A user trying to run this program on a system that only has the first version installed will not work (at best, the program will not load because of the missing symbols). The decision on which libImageLoad.so to install/keep  is left to the user – not a good idea.

Of course, this is still somewhat possible for AmigaOS shared libraries as well. A bad practice in the past has been to always install libraries in to the LIBS: drawer, even if the library was only ever used by a specific program. That lead to situations where two programs would install a library of identical name into LIBS:, and only one program would still work.

Glass Hammers and Steel Bottles

So, this leaves the question as to what kind of tool to use in what circumstance. In spite of their individual shortcomings, both shared libraries and shared objects provide a lot of power if used right.

Shared Objects are primary intended for porting of programs from other systems. Any sufficiently large or complex program from Linux or Windows will, in one way or the other, rely on shared code, for various reasons. Usually, this type of code sharing does not translate very well (if at all) to the AmigaOS shared library model. Using shared objects for such projects is almost mandatory.

Shared Objects have other possible uses on AmigaOS, even for new, native development. As an example, a concept like plugins or overlays can be easily and efficiently implemented using shared objects. Anything that is specific to an application and does not install on a global scope is probably easier to do with shared objects, more so since the compiler has intrinsic support for them (as opposed to AmigaOS shared libraries that need to be more or less hand-crafted).

For almost any other purpose, it is recommended to not use shared objects. If you are developing a library, chances are good that you should use a library for that purpose. AmigaOS shared libraries have limitations that are easy to work around in newly developed code. They offer a solid framework for versioning and an escape from other systems’ “Dll Hell”. They are independent on the compiler used, and as a rule, their limitation will force the programmer into a better designed interface. Since AmigaOS 4.0, the Interface concept allows for a strict versioning, meaning that changing the layout of an interface is even possible if they have a different version number, and GetInterface can retrieve a specific version of an interface if the library still implements it.

Closing Words

This post has tried to shed some light on a question that has confused some developers for a while. It tried to give a broad overview of how the different mechanisms work, and when to chose which method for implementing a task. AmigaOS 4.x and beyond tries to give you, the programmer, the tools required to build, but choosing the right tool is still your job.

Oh yeah, and we’re not responsible if you hit your thumb ;)