[comp.sys.amiga.tech] how to write reentrant C code?

ain@s.cc.purdue.edu (Patrick White) (07/21/88)

   Does anybody know what the dos and don'ts are to write reentrant code with
Manx 3.4?  (or 3.6 as I will be upgrading to that soon).

   For example, I know that static (global) variables are out, but are there
any other things? (like string constants?)

   While we are on the subject, what about reentrant code in all the other
languages for the Amiga.. like pascal, and assembly, and modula2, and
Lattice C  :-)?

   Thanks.


-- Pat White
ARPA/UUCP: s.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM  PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

dillon@CORY.BERKELEY.EDU (Matt Dillon) (07/24/88)

>   Does anybody know what the dos and don'ts are to write reentrant code with
>Manx 3.4?  (or 3.6 as I will be upgrading to that soon).
>
>   For example, I know that static (global) variables are out, but are there
>any other things? (like string constants?)
>
>   While we are on the subject, what about reentrant code in all the other
>languages for the Amiga.. like pascal, and assembly, and modula2, and
>Lattice C  :-)?
>
>   Thanks.

	Mainly, you have to stay away from most link-library routines (C.LIB).
As you said, globals, unless they are constants, are out.  For example,
SysBase and DOSBase are globals but never change, so they are ok.  STDIO 
(in C.LIB) is DEFINATELY out.  If you stick to run-time library calls and
keep in mind your code might be running simultaniously in different contexes,
you should be fine.

	AVOID RETURNING STRUCTURES.  Many compilers, Aztec included (I'm
pretty sure), use static memory to hold structural return values.  I don't
know of anybody who uses the [mis]feature, though.

	STATIC variables, unless used purposefully, are also out.

NOTE:  This means using the main() entry point is out, as it sets up stdio.
I am not sure about using _main() ... it might work.  If all else fails,
you can compile your code +B, and assuming there are no other references
to .begin, the code is entered at the first module.  In this case you must
remember that global variables will NOT be zero'd initially, and neither
the DOS or  EXEC libraries will be open.

	Did I miss anything?

				-Matt

jvkelley@watcgl.waterloo.edu (Jeff Kelley) (07/25/88)

In article <3415@s.cc.purdue.edu> ain@s.cc.purdue.edu (Patrick White) writes:
>
>   For example, I know that static (global) variables are out, but are there
>any other things? (like string constants?)
>
  With Lattice C 4.01, it is possible to have up to 64k of global variables
and reentrancy.  Essentially, you use address register a4 as a free parameter
pass between all your functions.  You just have to be careful to initialize it
correctly.  Global variables are handy because the Lattice compiler will
generate code to access amiga libraries (other than Exec) with references to
the library base as a global variable.  (e.g. using intuition.library involves
an access to IntuitionBase).

  My technique allows string constants, but only with a bit of trickery to
put them in the code section rather than the data section.

>   While we are on the subject, what about reentrant code in all the other
>languages for the Amiga.. like pascal, and assembly, and modula2, and
>Lattice C  :-)?

