web space | website hosting | Business Web Hosting | Free Website Submission | shopping cart | php hosting

PowerPC Support In C Or C++

by Steffen Haeuser

This article originally appeared in two issues of our Amiga publication Amiga Energy. As the new Amiga One is based around a PowerPC processor, we thought that it might be interesting to re-print this feature here.

What Steffen tried to do was to encourage developers of existing Amiga products to add support for the PowerPC processor to help persuade Amiga users to upgrade their processors as there would be more software available that made use of it.

Something of note is that the writer of this article, Steffen Haeuser, is currently part of Hyperion, developers for the Amiga One who are involved in writing a number of games for the launch of the machine, as well as being active in developing OS4.0!

We are bringing you the first part of this article here, and we’ll be adding the second half shortly as soon as we edit the file!

* * *

Principally PPC Development in C/C++ runs in 5 phases:

1) Rewrite all 68k ASM Stuff in C

2) Adapt Source to ANSI/StormC

3) Adapt to PPC

4) Contextswitch-Optimizing

5) Further Adaptions

Contrary to what you might believe, 3) is only a very small step, the big step is 2). And yes, you can do this already, even if you do not own a PPC, mainly. I will explain the different steps of development now in a more detailed way.

It has to be outlined, that it is advised to do steps 1)/2) already while developing a 68k version, even if at first no PPC Version is planned. It will simplify the PPC Development much, and it in fact does not need too much extra work...

It has also to be noted that things are not that easy using the PPC software from phase 5. This is a special feature of the WarpOS software that makes things easier. I won't discuss rewriting 68k ASM to C source here, you should be able to do this yourselves.

2) Adapt Source to ANSI/StormC

The most work is not the adaption to PPC, but the adaption from SAS/C or GNU C to StormC. StormC is a strict ANSI compiler. Because of that it knows only Standard-C-Functions that are contained in the ANSI-Standard. Some of the not-supported functions can be emulated using the not-yet-released UnixLib, though. It should be noted, that, if your program compiles on SAS/C with the STRICT ANSI mode set. You can think of StormC as a compiler that ALWAYS runs in STRICT ANSI mode.

The following SAS/C Functions are not contained in ANSI, and thus not supported by StormC (most of them are quite exotic functions, and it is possible that you do not even know a lot of them, even if you are a proficient C Coder):

astcsma, isascii, iscsym, iscsymf, toascii, scdir, stcpm, stcpma, stcsma, stccpy, stpcpy, stcis, stcisn, stclen, stpbrk, stpchr, stpchrn, strcmpi, strnset, strset, stcarg, stpsym, stptok, stpblk, strbpl, strdup, strins, strmid, stcd_i, stcd_l, ecvt, fcvt, gcvt, stch_i, stch_l, stci_d, stci_h, stci_o, stcl_d, stcl_h, stcl_o, stco_i, stco_l, stcu_d, stcul_d, toascii, stpdate, stptime, __datecvt, __timecvt, utpack, utunpk, cot, iabs, max, min, pow2, __emit, getreg, putreg, geta, isatty, ovlyMgr, dqsort, fqsort, lqsort, sqsort, strsrt, tqsort, drand48, erand48, jrand8, lcong48, lrand48, mrand8, nrand48, seed48, srand48, __autoopenfail, chkabort, Chk_Abort, _CXBRK, __exit, onexit, _XCEXIT, forkl, forkv, onbreak, wait, waitm, bldmem, rstmem, sizmem, chkml, getmem, getml, halloc, lsbrk, sbrk, _MemCleanup, rbrk, rlsmem, rlsml, memccpy, movmem, repmem, setmem, swmem, except, __matherr, poserr, datecmp, timer, __tzset, getch, fgetchar, fputchar, _dread, _dwrite, read, write, clrerr, close, _dclose, fcloseall, creat, _dcreat, _dcreatx, fdopen, fileno, fmode, iomode, open, _dopen, flushall, mkstemp, mktemp, setnbf, _dseek, lseek, tell, access, chkufb, chmod, fstat, getfa, getft, stat, stcgfe, stcgfn, stcgfp, strmfe, strmfn, strmfp, strsfn, unlink, argopt, chgclk, dos_packet, getclk, getasn, getdfs, putenv, rawcon, stackavail, stacksize, stackused, chdir, closedir, dfind, dnext, findpath, getcd, getcwd, getfnl, getpath, mkdir, opendir, readdir, rmdir, seekdir, rewinddir, telldir, readlocale, scr_beep, scr_bs, scr_cdelete, scr_cinsert, scr_clear, scr_cr, scr_curs, scr_cursrt, scr_cursup, scr_eol, scl_home, scr_ldelete scr_lf, scr_linsert scr_tab, _CXFERR, _CXOVF, _EPILOG, _PROLOG

