Copyright (c) Hyperion Entertainment and contributors.

AmiWest Lesson 2

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

AmiWest Lesson 2: Fundamentals

Basic Types

It is important to under at least the basic types when programming. The following table summarizes the basic types used in AmigaOS as compared to standard C and C++ types:

AmigaOS Types
Type Deprecated Type(s) C C++
uint64 none uint64_t uint64_t
int64 none int64_t int64_t
uint32 ULONG or LONGBITS or CPTR uint32_t uint32_t
int32 LONG int32_t int32_t
uint16 UWORD or WORDBITS or USHORT or UCOUNT or RPTR uint16_t uint16_t
int16 WORD or SHORT or COUNT int16_t int16_t
uint8 UBYTE or BYTEBITS char or unsigned char unsigned char
int8 BYTE signed char signed char
STRPTR none char* char*
CONST STRPTR n/a char* const x char* const x
CONST_STRPTR n/a const char* const char*
CONST CONST_STRPTR n/a const char* const const char* const
APTR none void* void*
CONST APTR none void* const x void* const x
CONST_APTR none const void* const void*
CONST CONST_APTR none const void* const const void* const
float32 FLOAT float float
float64 DOUBLE double double
BOOL none int16 int16
TEXT none char char
NULL none 0L (void*)0L
BPTR none int32_t int32_t
BSTR none int32_t int32_t
ZERO none (BPTR)0 (BPTR)0

Static versus Dynamic Linking

The different between static and dynamic linking can best be explained with an example.

#include <stdio.h>
 
int main()
{
 printf("Hello, world\n");
 return 0;
}

Here is "hello" created using static linking:

gcc -mcrt=clib2 -N -o hello hello.c -Wl,--cref,-M,-Map=hello.map
strip hello

The "hello" executable is roughly 34964 bytes in size. The "hello.map" file contains the linker map which shows you exactly what pieces of code have been pulled in from where to create that executable.

Here is "hello" created using dynamic linking:

gcc -mcrt=newlib -N -o hello hello.c -Wl,--cref,-M,-Map=hello.map
strip hello

The "hello" executable is now 5488 bytes in size and the "hello.map" file is substantially simpler as well.

Note
The -N switch is used to work around a feature in the current GCC toolset. The problem has been fixed but a new compiler is not yet generally available. See Myth #2: AmigaOS binaries are fat for more details.

It is important to remember that each program is still essentially the same size. In fact, the dynamically linked program may even be larger. The reason is that the same amount of code is still used. The difference is that the dynamically linked executable is sharing code with other executables. The statically linked executable is not sharing code and thus occupies more disk space.

A statically linked program is a self-contained unit which generally has no extraneous external dependencies. Any dependency problems will show up when linking the executable.

A dynamically linked program requires external libraries in order to function. If any of those libraries are missing or the wrong version, your program will fail at runtime and not when you compile it.

For programmers, it is very important to know where your libraries are coming from and what their limitations are. Just assuming you can mix and match static and dynamic libraries at will is foolish. Always try to understand what each library does and what caveats apply to its usage.

Libraries and Interfaces

AmigaOS provides hundreds of functions which are split up into different shared libraries. Each shared library is further split up into different interfaces. Much more detailed information about Libraries and Interfaces can be found in Exec Libraries.

The following code can be used to open interfaces:

struct Interface* try_open_iface_name(CONST_STRPTR libname, uint32 libver, CONST_STRPTR ifacename)
{
	struct Library* base = IExec->OpenLibrary(libname, libver);
	if ( base != 0 )  {
		struct Interface* iface = IExec->GetInterface(base, ifacename, 1, 0);
 
		if ( iface != 0 )  {
			return iface;
		}
 
		IExec->CloseLibrary(base);
	}
 
	return 0;
}
 
struct Interface* open_iface_name(CONST_STRPTR libname, uint32 libver, CONST_STRPTR ifacename)
{
	struct Interface* iface = try_open_iface_name(libname, libver, ifacename);
 
	if ( iface == 0 )  {
		IDOS->Printf("Can't open %s version %lu interface %s\n", libname, libver, ifacename);
	}
 
	return iface;
}
 
struct Interface* open_iface(CONST_STRPTR libname, uint32 libver)
{
	return open_iface_name(libname, libver, "main");
}

The following code can be used to close an interface and the corresponding library:

void close_iface(struct Interface* iface)
{
	if ( iface != 0 )  {
		struct Library* lib = iface->Data.LibBase;
		IExec->DropInterface(iface);
		IExec->CloseLibrary(lib);
	}
}

Exec Interfaces are called using a special syntax supported by the GNU GCC compiler included with the SDK:

IExec->DebugPrintF("Hello, world\n");

Using a regular compiler this would translate into:

IExec->DebugPrintF(IExec, "Hello, world\n");

Notice how the first argument to the function call is 'hidden' when using the modified GNU GCC compiler. This becomes especially important when trying to understand error message from the compiler. For example, the following code is invalid:

#include <proto/exec.h>
 
int main()
{
	IExec->DebugPrintF(42);
	return 0;
}
gcc -o args args.c
args.c: In function 'main':
args.c:5: warning: passing argument 2 of 'IExec->DebugPrintF' makes pointer from integer without a cast

Notice how the compiler is referring to "argument 2" even though there is only one argument in the call to IExec->DebugPrintF(). This is a side effect of the 'hidden' IExec argument referred to earlier.

Shared Objects

Tasks and Processes

Code to display Tasks and Processes.

Synchronization Primitives