This method will only work for Lattice C 4.01.  I use it to write ROMmable
Exec devices in C (with a tiny piece of assembler glue common to all the
devices I've written).  Writing Exec libraries currently requires a little
assembler for each library entry point to get parameters out of registers,
but if the "#pragma libcall" feature of Lattice would support passing
arguments to libraries on the stack, even this wouldn't be necessary.

Anyway, I've deviated from the topic at hand.  Here's a shar file which
explains the technique and contains a demo 'Hello, world' program.

--
Jeff Kelley    Graphics Lab, Dept. of Computer Science, University of Waterloo
   uunet!watmath!watcgl!jvkelley  	tel:   (519) 578-4514
"Remember, this is not a competition, only an exhibition. Please, no wagering."
	- David Letterman


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by watcgl!jvkelley on Sun Jul 24 14:42:26 EDT 1988
# Contents:  README data.c datatocode.c hw.c makefile startup.asm strings.c
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
Here's a simple example showing how to create a Lattice C 4.01 load file with
no data or bss sections.  The global variable section, pointed to by register
a4, is dynamically allocated.  Note that this only works with the small data
model and limits the combined size of globals to 64k.

The technique involved requires that all global variable declarations be
placed in one file, and referenced as 'extern's in other files where they
are used.  It is not possible to statically initialize these variables,
although they may be assumed to be zero if you clear the memory dynamically
allocated for them.

The trick involves the use of what is either a bug or a feature of 'blink'.
It seems that if you have only one bss section in one of the object files,
and no data sections in any of them, then the bss section will not appear
in the load file.  References to the bss section will be resolved as if it
existed, however, and this allows one to dynamically allocate it.

Note that if even a single data section is present during linking, the 
linking will either fail or produce an unwanted resultant load file.
I suggest linking first with the 'PRELINK' option to 'blink', and 'omd'ing
this file to ensure that no data or bss sections are present.

It is also important to realize that strings are placed in the data section.
It is possible to put them in the code section by declaring them in a separate
file, changing the data section of the object produced by that file to a code
section, and referencing the strings as external functions.  (Very gross.)
I've written a data hunk to code hunk translator to assist with this operation.
(source in datatocode.c)

Many library routines (in lc.lib and amiga.lib, for example) have data sections
and are thus off limits.

Anyway, included herein is a 'hello, world' demo of the technique.

--
Jeff Kelley    Graphics Lab, Dept. of Computer Science, University of Waterloo
   uunet!watmath!watcgl!jvkelley  	tel:   (519) 578-4514
"Remember, this is not a competition, only an exhibition. Please, no wagering."
	- David Letterman
@//E*O*F README//
chmod u=rw,g=r,o=r README
 
echo x - data.c
sed 's/^@//' > "data.c" <<'@//E*O*F data.c//'
unsigned long DOSBase;
@//E*O*F data.c//
chmod u=rw,g=r,o=r data.c
 
echo x - datatocode.c
sed 's/^@//' > "datatocode.c" <<'@//E*O*F datatocode.c//'
#include    <stdio.h>

#define HUNK_DATA 0x3EAL
#define HUNK_CODE 0x3E9L
#define HUNK_BSS  0x3EBL

void
main(argc, argv)
    int argc;
    char *argv[];
{
    FILE *pf;
    unsigned long i;
    char *name;

    if (argc < 2 || argc > 3)
    {
	puts("Usage: dc [-z] <file>");
	exit(1);
    }
    if (argc == 3)
    {
	if (strcmp(argv[1],"-z"))
	{
	    puts("Bad option");
	    exit(1);
	}
	name = argv[2];
    }
    else
	name = argv[1];
    if (!(pf = fopen(name, "r+")))
    {
	puts("Can't open file");
	exit(1);
    }
    while (fread((char *)&i, sizeof(i), 1, pf) == 1)
    {
	if (argc == 2)
	{
	    if (i == HUNK_DATA)
	    {
		fseek(pf,-sizeof(i),1);
		i = HUNK_CODE;
		fwrite((char *)&i,sizeof(i),1,pf);
		break;
	    }
	}
	else
	{
	    if (i == HUNK_BSS)
	    {
		i = 0;
		fseek(pf,0,1);
		fwrite((char *)&i,sizeof(i),1,pf);
		break;
	    }
	}
    }
    fclose(pf);
}

@//E*O*F datatocode.c//
chmod u=rw,g=r,o=r datatocode.c
 
echo x - hw.c
sed 's/^@//' > "hw.c" <<'@//E*O*F hw.c//'
#include    <exec/types.h>

#include    <string.h>

#include    <proto/exec.h>
#include    <proto/dos.h>

extern struct DosLibrary *DOSBase;

extern char *HelloWorld();
extern char *DosName();

void
myputs(s)
    char *s;
{
    Write(Output(), s, strlen(s));
}

void
main()
{
    DOSBase = (struct DosLibrary *)OpenLibrary((char *)DosName,0);
    myputs(HelloWorld);
    CloseLibrary((struct Library *)DOSBase);
}
@//E*O*F hw.c//
chmod u=rw,g=r,o=r hw.c
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'

LN = blink
CC = lc
CFLAGS = -v -d
AS = asm

all: datatocode hw

datatocode: datatocode.o
	$(CC) $(CFLAGS) -L datatocode.c

hw: hw.pre
	$(LN) from hw.pre to hw NODEBUG

hw.pre: hw.o data.o strings.o startup.o
	$(LN) from startup.o hw.o data.o strings.o to hw.pre lib lib:lc.lib PRELINK SMALLCODE SMALLDATA

hw.o: hw.c
	$(CC) $(CFLAGS) hw.c

data.o: data.c
	$(CC) $(CFLAGS) data.c


strings.o: strings.c
	$(CC) -v -b0 strings.c
	datatocode strings.o

startup.o: startup.asm
	$(AS) startup.asm

@//E*O*F makefile//
chmod u=rw,g=r,o=r makefile
 
echo x - startup.asm
sed 's/^@//' > "startup.asm" <<'@//E*O*F startup.asm//'
	SECTION startup,CODE

	XREF	_main

ABSEXECBASE EQU 4
DATASIZE    EQU 4


	move.l	ABSEXECBASE.w,a6
	move.l	#DATASIZE,d0
	moveq.l #0,d1
	jsr	-$00c6(a6)        AllocMem
	tst.l	d0
	beq	nomem

	movea.l d0,a4		Set up base register
	jsr	_main

	move.l	ABSEXECBASE.w,a6
	movea.l a4,a1
	move.l	#DATASIZE,d0
	jsr	-$00d2(a6)        FreeMem

nomem:
	rts

	END

@//E*O*F startup.asm//
chmod u=rw,g=r,o=r startup.asm
 
echo x - strings.c
sed 's/^@//' > "strings.c" <<'@//E*O*F strings.c//'
char HelloWorld[] = "Hello, world.\n";
char DosName[] = "dos.library";

@//E*O*F strings.c//
chmod u=rw,g=r,o=r strings.c
 
exit 0