The most important of the "not allowed" functions are the Level 0 I/O functions (open, close, read, write). Use fopen, fclose, fread, fwrite instead. Note: there might be a few errors in the list, but probably not many. But STRICT ANSI does not only limit the functions, there are also some things that cause a warning from SAS/C, but an error from a strict ANSI Compiler.

Things like:

char *string=malloc(300);

cause an error from StormC. Correct would be:

char *string=(char *)malloc(300);

ANSI wants STRONG TYPING. If you do not own StormC, but want to make your code easily compilable with StormC PPC later, compile with STRICT ANSI. Problems appear especially with function pointers. If you are not sure how to cast a thing for STRICT ANSI, maybe you should try void *, it works often for not strongly typed source.

You should also replace all K&R Syntax (example)

void main(argv,argc)

int argv;

char **argc;

by the normal syntax (example)

void main(int argv,char **argc);

Also a code like

int a=5;

int stuff[a];

is not legal on ANSI. Array Dimensions have to be constants. If you need them variable, use dynamic allocation using malloc.

A good method to convert to "Strict ANSI" is the following:

  1. Just compile it, and look at every warning and error
  2. Typecast everything that looks like a pointer (and causes an error) to void *, everything else that causes problems, to a int, long or double.
  3. If some things still don't work, have a look at them now.

Some Sources (like the Source of Doom) require parts of the Unix/TCP includes. If you need such things, please contact me via Infinite Frontiers, I have converted the needed things to StormC.

Now we are nearly done with the ANSI/StormC Adaption. At the end some keyword have to be defined differently:

#define __stdargs

#define __regargs

#define __asm

#define __far FAR

#define __inline inline

#define __volatile volatile

__chip, __fast and __interrupt do not exist on StormC, they have to be replaced by the appropriate OS Functions. Some programmers also use some strange combinations that won't work (static inline is complete nonsense, get it Unix-coders :) !!! Static OR inline but not both of them !!!)

And if we are at "bad coding style": Bitfields only exist on C++, not in ANSI C... Ah, and one word to those fclose-always-works-fans. No, fclose does not work, if the file is NOT OPEN!!! You crash your task if you try to close a file that is not open.

Do

if (file) fclose(file);

Some words to __attribute__ ((packed)). It does not exist, and is a feature that would slow down the PPC *much*, if it would exist. Please do not use __attribute ((packed)). The PPC needs a certain alignment to get optimal speed.

You should not use text constants longer than a line.

If you have done all this, you now (should) have a working StormC 68k Source. Now we go to the PPC stuff. The most work is done now. Only small things remain to do. PPC-handling is mostly done internally by the compiler.

3) Adapt to PPC

At first we have to change register parameters:

void test(register __a0 mytest);

has to be changed (for example) to

void test(register mytest);

The PPC does not know a register a0. But you can tell it to use a register by usage of the keyword "register", without specifying a register number.

Next we have to do some changes to OS-Includes:

up to now, depending on which compiler you used, you did (example):

#include <clib/exec_protos.h>

#include <pragmas/exec_pragmas.h>

or

#include <clib/exec_protos.h>

