cheesestraws
Well-known member
There don't seem to be much information out there on writing applications for A/UX that aren't just ports from other UNIXes. I've now written a couple of Mac applications that use A/UX facilities—and I've done it more than once, so by YouTube standards that means I'm an expert. Heaven help us all. So, here are some thoughts.
There are really two kinds of "hybrid" application. The first is a UNIX program that calls the Macintosh Toolbox and OS calls. The second is to build a Mac application that makes UNIX system calls. Apple's documentation tries to encourage you to do the former, for fairly good reasons, but in the hobbyist world with small audiences, it's probably a better use of effort to build a Mac application that has optimisations for A/UX (such as using native UNIX sockets instead of MacTCP when on A/UX).
To check whether you can do this, you can just check 'a/ux' in the Gestalt Manager, something like:
Once you know you're running on A/UX, you can make system calls. Apple's own advice on how to do this is "write a C program in A/UX that uses it, build it with cc, disassemble it, then copy and paste the assembler info your project". This does work, but is very tedious. So you'd write something like this:
Then compile it with
Loading this into your disassembler of choice (a modern one, like IDA Pro or Ghidra, can make this considerably less tedious: though note that I've had some funky misparses with IDA) will give you something like this:
which you can then wrap with the appropriate prototype and declarations (taken from the man page) to get something like this. Note that this uses 'long' where the manual page uses 'int' because 'int' on Mac compilers tends to be 16 bits, and on A/UX it's definitely 32:
The shortcut, of course, is to get someone else to do this for you. Apple produced a load of assembly snippets for various calls, which are in the "AUX System Calls" ZIP file attached to this thread. Those are easier than doing the disassembly yourself. For other people who use CodeWarrior, auxsock.zip contains a wrapper for some of the syscalls that I have found useful.
This all works fine right up until you want to use any asynchronous code at all, whereupon a major, but subtle problem raises its head.
Remember that you're sharing a Mac environment with hybrid applications that have their own process contexts. Imagine you have a VBL interrupt in your Mac application, and a hybrid process with its own process is running. When that process is hogging the Mac (say, it has a menu open because the mouse button is down in the menu bar) and your VBL interrupt runs, it runs /in the process context of the hybrid process/. This means that any UNIX resources you have open are not available. So, check your pid in asynchronous code before using things like file descriptors. This is why getpid() is in auxsock.zip.
Any questions that I can pretend to know the answers to? :-D
There are really two kinds of "hybrid" application. The first is a UNIX program that calls the Macintosh Toolbox and OS calls. The second is to build a Mac application that makes UNIX system calls. Apple's documentation tries to encourage you to do the former, for fairly good reasons, but in the hobbyist world with small audiences, it's probably a better use of effort to build a Mac application that has optimisations for A/UX (such as using native UNIX sockets instead of MacTCP when on A/UX).
To check whether you can do this, you can just check 'a/ux' in the Gestalt Manager, something like:
C:
void auxinit(int alertID) {
long auxversion;
OSErr err;
err = Gestalt('a/ux', &auxversion);
if (err != noErr || auxversion < 2) {
StopAlert(alertID, nil);
ExitToShell();
}
}
Once you know you're running on A/UX, you can make system calls. Apple's own advice on how to do this is "write a C program in A/UX that uses it, build it with cc, disassemble it, then copy and paste the assembler info your project". This does work, but is very tedious. So you'd write something like this:
C:
#include sys/types.h
#include sys/stat.h
void main() {}
struct stat buf;
fstat(0, &buf);
}
Then compile it with
$ cc fstat.c
Loading this into your disassembler of choice (a modern one, like IDA Pro or Ghidra, can make this considerably less tedious: though note that I've had some funky misparses with IDA) will give you something like this:
Code:
movea.l 4(sp), a0
move.l 8(sp), d1
moveq #0x7c, d0
trap #0xf
bcs.w err
rts
err:
jmp cerror
cerror:
move.l d0, errno
moveq #-1, d0
movea.l d0, a0
rts
which you can then wrap with the appropriate prototype and declarations (taken from the man page) to get something like this. Note that this uses 'long' where the manual page uses 'int' because 'int' on Mac compilers tends to be 16 bits, and on A/UX it's definitely 32:
C:
long errno = 0;
struct stat
{
// lots of stuff taken from sys/stat.h here
}
asm long fstat(long fd, struct stat* buf) {
movea.l 4(sp), a0
move.l 8(sp), d1
moveq #0x7c, d0
trap #0xf
bcs.w err
rts
err:
jmp cerror
cerror:
move.l d0, errno
moveq #-1, d0
movea.l d0, a0
rts
}
The shortcut, of course, is to get someone else to do this for you. Apple produced a load of assembly snippets for various calls, which are in the "AUX System Calls" ZIP file attached to this thread. Those are easier than doing the disassembly yourself. For other people who use CodeWarrior, auxsock.zip contains a wrapper for some of the syscalls that I have found useful.
This all works fine right up until you want to use any asynchronous code at all, whereupon a major, but subtle problem raises its head.
Remember that you're sharing a Mac environment with hybrid applications that have their own process contexts. Imagine you have a VBL interrupt in your Mac application, and a hybrid process with its own process is running. When that process is hogging the Mac (say, it has a menu open because the mouse button is down in the menu bar) and your VBL interrupt runs, it runs /in the process context of the hybrid process/. This means that any UNIX resources you have open are not available. So, check your pid in asynchronous code before using things like file descriptors. This is why getpid() is in auxsock.zip.
Any questions that I can pretend to know the answers to? :-D