#include <pragma/exec_lib.h>

or

#include <clib/exec_protos.h>

#include <inline/exec.h>

or

#include <proto/exec.h>

For StormC PPC you do:

#include <clib/exec_protos.h>

Do not include any pragmas/pragma files, or you will be swamped by error-messages. Also do not include any proto/ files.

If you want to compile your source for both 68k and PPC (without changing the source) you do:

#include <clib/exec_protos.h>

#ifndef __PPC__

#include <pragma/exec_lib.h>

#endif

__PPC__ is always set correctly.

Yet another difference between 68k and PPC concerns the usage of Subtasks. If you want to do the Subtask as PPC Task (recommended) you have to replace functions like CreateTask() by CreateTaskPPC() of the powerpc.library. I won't go into detail here, most of the time the API is absolutely identical to the usual functions, with the exception of a PPC at the end of the function name. Read the documentation of WarpOS for more information.

The other method would be doing the subtask as 68k task and calling CreateTask(). To do so you would have to make your program a mixed Binary, though, and you also would not get full PPC Speedup. So usually (unless the subtask does many OS Calls) the CreateTaskPPC() approach is the better method. Also, it is recommended not to use 68k Subtasks in PPC programs, so that your program will get optimal speed on a 100% PPC Amiga System (that surely will appear some time in the future).

Next thing to discuss would be the Tag-List notation of some OS-Functions. The PPC-Compiler can do OpenScreenTagList (the one with the array), but it *cannot* do OpenScreenTags. So if you use the "Tags"-version of OS Calls in your source, you have to change this to the "TagList"-version.

Then we come to the BeginIO-Function. This function only exists with a Library Base on the PPC Compiler. You can use the following code (example is for audio.device):

#include <libraries/powerpc.h>

#include <ppcamiga.h>

void BeginIOAudioPPC(struct IORequest *arg1)

(

extern struct Library *AudioBase;

ULONG regs[16];

regs[9]=(ULONG) arg1;

__CallLibrary(AudioBase,-30,regs);

)

An example how this can be used (out of the Sound-Code of ZhaDoom...):

AudioBase=(struct Library *)audio_io->ioa_Request.io_Device;

c=&channel_info[cnum];

c->audio_io->ioa_Request.io_Command=CMD_WRITE;

c->audio_io->ioa_Request.io_Flags=ADIOF_PERVOL;

c->audio_io->ioa_Data=&chip_cache_info[cache_chip_data (id)].chip_data[8];

c->audio_io->ioa_Length=lengths[id] - 8;

c->audio_io->ioa_Period=period_table[pitch];

c->audio_io->ioa_Volume=vol << 2;

c->audio_io->ioa_Cycles=1;

#ifdef __PPC__

BeginIOAudioPPC((struct IORequest *)c->audio_io);

#else

BeginIO ((struct IORequest *)c->audio_io);

#endif

You see? You always have to read out the LibraryBase of a device to do a BeginIO on PPC...

Some readers now probably ask themselves what about the famous "Context-Switch". Well, the truth is, under StormC, the Compiler automatically deals with the Contextswitch. You won't have to think about it... I will say a few words about it, anyway:

There are two sorts of Contextswitches:

a) Function-Contextswitches

You have to compile with Debugging-Information the first time you compile the Source. Then the compiler handles the Contextswitches automatically. Later you can compile without Debugging-Information, if you want.

b) Library-Contextswitches

These need so-called "function-stubs". ppcamiga.lib already contains the function-stubs for all Amiga-OS-functions, and for the 68k-functions of rtgmaster (But for rtgmaster also PPC-functions exist, and it is advised to use these). To create a stub for a not yet supported library, you do:

genppcstub mylib_protos.h mylib.fd

VERBOSE

You need the proto- and the FD-File to create the stub. The stub is a C Source file that you link together with your Source. The Contextswitch itself then works automatically.

Return to AmigaReturn to Computing / GamingReturn to front page