[net.sources] smallc

sob (03/13/83)

this an archive file: place in a directory, chmod +x, then run to extract
the files

echo x - Makefile
cat >Makefile <<'!Funky!Stuff!'
SMALLH =  cc.def

LIBDIR = /usr/lib/smallc
BINDIR = /usr/bin

CFLAGS =  -w -DLIBDIR=\"$(LIBDIR)\"

OBJS = cc1.o cc11.o cc12.o cc13.o cc2.o cc21.o cc22.o cc3.o cc31.o cc32.o cc33.o cc4.o cc41.o cc42.o

SRCS = cc1.c cc11.c cc12.c cc13.c cc2.c cc21.c cc22.c cc3.c cc31.c cc32.c cc33.c cc4.c cc41.c cc42.c

smallc: ${OBJS} 
	cc -o smallc ${OBJS}

${OBJS}:    $(SMALLH)

install: smallc stdio.c asm.8085.c libsmc.c
	cp smallc $(BINDIR)
	cp stdio.c libasm.c libsmc.c $(LIBDIR)

clean:
	rm smallc ${OBJS}
!Funky!Stuff!
echo x - READ_ME
cat >READ_ME <<'!Funky!Stuff!'
Over All description of Implementation
======================================

    The major change to be made in this system is how to do "includes".
If the include statement file name has surrounding quotes ( " " ),
then the file is searched for in the current directory only, and
if not found results in an error.
    If the file name specified in the "include" statement is surrounded
by "<" and ">", then that indicates to the compiler that the file is
a library file, so it will not search the current directory for the
file, but a system defined directory, that can be defined when "making"
Smallc.  In order to do this, simply go into the Makefile, and where
it says "LIBDIR= ...", substitute the directory pathname your system
wishes to use.  Re-make the file, and you have defined a library dir-
ectory.

There are three different libraries that come with Smallc V2.

libsmc.c :
	Contains Smallc functions for Output (utilizing putc et. al.),
	and arithmetic operations and string functions.  Can either
	be "included" or , as it was meant to be used, can be
	passed on the command line as a library function.

libasm.c :
	Contains 8085 library routines for arithmetic operations and
	register transfers and shifts.  The routines in this file
	are called by the Smallc-compiled 8085 code very frequently,
	so it may be necessary to include this file with all Smallc
	programs. (This file may be included as is because of the
	#asm statements)

stdio.c :
	Contains I/O parameters for the Smallc system


   We hope very soon for there to be a way to pass a file with 8085 code
to the command line, and have it only append to the output file those
functions that are called for in the code.


      Also included in this package is a manual page, based as much
on the UNIXtm manual pages as possible, as well as the Makefile
described above.
!Funky!Stuff!
echo x - cc.def
cat >cc.def <<'!Funky!Stuff!'
/* %W% %G% */
/*
** Small-C Compiler Version 2.0
**
** Copyright 1982 J. E. Hendrix
**
** Macro Definitions
*/

#include <stdio.h>


/*
** compile options
*/
#define PHASE2   /* 2nd and later compiles */
#define OPTIMIZE /* compile output optimizer */
#define NOCCARGC /* no calls to CCARGC */
/* #define CMD_LINE /* command line run options */
/* #define HASH     /* use hash search for macros */
/* #define SMALL_VM /* uses Small-VM interface */
/* #define DYNAMIC  /* allocate memory dynamically */
/* #define POLL     /* poll for operator interruptions */
/* #define PDS       uses PDS assembler and loader */
#define COL      /* terminate labels with a colon */
#define TAB  9   /* put out tabs of this value */
#define UPPER    /* force symbols to upper case */
#define LINK     /* will use with linking loader */

/*
** machine dependent parameters
*/
#define BPW     2   /* bytes per word */
#define LBPW    1   /* log2(BPW) */
#define SBPC    1   /* stack bytes per character */
#define ERRCODE 7   /* op sys return code */
 
/*
** symbol table format
*/
#define IDENT    0
#define TYPE     1
#define CLASS    2
#define OFFSET   3
#define NAME     5
#define OFFSIZE (NAME-OFFSET)
#define SYMAVG  10
#define SYMMAX  14

/*
** symbol table parameters
*/
#define NUMLOCS   25
#define STARTLOC  symtab
#define ENDLOC   (symtab+(NUMLOCS*SYMAVG))
#define NUMGLBS   180
#define STARTGLB  ENDLOC
#define ENDGLB   (ENDLOC+((NUMGLBS-1)*SYMMAX))
#define SYMTBSZ   2770  /* NUMLOCS*SYMAVG + NUMGLBS*SYMMAX */
 
/*
** System wide name size (for symbols)
*/
#define NAMESIZE 9
#define NAMEMAX  8

/*
** Filestack size (for input files)
*/
#define FILESTACKMAX 30
#define INCLUDESTACKMAX 30
#define CHARSTACKMAX  128
 
/*
** possible entries for "IDENT"
*/
#define LABEL    0
#define VARIABLE 1
#define ARRAY    2
#define POINTER  3
#define FUNCTION 4
 
/*
** possible entries for "TYPE"
**    low order 2 bits make type unique within length
**    high order bits give length of object
*/
/*      LABEL   0 */
#define CCHAR   (1<<2)
#define CINT    (BPW<<2)
 
/*
** possible entries for "CLASS"
*/
/*      LABEL     0 */
#define STATIC    1
#define AUTOMATIC 2
#define EXTERNAL  3

/*
** "switch" table
*/

#define SWSIZ   (2*BPW)
#define SWTABSZ (25*SWSIZ)
 
/*
** "while" statement queue
*/
#define WQTABSZ  30
#define WQSIZ     3
#define WQMAX   (wq+WQTABSZ-WQSIZ)
 
/*
** entry offsets in while queue
*/
#define WQSP    0
#define WQLOOP  1
#define WQEXIT  2
 
/*
** literal pool
*/
#define LITABSZ 700
#define LITMAX  (LITABSZ-1)
 
/*
** input line
*/
#define LINEMAX  80
#define LINESIZE 81

/*
** output staging buffer size
*/
#define STAGESIZE   800
#define STAGELIMIT  (STAGESIZE-1)
 
/*
** macro (define) pool
*/
#define MACNBR   90
#define MACNSIZE 990   /* 90*(NAMESIZE+2) */
#define MACNEND  (macn+MACNSIZE)
#define MACQSIZE 450   /* 90*5 */
#define MACMAX  (MACQSIZE-1)
 
/*
** statement types
*/
#define STIF      1
#define STWHILE   2
#define STRETURN  3
#define STBREAK   4
#define STCONT    5
#define STASM     6
#define STEXPR    7
#define STDO      8 /* compile "do" logic */
#define STFOR     9 /* compile "for" logic */
#define STSWITCH 10 /* compile "switch/case/default" logic */
#define STCASE   11
#define STDEF    12
#define STGOTO   13 /* compile "goto" logic */

/*
** I/O parameters, logical flags
*/
/* #define stdin 0
#define stdout 1
#define stderr 2 */
#define stdport 3
#define stdlist 4
#define ERR -2
#define EOL 10
/* #define EOF -1 */
#define YES 1
#define NO 0
/* #define NULL 0 */
#define CR 13
#define LF 10

!Funky!Stuff!
echo x - smallc.1
cat >smallc.1 <<'!Funky!Stuff!'
.TH SMALLC 1  PDP11
.SH NAME
smallc  \- A subset C compiler
.SH SYNOPSIS
.B smallc
[ option ] ... file ...
.PP
.SH DESCRIPTION
Smallc is a subset C compiler that compiles pseudo-C
code into 8085 micro-computer assembler language.
It accepts several types of arguments:
.PP
Arguments whose names end with `.c' are taken to be
Smallc source programs; they are compiled, and
each assembler-language program is left on the file
whose name is that of the source with `.8085' substituted
for `.c'.
If multiple source files are supplied, then the output
file name will be the lastfilename.8085.
.PP
It is not possible to produce object code for the 8085 with
the Smallc compiler; It produces only assembly code.  Object
code must be produced from this assembly code with an 8085
assembler.
.PP
The following options are interpreted by
.IR smallc .
.TP 8
.B \-p
Causes the compiler to pause after each error it finds in the source.
The compilation will proceed only after the user has hit a carriage
return.
.TP
.SM
.B \-O
Invoke a
machine-code optimizer.
.TP
.SM
.B \-e
Utilize 'pointer' type error messages. The compiler
outputs the line at which the error occurred as well
as a 'pointer' to the illegal component of the line.
.TP
.BI \-o " output"
Name the final output file
.IR output .
If this option is used the file
inputfile.8085 will not be created.
.TP
.SM
.B \-m
Function monitor.
Displays function names and arguments as the compiler parses
each function.
.TP
.SM
.BI \-l " libfile"
After parsing the input file(s), will look up in the
symbol table for unresolved (undefined) functions and
then search through libfile looking for the function
definitions, whereupon it parses them to the output
file.
.TP
.SM
.B \-a
Error alarm.  Outputs a bell (^G) to stderr after each
error found.
.TP
.SM
.BI \-b " number"
Specify a starting number for assembler-language labels.
Used so that when appending compiled modules,
there are no multiply defined labels.
number is taken in decimal. 
.SH EXAMPLE
To compile a program and specify the output file name.
.br
$ smallc -o fname fname.c
.SH FILES
.ta \w'/usr/c/oc[102]  'u
file.c	input file
.br
file.8085	assembler-language file
.SH DIAGNOSTICS
The diagnostics produced by Smallc itself are intended to be
self-explanatory.
For a more detailed diagnostic error message, use the '-e' option.
.SH BRIEF OPTIONS
.br
.B \-p
Pauses after each error; waits for <cr> to continue.
.B \-O
Invoke a machine-code optimizer.
.B \-e
Produce "pointer" type error messages.
.br
.B \-o
output       Name final output file output.
.br
.B \-m
Prints out function names and args as they are parsed.
.B \-l
libfile      Search for unresolved functions in libfile.
.B \-a
Sound error alarm after each error.
.B \-b
number       Specify a starting label for assembler code.
!Funky!Stuff!
echo x - cc1.c
cat >cc1.c <<'!Funky!Stuff!'
/*
** Small-C Compiler Version 2.0
**
** Copyright 1982 J. E. Hendrix
**
** Part 1
*/

#include "cc.def"

/*
** miscellaneous storage
*/
char
  optimize, /* optimize output of staging buffer */
  alarm,    /* audible alarm on errors? */
  monitor,  /* monitor function headers? */
  library,  /* look up unresolved functions in user library */
  pause,    /* pause for operator on errors? */
  errorptr, /* print out line and error pointer on error */
  stage[STAGESIZE],
  symtab[SYMTBSZ],
  litq[LITABSZ],
  macn[MACNSIZE],
  macq[MACQSIZE],
  pline[LINESIZE],
  mline[LINESIZE],
  swq[SWTABSZ],
 *line,     /* points to pline or mline */
 *lptr,     /* ptr to either */
 *glbptr,   /* ptrs to next entries */
 *locptr,   /* ptr to next local symbol */
 *stagenext,/* next addr in stage */
 *stagelast,/* last addr in stage */
  quote[2], /* literal string for '"' */
 *cptr,     /* work ptrs to any char buffer */
 *cptr2,
 *cptr3,
  msname[NAMESIZE], /* macro symbol name array */
  ssname[NAMESIZE]; /* static symbol name array */
int
  nogo,     /* > 0 disables goto statements */
  noloc,    /* > 0 disables block locals */
  op[16],   /* function addresses of binary operators */
  op2[16],  /* same for unsigned operators */
  opindex,  /* index to matched operator */
  opsize,   /* size of operator in bytes */
  swactive, /* true inside a switch */
  swdefault,/* default label #, else 0 */
 *swnext,   /* address of next entry */
 *swend,    /* address of last table entry */
  wq[WQTABSZ],
 *wqptr,    /* ptr to next entry */
  litptr,   /* ptr to next entry */
  macptr,   /* macro buffer index */
  mack,     /* variable k for findmac routine */
  argcs,    /* static argc  */
  *argvs,   /* static argv  */
  filestack[FILESTACKMAX],    /* stack of input files */
  filebuffer[LINESIZE],    /* holds the current processing file  */
  charstack[CHARSTACKMAX],     /* holds included file names     */
  charcount,            /* # of chars in charstack    */
  includectr,           /* counter, indicating # of records in stack */
  filecount,            /* counter, accessing files in filestack */
  pptr,     /* ptr to parsing buffer */
  oper,     /* address of binary operator function */
  declared, /* # of local bytes declared, else -1 when done */
  iflevel,  /* #if... nest level */
  skiplevel,/* level at which #if... skipping started */
  func1,    /* true for first function */
  nxtlab,   /* next avail label # */
  litlab,   /* label # assigned to literal pool */
  beglab,   /* beginning label -- first function */
  ch,       /* current character of line being scanned */
  nch,      /* next character of line being scanned */
  csp,      /* compiler relative stk ptr */
  argstk,   /* function arg sp */
  argtop,
  ncmp,     /* # open compound statements */
  errflag,  /* non-zero after 1st error in statement */
  outflg,   /* specifies whether or not to open a default output file */
  eof,      /* set non-zero on final input eof */
  input,    /* fd # for input file */
  input2,   /* fd # for "include" file */
  output,   /* fd # for output file */
  linectr,  /* # of parsed lines in current file */
  files,    /* non-zero if file list specified on cmd line */
  filearg,  /* cur file arg index */
  glbflag,  /* non-zero if internal globals */
  ctext,    /* non-zero to intermix c-source */
  ccode,    /* non-zero while parsing c-code */
            /* zero when passing assembly code */
  listfp,   /* file pointer to list device */
  lastst,   /* last executed statement type */
 *iptr;     /* work ptr to any int buffer */

long 
	includestack[INCLUDESTACKMAX]; /* holds info for included files */

extern int
  addmac(),  addsym(),  addwhile(),  amatch(),  blanks(),
  bump(),  clearstage(),  col(),  delwhile(),  endst(),
  error(),  findglb(),  findloc(),  gch(),  getint(),
  getlabel(),  illname(),  inbyte(),  inline(),  junk(),
  kill(),  lout(),  match(),  multidef(),  needtoken(),
  nextsym(),  nl(),  numeric(),  outbyte(),  outdec(),
  postlabel(),  preprocess(),  printlabel(),  putint(),
  readwhile(),  setstage(),  sout(),  streq(),  symname(),
  upper();

extern int
  constexpr(),  expression(),  number(),  qstr(),
  test(),  stowlit();

extern int
  gt(),  and(),  asl(),  asr(),  defstora(),
  div(),  eq(),  entry(),  external(),  ge(),
  add(),  header(),  jump(),  le(),  lt(),  mod(),  modstk(),
  mult(),  ne(),  or(),  point(),  ret(),  sub(),  sw(),
  trailer(),  uge(),  ugt(),  ule(),  ult(),  xor();
!Funky!Stuff!
echo x - cc11.c
cat >cc11.c <<'!Funky!Stuff!'
/*
** execution begins here
*/

#include "cc1.c"

main(argc, argv) 
int argc; 
char *argv; 
{
  swend=(swnext=swq)+SWTABSZ-SWSIZ;
  stagelast=stage+STAGELIMIT;
 	swactive=       /* not in switch */
  stagenext=      /* direct output mode */
  iflevel=        /* #if... nesting level = 0 */
  skiplevel=      /* #if... not encountered */
  macptr=         /* clear the macro pool */
  csp =           /* stack ptr (relative) */
  errflag=        /* not skipping errors till ";" */
  eof=            /* not eof yet */
  ncmp=           /* not in compound statement */
  files=
  filearg=
  linectr=
  filecount=
  quote[1]=0;
  func1=          /* first function */
  ccode=1;        /* enable preprocessing */
  wqptr=wq;       /* clear while queue */
  quote[0]='"';   /* fake a quote literal */
  charstack[0] = NULL;           /* initialize charstack */
  charcount =  1;  /* chars in character stack */
  input = input2 = EOF;
  options(argc,argv);          /* get user options */
  openin();     	 /* get first input file from command line */
  preprocess();   /* fetch first line */
  cptr = STARTGLB - 1;
  while (++cptr < ENDGLB) *cptr = 0;	/* initialize globla symtab */
  glbptr=STARTGLB; glbflag=1; ctext=0;
  header();          /* intro code */
  setops();          /* set values in op arrays */
  parse();           /* process ALL input */
  outside();         /* verify outside any function */
  dumpglbs();
  trailer();         /* follow-up code */
  fclose(output);
  }

/*
** process all input text
**
** At this level, only static declarations,
**      defines, includes and function
**      definitions are legal...
*/
parse() {
  while (eof==0) {
    if(amatch("extern", 6))   dodeclare(EXTERNAL);
    else if(dodeclare(STATIC));
    else if(match("#asm"))    doasm();
    else if(match("#include"))doinclude();
    else if(match("#define")) addmac();
    else                      newfunc();
    blanks();       /* force eof if pending */
    }
  }

/*
** dump the literal pool
*/
dumplits(size) 
int size;
{
  int j, k;
  k=0;
  while (k<litptr) {
    defstorage(size);
    j=10;
    while(j--) {
      outdec(getint(litq+k, size));
      k=k+size;
      if ((j==0)|(k>=litptr)) {
        nl();
        break;
        }
      outbyte(',');
      }
    }
  }

/*
** dump zeroes for default initial values
*/
dumpzero(size, count)
int size, count;
{
  int j;
  while (count > 0) {
    defstorage(size);
    j=30;
    while(j--) {
      outdec(0);
      if ((--count <= 0)|(j==0)) {
        nl();
        break;
        }
      outbyte(',');
      }
    }
  }

/*
** verify compile ends outside any function
*/
outside()  {
  if (ncmp) error("no closing bracket");
  }

/*
**
**	Get options from user
**
*/	
options(argc, argv) 
int     argc; 
char    *argv[];
{
	int     k;
	char    *foo;

	linectr = k = listfp = nxtlab = 0; 
	includectr = 1;
	output = stdout;
	library = optimize = errorptr = alarm = monitor = pause = NO;
	line = mline;			/* ???? */

	/* 
	**
	**	get options from command line 
	**
	*/
	while (++k < argc) {
		foo = argv[k];
		if( *foo == '-' ) {
			foo++;
			switch(*foo) {		 /* Command line switches */
				case 'C':
						ctext = 1;
						break;
				case 'o':
						/* user names the output file */
						foo++;
						if (*foo == 0) foo = argv[++k]; /* foo gets output filename */
						openout(foo);		/* open file */
						outflg = 1;		   /* set output flag */ 
						break;
				case 'l':
						foo++;
						if (*foo == 0) foo = argv[++k]; /* foo gets output filename */
						filecount = FILESTACKMAX - 2;
						storefile(foo);
						filecount = 0;
						library = YES;
						break;
				case 'a':
        		alarm=YES;
						break;
				case 'm':
						monitor=YES;
						break;
				case 'O':
						optimize=YES;
						break;
				case 'p':
						pause=YES;
						break;
				case 'e':
						errorptr=YES;
						break;
				case 'b':
						foo++;
						if (*foo == 0) foo = argv[++k]; /* foo gets output filename */
						if(sscanf(foo, "%d", &nxtlab) != 1) {
							printf("smallc: must be a number after -b option\n");
							exit(1);
						}
						else break;
		 		default: 
						/* unknown option */
						printf("%s: unknown option: %s\n", argv[0], foo);
						exit(1);
			}
		}
		else storefile(foo);   /* store input files */
	}
	if (argc == 1) exit(1);			   /* no input file supplied -- exit */
	if (outflg == 0) opendef(foo); /* no output file specified -- open one */
											 					 /* default is lastfilename.8085 */
	kill();
}

/* pushes each input file it gets onto a filestack */
storefile(name)
char *name;
{
	if (filecount > (FILESTACKMAX - 2) && library == YES)  {
											 /* save one space on filestack */
		sout("Too many input files: system error\n",stdout);
		exit(1);	
	}
	filestack[++filecount] = name;
}

/* pops into it's argument array the top file name on the filestack */
restorefile(array)
char array[];
{
	char *foo;
	int i;
	i = 0;
	foo = filestack[filecount--];
	if (foo  == NULL) return 0;
	while (*foo != NULL) 
			array[i++] = *foo++;
	array[i] = NULL;
	return i;
}
	
/*
** get next input file
*/
openin()
{
	int i,k,j;
	char temp[LINESIZE],*pt;
  k = j = 0;
	input = EOF;
	pt = &temp[0];
	if (i = restorefile(temp)) {
		if ((temp[i-2] != '.') & (temp[i-1] != 'c')) {
			fprintf(stdout,"Open failure for input file %s (must be appended with '.c')\n",pt);
			abort();
		}
		if ((input = fopen(temp, "r")) == NULL) {
		 	fprintf(stdout,"Open failure for input file %s\n",pt);
			abort();
		}
		files = YES;
		linectr = 0;
	  while (j < i) filebuffer[j] = temp[j++];
    filebuffer[j] = NULL;
		kill();			/* erase line */
		return;
	}
	if (files++) eof = YES;
	else input = stdin;
	kill();
}

/*  open a specified-by-name output file   */
openout(filname)
char	*filname;
{
	int i;
	i = 0;
	/* while((line[i++] = *filname++) != NULL)
	;
	line[i] = NULL; */
	if ((output = fopen(filname,"w")) == NULL)
	{
		output = 0;	/* can't open */
		error("Open error for output file\n");
	}
}

/*	construct default	*/
/*	output filename		*/
/*	append '.8085' to filname */
opendef(filname)
char    *filname;
{
	int i;
	i = output = 0;		/* start with none */
	while (((line[i++] = *filname++) != '.'));
	line[i++] = '8';
	line[i++] = '0';
	line[i++] = '8';
	line[i++] = '5';
	line[i]   = NULL;	/* make .asciz string */
	if ((output=fopen(line,"w")) == NULL) 
	{
		output = 0;	/* can't open */
		error("Open failure for output file\n");
	}
	kill();			/* erase line */
}

setops() {
  op2[0]=     op[0]=  or;  /* heir5 */
  op2[1]=     op[1]= xor;  /* heir6 */
  op2[2]=     op[2]= and;  /* heir7 */
  op2[3]=     op[3]=  eq;  /* heir8 */
  op2[4]=     op[4]=  ne;
  op2[5]=ule; op[5]= le;  /* heir9 */
  op2[6]=uge; op[6]=  ge;
  op2[7]=ult; op[7]=  lt;
  op2[8]=ugt; op[8]=gt;
  op2[9]=     op[9]= asr;  /* heir10 */
  op2[10]=     op[10]= asl;
  op2[11]=     op[11]= add;  /* heir11 */
  op2[12]=     op[12]= sub;
  op2[13]=     op[13]=mult;  /* heir12 */
  op2[14]=     op[14]= div;
  op2[15]=     op[15]= mod;
  }
!Funky!Stuff!
echo x - cc12.c
cat >cc12.c <<'!Funky!Stuff!'
static char *cc12_id = "%W% %G%";

#include "cc1.c"

#ifndef LIBDIR
#define LIBDIR ="."
#endif

char *LIB = LIBDIR;

/*
** open an include file
*/
doinclude()  
{
	int i,j;
  char name[LINESIZE];
	int c;
	i = j = 0;
  pushfile(input);	/* save fd, filename and linecount in stack */
  blanks();          /* skip over to name and read between quotes */
  if (*lptr == '<') {
		while (*LIB != NULL) name[i++] = *LIB++;
		name[i++] = '/';
		while (*++lptr != '>') name[i++] = *lptr;
	}
  else if (*lptr++ == '"') while (*lptr != '"') name[i++] = *lptr++;
	else error("Bad include syntax");
  name[i] =(char) NULL;
  i = 0;
	while ((c = (int) name[i]) != NULL) filebuffer[i++] = c;
	filebuffer[i] = c;
	if ((input = fopen(name,"r")) == NULL ) {
    i = 0;
    input = EOF;
    printf("open failure on include file ");
    while(filebuffer[i]) printf("%c",filebuffer[i++]);
    printf("\n");
    if (alarm) putc(7,stderr);
  }
  kill();         /* clear rest of line */
  }

/*
** test for global declarations
*/
dodeclare(class) int class; {
  if(amatch("char",4)) {
    declglb(CCHAR, class);
    ns();
    return 1;
    }
  else if((amatch("int",3))|(class==EXTERNAL)) {
    declglb(CINT, class);
    ns();
    return 1;
    }
  return 0;
  }

/*
** delcare a static variable
*/
declglb(type, class)  int type, class; {
  int k, j;
  while(1) {
    if(endst()) return;     /* do line */
    if(match("*")) {
      j=POINTER;
      k=0;
      }
    else {
      j=VARIABLE;
      k=1;
      }
    if (symname(ssname, YES)==0) illname();
    if(findglb(ssname)) multidef(ssname);
    if(match("()")) j=FUNCTION;
    else if (match("[")) {
      k=needsub();    /* get size */
      j=ARRAY;   /* !0=array */
      }
    if(class==EXTERNAL) external(ssname);
    else j=initials(type>>2, j, k);
    addsym(ssname, j, type, k, &glbptr, class);
    if (match(",")==0) return; /* more? */
    }
  }

/*
** declare local variables
*/
declloc(typ)  int typ;  {
  int k,j;
  if(noloc) error("not allowed with goto");
  if(declared < 0) error("must declare first in block");
  while(1) {
    while(1) {
      if(endst()) return;
      if(match("*")) j=POINTER;
      else j=VARIABLE;
      if (symname(ssname, YES)==0) illname();
      /* no multidef check, block-locals are together */
      k=BPW;
      if (match("[")) {
        k=needsub();
        if(k) {
          j=ARRAY;
          if(typ==CINT)k=k<<LBPW;
          }
        else j=POINTER;
        }
      else if(match("()")) j=FUNCTION;
      else if((typ==CCHAR)&(j==VARIABLE)) k=SBPC;
      declared = declared + k;
      addsym(ssname, j, typ, csp - declared, &locptr, AUTOMATIC);
      break;
      }
    if (match(",")==0) return;
    }
  }

/*
** initialize global objects
*/
initials(size, ident, dim) int size, ident, dim; {
  int savedim;
  litptr=0;
  if(dim==0) dim = -1;
  savedim=dim;
  entry();
  if(match("=")) {
    if(match("{")) {
      while(dim) {
        init(size, ident, &dim);
        if(match(",")==0) break;
        }
      needtoken("}");
      }
    else init(size, ident, &dim);
    }
  if((dim == -1)&(dim==savedim)) {
     stowlit(0, size=BPW);
    ident=POINTER;
    }
  dumplits(size);
  dumpzero(size, dim);
  return ident;
  }

/*
** evaluate one initializer
*/
init(size, ident, dim) int size, ident, *dim; {
  int value;
  if(qstr(&value)) {
    if((ident==VARIABLE)|(size!=1))
      error("must assign to char pointer or array");
    *dim = *dim - (litptr - value);
    if(ident==POINTER) point();
    }
  else if(constexpr(&value)) {
    if(ident==POINTER) error("cannot assign to pointer");
    stowlit(value, size);
    *dim = *dim - 1;
    }
  }

/*
** get required array size
*/
needsub()  {
  int val;
  if(match("]")) return 0; /* null size */
  if (constexpr(&val)==0) val=1;
  if (val<0) {
    error("negative size illegal");
    val = -val;
    }
  needtoken("]");      /* force single dimension */
  return val;          /* and return size */
  }

/*
** begin a function
**
** called from "parse" and tries to make a function
** out of the following text
**
** Patched per P.L. Woods (DDJ #52)
*/
newfunc()  {
  char *ptr;
  nogo  =             /* enable goto statements */
  noloc = 0;          /* enable block-local declarations */
  lastst=             /* no statement yet */
  litptr=0;           /* clear lit pool */
  litlab=getlabel();  /* label next lit pool */
  locptr=STARTLOC;    /* clear local variables */
  if(monitor) lout(line, stderr);
  if (symname(ssname, YES)==0) {
    error("illegal function or declaration");
    kill(); /* invalidate line */
    return;
    }
  if(func1) {
    postlabel(beglab);
    func1=0;
    }
  if(ptr=findglb(ssname)) {      /* already in symbol table ? */
    if(ptr[IDENT]!=FUNCTION)       multidef(ssname);
    else if(ptr[OFFSET]==FUNCTION) multidef(ssname);
    else ptr[OFFSET]=FUNCTION;
      /*  earlier assumed to be a function */
    }
  else
    addsym(ssname, FUNCTION, CINT, FUNCTION, &glbptr, STATIC);
  if(match("(")==0) error("no open paren");
  entry();
  locptr=STARTLOC;
  argstk=0;               /* init arg count */
  while(match(")")==0) {  /* then count args */
    /* any legal name bumps arg count */
    if(symname(ssname, YES)) {
      if(findloc(ssname)) multidef(ssname);
      else {
        addsym(ssname, 0, 0, argstk, &locptr, AUTOMATIC);
        argstk=argstk+BPW;
        }
      }
    else {error("illegal argument name");junk();}
    blanks();
    /* if not closing paren, should be comma */
    if(streq(lptr,")")==0) {
      if(match(",")==0) error("no comma");
      }
    if(endst()) break;
    }
  csp=0;        /* preset stack ptr */
  argtop=argstk;
  while(argstk) {
    /* now let user declare what types of things */
    /*      those arguments were */
    if(amatch("char",4))     {doargs(CCHAR);ns();}
    else if(amatch("int",3)) {doargs(CINT);ns();}
    else {error("wrong number of arguments");break;}
    }
  if(statement()!=STRETURN) ret();
  if(litptr) {
    printlabel(litlab);
    col();
    dumplits(1); /* dump literals */
    }
  }

/*
** declare argument types
**
** called from "newfunc" this routine adds an entry in the
** local symbol table for each named argument
**
** rewritten per P.L. Woods (DDJ #52)
*/
doargs(t) int t; {
  int j, legalname;
  char c, *argptr;
  while(1) {
    if(argstk==0) return; /* no arguments */
    if(match("*")) j=POINTER; else j=VARIABLE;
    if((legalname=symname(ssname, YES))==0) illname();
    if(match("[")) {   /* is it a pointer? */
      /* yes, so skip stuff between "[...]" */
      while(inbyte()!=']') if(endst()) break;
      j=POINTER; /* add entry as pointer */
      }
    if(legalname) {
      if(argptr=findloc(ssname)) {
        /* add details of type and address */
        argptr[IDENT]=j;
        argptr[TYPE]=t;
        putint(argtop-getint(argptr+OFFSET, OFFSIZE), argptr+OFFSET, OFFSIZE);
        }
      else error("not an argument");
      }
    argstk=argstk-BPW;        /* cnt down */
    if(endst())return;
    if(match(",")==0) error("no comma");
    }
  }

pushfile(fd)
int fd;
{
   char name[LINESIZE];
	int i;
	i = 0;
	if (includectr > INCLUDESTACKMAX) error("Too many nested includes");
	includestack[includectr++] = fd;
	includestack[includectr++] = linectr;
	while (filebuffer[i]){
  		 charstack[charcount++] = filebuffer[i++];
   }
	charstack[charcount++] = NULL;
	linectr = 0;             /* reset line count for new file */
}

popfile()
{
	int i;
	i = 0;
	if (includectr != YES) {       					/* if inside an include */
		if (charstack[--charcount]){	/* and stack is set up correctly */
			printf("System Error\n");
			exit(1);
      }
		while (charstack[--charcount]) /* while char on stack is good */
			filebuffer[i++] = charstack[charcount]; /* copy into filebuffer */
		filebuffer[i] = NULL;                     
		charcount++;									 /* next open pos. in stack */
		reversearray(filebuffer);
		linectr = includestack[--includectr];   /* restore line count */
	   return(includestack[--includectr]);
}
	else return(EOF);
}

copyarray(array1,array2)   /* returns a pointer to a copy of its first arg */
int array1[],array2[];
{
	int i;
	i = 0;
	while (array1[i])
		array2[i] = array1[i++];
	array2[i] = NULL;
	return &array2[i];
}

reversearray(array)
int array[];
{
	int i,j;
	int array2[LINESIZE];
	i = j = 0;
	while (array[i++]);
	--i;
	while (i) array2[j++] = array[--i];
	array2[j] = NULL;
   copyarray(array2,array);
}
!Funky!Stuff!
echo x - cc13.c
cat >cc13.c <<'!Funky!Stuff!'
static char *cc13_id = "%W% %G%";

#include "cc1.c"

/*
** statement parser
**
** called whenever syntax requires a statement
**  this routine performs that statement
**  and returns a number telling which one
*/
statement() {
  if ((ch==0) & (eof)) return;
  else if(amatch("char",4))  {declloc(CCHAR);ns();}
  else if(amatch("int",3))   {declloc(CINT);ns();}
  else {
    if(declared >= 0) {
      if(ncmp > 1) nogo=declared; /* disable goto if any */
      csp=modstk(csp - declared, NO);
      declared = -1;
      }
    if(match("{"))               compound();
    else if(amatch("if",2))      {doif();lastst=STIF;}
    else if(amatch("while",5))   {dowhile();lastst=STWHILE;}
    else if(amatch("do",2))      {dodo();lastst=STDO;}
    else if(amatch("for",3))     {dofor();lastst=STFOR;}
    else if(amatch("switch",6))  {doswitch();lastst=STSWITCH;}
    else if(amatch("case",4))    {docase();lastst=STCASE;}
    else if(amatch("default",7)) {dodefault();lastst=STDEF;}
    else if(amatch("goto", 4))   {dogoto(); lastst=STGOTO;}
    else if(dolabel())           ;
    else if(amatch("return",6))  {doreturn();ns();lastst=STRETURN;}
    else if(amatch("break",5))   {dobreak();ns();lastst=STBREAK;}
    else if(amatch("continue",8)){docont();ns();lastst=STCONT;}
    else if(match(";"))          errflag=0;
    else if(match("#asm"))       {doasm();lastst=STASM;}
    else                         {doexpr();ns();lastst=STEXPR;}
    }
  return lastst;
  }

/*
** semicolon enforcer
**
** called whenever syntax requires a semicolon
*/
ns()  {
  if(match(";")==0) error("no semicolon");
  else errflag=0;
  }

compound()  {
  int savcsp;
  char *savloc;
  savcsp=csp;
  savloc=locptr;
  declared=0;    /* may now declare local variables */
  ++ncmp;        /* new level open */
  while (match("}")==0)
    if(eof) {
      error("no final }");
      break;
      }
    else statement();     /* do one */
  --ncmp;                 /* close current level */
  csp=modstk(savcsp, NO); /* delete local variable space */
  cptr=savloc;            /* retain labels */
  while(cptr < locptr) {
    cptr2=nextsym(cptr);
    if(cptr[IDENT] == LABEL) {
      while(cptr < cptr2) *savloc++ = *cptr++;
      }
    else cptr=cptr2;
    }
  locptr=savloc;          /* delete local symbols */
  declared = -1;          /* may not declare variables */
  }

doif()  {
  int flab1,flab2;
  flab1=getlabel(); /* get label for false branch */
  test(flab1, YES); /* get expression, and branch false */
  statement();      /* if true, do a statement */
  if (amatch("else",4)==0) {      /* if...else ? */
    /* simple "if"...print false label */
    postlabel(flab1);
    return;         /* and exit */
    }
  flab2=getlabel();
  if((lastst != STRETURN)&(lastst != STGOTO)) jump(flab2);
  postlabel(flab1); /* print false label */
  statement();      /* and do "else" clause */
  postlabel(flab2); /* print true label */
  }

doexpr() {
  int const, val;
  char *before, *start;
  while(1) {
    setstage(&before, &start);
    expression(&const, &val);
    clearstage(before, start);
    if(ch != ',') break;
    bump(1);
    }
  }

dowhile()  {
  int wq[4];              /* allocate local queue */
  addwhile(wq);           /* add entry to queue for "break" */
  postlabel(wq[WQLOOP]);  /* loop label */
  test(wq[WQEXIT], YES);  /* see if true */
  statement();            /* if so, do a statement */
  jump(wq[WQLOOP]);       /* loop to label */
  postlabel(wq[WQEXIT]);  /* exit label */
  delwhile();             /* delete queue entry */
  }

dodo() {
  int wq[4], top;
  addwhile(wq);
  postlabel(top=getlabel());
  statement();
  needtoken("while");
  postlabel(wq[WQLOOP]);
  test(wq[WQEXIT], YES);
  jump(top);
  postlabel(wq[WQEXIT]);
  delwhile();
  ns();
  }

dofor() {
  int wq[4], lab1, lab2;
  addwhile(wq);
  lab1=getlabel();
  lab2=getlabel();
  needtoken("(");
  if(match(";")==0) {
    doexpr();            /* expr 1 */
    ns();
    }
  postlabel(lab1);
  if(match(";")==0) {
    test(wq[WQEXIT], NO); /* expr 2 */
    ns();
    }
  jump(lab2);
  postlabel(wq[WQLOOP]);
  if(match(")")==0) {
    doexpr();            /* expr 3 */
    needtoken(")");
    }
  jump(lab1);
  postlabel(lab2);
  statement();
  jump(wq[WQLOOP]);
  postlabel(wq[WQEXIT]);
  delwhile();
  }

doswitch() {
  int wq[4], endlab, swact, swdef, *swnex, *swptr;
  swact=swactive;
  swdef=swdefault;
  swnex=swptr=swnext;
  addwhile(wq);
  *(wqptr + WQLOOP - WQSIZ) = 0;
  needtoken("(");
  doexpr();      /* evaluate switch expression */
  needtoken(")");
  swdefault=0;
  swactive=1;
  jump(endlab=getlabel());
  statement();   /* cases, etc. */
  jump(wq[WQEXIT]);
  postlabel(endlab);
  sw();          /* match cases */
  while(swptr < swnext) {
    defstorage(CINT>>2);
    printlabel(*swptr++);  /* case label */
    outbyte(',');
    outdec(*swptr++);      /* case value */
    nl();
    }
  defstorage(CINT>>2);
  outdec(0);
  nl();
  if(swdefault) jump(swdefault);
  postlabel(wq[WQEXIT]);
  delwhile();
  swnext=swnex;
  swdefault=swdef;
  swactive=swact;
  }

docase() {
  if(swactive==0) error("not in switch");
  if(swnext > swend) {
    error("too many cases");
    return;
    }
  postlabel(*swnext++ = getlabel());
  constexpr(swnext++);
  needtoken(":");
  }

dodefault() {
  if(swactive) {
    if(swdefault) error("multiple defaults");
    }
  else error("not in switch");
  needtoken(":");
  postlabel(swdefault=getlabel());
  }

dogoto() {
  if(nogo > 0) error("not allowed with block-locals");
  else noloc = 1;
  if(symname(ssname, YES)) jump(addlabel());
  else error("bad label");
  ns();
  }

dolabel() {
  char *savelptr;
  blanks();
  savelptr=lptr;
  if(symname(ssname, YES)) {
    if(gch()==':') {
      postlabel(addlabel());
      return 1;
      }
    else bump(savelptr-lptr);
    }
  return 0;
  }

addlabel()  {
  if(cptr=findloc(ssname)) {
    if(cptr[IDENT]!=LABEL) error("not a label");
    }
  else cptr=addsym(ssname, LABEL, LABEL, getlabel(), &locptr, LABEL);
  return (getint(cptr+OFFSET, OFFSIZE));
  }

doreturn()  {
  if(endst()==0) {
    doexpr();
    modstk(0, YES);
    }
  else modstk(0, NO);
  ret();
  }

dobreak()  {
  int *ptr;
  if ((ptr = readwhile(wqptr))==0) return; /* no loops open */
  modstk((ptr[WQSP]), NO);          /* clean up stk ptr */
  jump(ptr[WQEXIT]);                /* jump to exit label */
  }

docont()  {
  int *ptr;
  ptr = wqptr;
  while(1) {
   	if ((ptr=readwhile(ptr))==0) return; /* no loops open */
	   if (ptr[WQLOOP]) break;
  }
  modstk((ptr[WQSP]), NO);          /* clean up stk ptr */
  jump(ptr[WQLOOP]);                /* jump to loop label */
  }

doasm()  {
  ccode=0;                /* mark mode as "asm" */
  while (1) {
    inline();
    if (match("#endasm")) break;
    if(eof)break;
    sout(line, output);
    }
  kill();
  ccode=1;
  }
!Funky!Stuff!
echo x - cc2.c
cat >cc2.c <<'!Funky!Stuff!'
static char *cc2_id = "%W% %G%";
/*
** Small-C Compiler Version 2.0
**
** Copyright 1982 J. E. Hendrix
**
** Part 2
*/

#include "cc.def"

/*
** external references in parts 1 & 2
*/
extern char
  symtab[SYMTBSZ],
  stage[STAGESIZE],
  macn[MACNSIZE],
  macq[MACQSIZE],
  pline[LINESIZE],
  mline[LINESIZE],
  alarm, *glbptr, *line, *lptr, *cptr, *cptr2,  *cptr3,
 *locptr, msname[NAMESIZE],  optimize,  pause, library, quote[2],
 *stagelast, *stagenext;
extern int
  wq[WQTABSZ],
  mack, linectr, includectr, charcount, filecount,
  includestack[INCLUDESTACKMAX],
  charstack[LINESIZE],
  filestack[FILESTACKMAX],
  filebuffer[LINESIZE],
  ccode,  ch,  csp,  eof,  errflag,  iflevel, errorptr,
  input,  input2,  listfp,  macptr,  nch,
  nxtlab,  op[16],  opindex,  opsize,  output,  pptr,
  skiplevel,  *wqptr;
extern int
  openin(), popfile(),reversearray();

/*
** external references in part 4
*/
#ifdef OPTIMIZE
extern int
  peephole();
#endif
!Funky!Stuff!
echo x - cc21.c
cat >cc21.c <<'!Funky!Stuff!'
#include "cc2.c"

junk() {
  if(an(inbyte())) while(an(ch)) gch();
  else while(an(ch)==0) {
    if(ch==0) break;
    gch();
    }
  blanks();
  }

endst() {
  blanks();
  return ((streq(lptr,";")|(ch==0)));
  }

illname() {
  error("illegal symbol");
  junk();
  }
  

multidef(sname)  char *sname; {
  error("already defined");
  }

needtoken(str)  char *str; {
  if (match(str)==0) error("missing token");
  }

needlval() {
  error("must be lvalue");
  }

findglb(sname)  char *sname; {
  if (search( sname, STARTGLB, SYMMAX, ENDGLB, NUMGLBS, NAME))
	 return cptr;
  return 0;
  }

findloc(sname)  char *sname;  {
  cptr = locptr - 1;  /* search backward for block locals */
  while(cptr > STARTLOC) {
    cptr = cptr - *cptr;
    if(astreq(sname, cptr, NAMEMAX)) return (cptr - NAME);
    cptr = cptr - NAME - 1;
    }
  return 0;
  }

addsym(sname, id, typ, value, lgptrptr, class)
  char *sname, id, typ;
  int value, class, *lgptrptr;
{
  if(lgptrptr == &glbptr) {
    if(cptr2=findglb(sname)) return cptr2;
	 if (cptr == 0) {
		 error("global symbol table overflow");
		 return 0;
	 }
  }
  else {
    if(locptr > (ENDLOC-SYMMAX)) {
      error("local symbol table overflow");
      abort(ERRCODE);
      }
    cptr = *lgptrptr;
    }
  cptr[IDENT]=id;
  cptr[TYPE]=typ;
  cptr[CLASS]=class;
  putint(value, cptr+OFFSET, OFFSIZE);
  cptr3 = cptr2 = cptr + NAME;
  while(an(*sname)) *cptr2++ = *sname++;
  if (lgptrptr == &locptr) {
	 *cptr2 = cptr2 - cptr3;  /* set length */
	 *lgptrptr = ++cptr2;
  }
  return cptr;
  }

nextsym(entry) char *entry; {
  entry = entry + NAME;
  while(*entry++ >= ' '); /* find length byte */
  return entry;
  }  

/*
** get integer of length len from address addr
** (byte sequence set by "putint")
*/
getint(addr, len) char *addr; int len; {
  int i;
  i = *(addr + --len);  /* high order byte sign extended */
  while(len--) i = (i << 8) | *(addr+len)&255;
  return i;
  }

/*
** put integer i of length len into address addr
** (low byte first)
*/
putint(i, addr, len) char *addr; int i, len; {
  while(len--) {
    *addr++ = i;
    i = i>>8;
    }
  }

/*
** test if next input string is legal symbol name
*/
symname(sname, ucase) char *sname; int ucase; {
  int k;char c;
  blanks();
  if(alpha(ch)==0) return 0;
  k=0;
  while(an(ch)) {
    if(ucase)
      sname[k]=upper(gch());
    else
      sname[k]=gch();
    if(k<NAMEMAX) ++k;
    }
  sname[k]=0;
  return 1;
  }

/*
** force upper case alphabetics
*/
upper(c)  char c; {
  if((c >= 'a') & (c <= 'z')) return (c - 32);
  else return c;
  }

/*
** force upper case alphabetics
*/
lower(c) char c; {
  if((c >= 'A') & (c <= 'Z')) return (c + 32);
  else return c;
  }

/*
** return next avail internal label number
*/
getlabel() {
  return(++nxtlab);
  }

/*
** post a label in the program
*/
postlabel(label) int label; {
  printlabel(label);
  col();
  nl();
  }

/*
** print specified number as a label
*/
printlabel(label)  int label; {
  outstr("cc");
  outdec(label);
  }

/*
** test if given character is alphabetic
*/
alpha(c)  char c; {
  return (((c>='a')&(c<='z'))|((c>='A')&(c<='Z'))|(c=='_'));
  }

/*
** test if given character is numeric
*/
numeric(c)  char c; {
  return((c>='0')&(c<='9'));
  }

/*
** test if given character is alphanumeric
*/
an(c)  char c; {
  return ((alpha(c))|(numeric(c)));
  }

addwhile(ptr)  int ptr[]; {
  int k;
  ptr[WQSP]=csp;           /* and stk ptr */
  ptr[WQLOOP]=getlabel();  /* and looping label */
  ptr[WQEXIT]=getlabel();   /* and exit label */
  if (wqptr==WQMAX) {
    error("too many active loops");
    abort(ERRCODE);
    }
  k=0;
  while (k<WQSIZ) *wqptr++ = ptr[k++];
  }

delwhile() {
  if(wqptr > wq) wqptr=wqptr-WQSIZ;
  }

readwhile(ptr) int *ptr; {
  if (ptr <= wq) {
    error("out of context");
    return 0;
  }
  else return (ptr-WQSIZ);
 }

white() {
  /* test for stack/program overlap */
  /* primary -> symname -> blanks -> white */
  if(*lptr==' ') return 1;
  if(*lptr==9)   return 1;
  return 0;
  }

gch() {
  int c;
  if(c=ch) bump(1);
  return c;
  }

bump(n) int n; {
  if(n) lptr=lptr+n;
  else  lptr=line;
  if(ch = nch = *lptr) nch = *(lptr + 1);
  }

kill() {
  *line=0;
  bump(0);
  }

inbyte()  {
  while(ch==0) {
    if (eof) return 0;
    preprocess();
    }
  return gch();
  }

inline() {
  int k,unit;
  while(1) {
    if (input==EOF) openin();
		if (eof && library) appendlib();  /* append user library */
		if (eof) return;
    if(fgets(line, LINEMAX, input)==NULL) {
      fclose(input);
      input = popfile();
      }
    else {
      bump(0);
      linectr++;
      return;
      }
    }
  }

undefined(sname)
char *sname;
{
	char c;
	if (errflag) return;
	if (errorptr)  /* if user wants pointer to variable */
		error("Undefined variable");
	else {
		while(*sname) {
	  		c = lower(*sname++);
	  		printf("%c",c);
      }
		printf(" in ");
		error("Undefined variable");
		if ((ch == ')') || (ch == ',' )) bump(1); /* if in function call */
	}
}

appendlib() 
{
	int index,i,j;
	long tempstack[50];
	char c,str[LINESIZE];
	filecount = FILESTACKMAX - 1;
	openin();								/* get library file from filestack[FILESTACKMAX] */
	eof = library = NO;  		/* no nested appendlibs */
	j = 0;
	preprocess();    			 /* get first line  */
	unresolved(tempstack);
	while (eof == NO) {
		cptr = STARTGLB;
		while ((index = tempstack[j++]) != YES) {
			i = 0;
			while((c = cptr[index + NAME + i]) != NULL) str[i++] = lower(c);
			str[i] = NULL;
			if(match(str)) {
				bump(0);
				newfunc();  /* parses and resolves functions on symtab */
				break;
			}
		}
		ch = EOL;       /* force blanks to get new line */
		j = 0;
		blanks();       
	}
}

unresolved(array)
long array[];
{
	int index,i;
	cptr = STARTGLB;
	index = i = 0;
	while(&cptr[index] < ENDGLB) {
		if (cptr[index + IDENT])
			if (cptr[index + OFFSET] == NULL){
			 array[i++] = index;
		}
		index = index + SYMMAX;
	}
	array[i] = YES;   /* end-of-stack marker */
}

abort(n)
int n;
{
	exit(1);
}
!Funky!Stuff!
echo x - cc22.c
cat >cc22.c <<'!Funky!Stuff!'
static char *cc22_id = "%W% %G%";

#include "cc2.c"

ifline() {
  while(1) {
    inline();
    if(eof) return;
    if(match("#ifdef")) {
      ++iflevel;
      if(skiplevel) continue;
      blanks();
	   if (search(lptr, macn, NAMESIZE + 2, MACNEND, MACNBR, 0) == 0)
        skiplevel=iflevel;
      continue;
      }
    if(match("#ifndef")) {
      ++iflevel;
      if(skiplevel) continue;
      blanks();
	   if (search(lptr, macn, NAMESIZE + 2, MACNEND, MACNBR, 0))
        skiplevel=iflevel;
      continue;
      }
    if(match("#else")) {
      if(iflevel) {
        if(skiplevel==iflevel) skiplevel=0;
        else if(skiplevel==0)  skiplevel=iflevel;
        }
      else noiferr();
      continue;
      }
    if(match("#endif")) {
      if(iflevel) {
        if(skiplevel==iflevel) skiplevel=0;
        --iflevel;
        }
      else noiferr();
      continue;
      }
    if(skiplevel) continue;
    if(listfp) {
      if(listfp==output) cout(';', output);
      lout(line, listfp);
      }
    if(ch==0) continue;
    break;
    }
  }

keepch(c)  char c; {
  if(pptr<LINEMAX) pline[++pptr]=c;
  }

preprocess() {
  int k;
  char c;
  if(ccode) {
    line=mline;
    ifline();
    if(eof) return;
    }
  else {
    line=pline;
    inline();
    return;
    }
  pptr = -1;
  while(ch) {
    if(white()) {
      keepch(' ');
      while(white()) gch();
      }
    else if(ch=='"') {
      keepch(ch);
      gch();
      while((ch!='"')|((*(lptr-1)==92)&(*(lptr-2)!=92))) {
        if(ch==0) {
          error("no quote");
          break;
          }
        keepch(gch());
        }
      gch();
      keepch('"');
      }
    else if(ch==39) {
      keepch(39);
      gch();
      while((ch!=39)|((*(lptr-1)==92)&(*(lptr-2)!=92))) {
        if(ch==0) {
          error("no apostrophe");
          break;
          }
        keepch(gch());
        }
      gch();
      keepch(39);
      }
    else if((ch=='/')&(nch=='*')) {
      bump(2);
      while(((ch=='*')&(nch=='/'))==0) {
        if(ch) bump(1);
        else {
          ifline();
          if(eof) break;
          }
        }
      bump(2);
      }
    else if(an(ch)) {
      k=0;
      while(an(ch)) {
        if(k<NAMEMAX) msname[k++]=ch;
        gch();
        }
      msname[k]=0;
		if (search(msname, macn, NAMESIZE + 2, MACNEND, MACNBR, 0)) {
			k = getint(cptr + NAMESIZE, 2);
			while (c = macq[k++]) keepch(c);
      }
      else {
        k=0;
        while(c=msname[k++]) keepch(c);
        }
      }
    else keepch(gch());
    }
  if(pptr>=LINEMAX) error("line too long");
  keepch(0);
  line=pline;
  bump(0);
  }

noiferr() {
  error("no matching #if...");
  errflag=0;
  }

addmac() {
  int k;
  char c;
  if(symname(msname, NO)==0) {
    illname();
    kill();
    return;
    }
  k=0;
  if (search(msname, macn, NAMESIZE + 2, MACNEND, MACNBR, 0) == 0) {
	 if (cptr2 = cptr) while (*cptr2++ = msname[k++]);
	 else {
		 error("macro name table full");
		 return;
	 }
  }
  putint(macptr, cptr + NAMESIZE, 2);
  while(white()) gch();
  while((c = gch()) != EOL) putmac(c);
  putmac(0);
  if(macptr>=MACMAX) {
    error("macro string queue full"); abort(ERRCODE);
    }
  }

putmac(c)  char c; {
  macq[macptr]=c;
  if(macptr<MACMAX) ++macptr;
  return c;
  }

/*
** search for symbol match
** on return cptr points to slot found or empty slot
*/
search(sname, buf, len, end, max, off)
  char *sname, *buf, *end;  int len, max, off; {
  cptr=cptr2=buf+((hash(sname)%(max-1))*len);
  while(*cptr != 0) {
    if(astreq(sname, cptr+off, NAMEMAX)) return 1;
    if((cptr=cptr+len) >= end) cptr=buf;
    if(cptr == cptr2) return (cptr=0);
    }
  return 0;
  }

hash(sname) char *sname; {
  int i, c;
  i=0;
  while(c = *sname++) i=(i<<1)+c;
  return i;
  }

setstage(before, start) int *before, *start; {
  if((*before=stagenext)==0) stagenext=stage;
  *start=stagenext;
  }

clearstage(before, start) char *before, *start; {
  *stagenext=0;
  if(stagenext=before) return;
  if(start) {
#ifdef OPTIMIZE
    peephole(start);
#else
    sout(start, output);
#endif
    }
  }

outdec(number)  int number; {
  int k,zs;
  char c;
  zs = 0;
  k=10000;
  if (number<0) {
    number=(-number);
    outbyte('-');
    }
  while (k>=1) {
    c=number/k + '0';
    if ((c!='0')|(k==1)|(zs)) {
      zs=1;
      outbyte(c);
      }
    number=number%k;
    k=k/10;
    }
  }

ol(ptr)  char ptr[];  {
  ot(ptr);
  nl();
  }

ot(ptr) char ptr[]; {
#ifdef TAB
  tab();
#endif
  outstr(ptr);
  }

outstr(ptr) char ptr[]; {
  /* must work with symbol table names terminated by length */
  while(*ptr >= ' ') outbyte(*ptr++);
  }

outbyte(c) char c; {
  if(stagenext) {
    if(stagenext==stagelast) {
      error("staging buffer overflow");
      return 0;
      }
    else *stagenext++ = c;
    }
  else cout(c,output);
  return c;
  }

cout(c, fd) char c; int fd; {
  if(fputc(c, fd)==EOF) xout();
  }

sout(string, fd) char *string; int fd; {
  if(fputs(string, fd)==EOF) xout();
  }

lout(line, fd) char *line; int fd; {
  sout(line, fd);
  cout('\n', fd);
  }

xout() {
  fputs("output error\n", stderr);
  abort(ERRCODE);
  }

nl() {
  outbyte('\n');
  }

tab() {
  outbyte(TAB);
  }

col() {
#ifdef COL
  outbyte(':');
#endif
  }

error(msg)
char msg[];
{
  int i;
  i = 0;
  if (errflag) return;
  else errflag=1;
  if (errorptr == NO) {
      while (filebuffer[i]) printf("%c",filebuffer[i++]);
		printf(", %s %d: %s\n","line",linectr,msg);
  }
  else {
		lout(line, stderr);
  		errout(msg, stderr);
  }
  if(alarm) fputc(7, stderr);
  if(pause) while(fgetc(stdin)!='\n');
  if(listfp>0) errout(msg, listfp);
  }

errout(msg, fp) char msg[]; int fp; {
  int k; k=line+2;
  while(k++ <= lptr) cout(' ', fp);
  lout("/\\", fp);
  sout("**** ", fp); lout(msg, fp);
  }

streq(str1,str2)  char str1[],str2[]; {
  int k;
  k=0;
  while (str2[k]) {
    if ((str1[k])!=(str2[k])) return 0;
    ++k;
    }
  return k;
 }

astreq(str1,str2,len)  char str1[],str2[];int len; {
  int k;
  k=0;
  while (k<len) {
    if ((str1[k])!=(str2[k]))break;
    /*
    ** must detect end of symbol table names terminated by
    ** symbol length in binary
    */
    if(str1[k] < ' ') break;
    if(str2[k] < ' ') break;
    ++k;
    }
  if (an(str1[k]))return 0;
  if (an(str2[k]))return 0;
  return k;
 }

match(lit)  char *lit; {
  int k;
  blanks();
  if (k=streq(lptr,lit)) {
    bump(k);
    return 1;
    }
  return 0;
  }

amatch(lit,len)  char *lit;int len; {
  int k;
  blanks();
  if (k=astreq(lptr,lit,len)) {
    bump(k);
    while(an(ch)) inbyte();
    return 1;
    }
  return 0;
 }

nextop(list) char *list; {
  char op[4];
  opindex=0;
  blanks();
  while(1) {
    opsize=0;
    while(*list > ' ') op[opsize++] = *list++;
    op[opsize]=0;
    if(opsize=streq(lptr, op))
      if((*(lptr+opsize) != '=')&
         (*(lptr+opsize) != *(lptr+opsize-1)))
         return 1;
    if(*list) {
      ++list;
      ++opindex;
      }
    else return 0;
    }
  }

blanks() {
  while(1) {
    while((ch != EOL) && (ch != NULL)) {
      if(white()) gch();
      else return;
    }
    if(line==mline) return;
    preprocess();
    if(eof)break;
  }
}

dumpglbs()
{
	int index,i;
	char c,*pointer,*pointer1,temp[LINESIZE];

	cptr = STARTGLB;
	index = 0;
	while(&cptr[index] < ENDGLB) {
	     if (cptr[index + IDENT]) {
				if (cptr[index + OFFSET] == NULL) {
					i = 0;
					printf("Undefined function: ");
			   	while (cptr[index + NAME + i]) {
						c = lower(cptr[index + NAME + i++]);
						printf("%c",c);
					}
					printf("\n");
				}
			}
			index = index + SYMMAX;
	}
}
!Funky!Stuff!
echo x - cc3.c
cat >cc3.c <<'!Funky!Stuff!'
static char *cc3_id = "%W% %G%";
/*
** Small-C Compiler Version 2.0
**
** Copyright 1982 J. E. Hendrix
**
** Part 3
*/

#include "cc.def"

/*
** external references in part 1
*/
extern char
  stage[STAGESIZE],
  litq[LITABSZ],
 *glbptr, *lptr,  ssname[NAMESIZE],  quote[2], *stagenext;
extern int
  ch,  csp,  litlab,  litptr,  nch,  op[16],  op2[16],
  oper,  opindex,  opsize;

extern int 
	heir1(), heir3(), heir4(), heir5(), heir6(), heir7(),
   heir8(), heir9(), heir10(), heir11(), heir12(), heir13();

/*
** external references in part 2
*/
extern int
  addsym(),  blanks(),  bump(),  clearstage(),  endst(),
  error(),  findglb(),  findloc(),  gch(),  getlabel(),
  inbyte(),  junk(),  match(),  needlval(),  needtoken(),
  nextop(),  nl(),  numeric(),  outbyte(),  outdec(),
  outstr(),  postlabel(),  printlabel(),  putint(),
  setstage(),  streq(),  symname();

/*
** external references in part 4
*/
extern int
  add(),  and(),  asl(),  asr(),  call(),  callstk(),
  com(),  dec(),  div(),  doublereg(),  eq(),  eq0(),
  ge(),  ge0(),  getloc(),  getmem(),  gt(),  gt0(),
  immed(),  immed2(),  inc(),  indirect(),  jump(),
  le(),  le0(),  lneg(),  loadargc(),  lt(),  lt0(),
  mod(),  modstk(),  move(),  mult(),  ne(),  ne0(),
  neg(),  or(),  pop(),  push(),  putmem(),
  putstk(),  ret(),  smartpop(),  sub(),  swap(),  swapstk(),
  testjump(),  uge(),  ugt(),  ule(),  ult(),  ult0(),
  xor(),  zerojump();
!Funky!Stuff!
echo x - cc31.c
cat >cc31.c <<'!Funky!Stuff!'
static char *cc31_id = "%W% %G%";

#include "cc3.c"

/*
** lval[0] - symbol table address, else 0 for constant
** lval[1] - type of indirect obj to fetch, else 0 for static
** lval[2] - type of pointer or array, else 0 for all other
** lval[3] - true if constant expression
** lval[4] - value of constant expression
** lval[5] - true if secondary register altered
** lval[6] - function address of highest/last binary operator
** lval[7] - stage address of "oper 0" code, else 0
*/

/*
** skim over terms adjoining || and && operators
*/
skim(opstr, testfunc, dropval, endval, heir, lval)
  char *opstr;
  int testfunc, dropval, endval, lval[]; 
  int (*heir)();
{
  int k, hits, droplab, endlab;
  hits=0;
  while(1) {
    k=plunge1(heir, lval);
    if(nextop(opstr)) {
      bump(opsize);
      if(hits==0) {
        hits=1;
        droplab=getlabel();
        }
      dropout(k, testfunc, droplab, lval);
      }
    else if(hits) {
      dropout(k, testfunc, droplab, lval);
      const(endval);
      jump(endlab=getlabel());
      postlabel(droplab);
      const(dropval);
      postlabel(endlab);
      lval[1]=lval[2]=lval[3]=lval[7]=0;
      return 0;
      }
    else return k;
    }
  }

/*
** test for early dropout from || or && evaluations
*/
dropout(k, testfunc, exit1, lval)
int k, exit1, lval[]; 
int (*testfunc)();
{
  if(k) rvalue(lval);
  else if(lval[3]) const(lval[4]);
  (*testfunc)(exit1); /* jumps on false */
  }

/*
** plunge to a lower level
*/
plunge(opstr, opoff, heir, lval)
  char *opstr;
  int opoff, lval[]; 
  int (*heir)();
{
  int k, lval2[8];
  k=plunge1(heir, lval);
  if(nextop(opstr)==0) return k;
  if(k) rvalue(lval);
  while(1) {
    if(nextop(opstr)) {
      bump(opsize);
      opindex=opindex+opoff;
      plunge2(op[opindex], op2[opindex], heir, lval, lval2);
      }
    else return 0;
    }
  }

/*
** unary plunge to lower level
*/
plunge1(heir, lval)
  int lval[]; 
  int (*heir)();
{
  char *before, *start;
  int k;
  setstage(&before, &start);
  k=(*heir)(lval);
  if(lval[3]) clearstage(before,0); /* load constant later */
  return k;
  }

/*
** binary plunge to lower level
*/
plunge2(oper, oper2, heir, lval, lval2)
  int (*oper)();
  int (*oper2)();
  int lval[], lval2[];
  int (*heir)();
{
  char *before, *start;
  setstage(&before, &start);
  lval[5]=1;          /* flag secondary register used */
  lval[7]=0;          /* flag as not "... oper 0" syntax */
  if(lval[3]) {       /* constant on left side not yet loaded */
    if(plunge1(heir, lval2)) rvalue(lval2);
    if(lval[4]==0) lval[7]=stagenext;
    const2(lval[4]<<dbltest(lval2, lval));
    }
  else {              /* non-constant on left side */
    push();
    if(plunge1(heir, lval2)) rvalue(lval2);
    if(lval2[3]) {    /* constant on right side */
      if(lval2[4]==0) lval[7]=start;
      if(oper==add) { /* may test other commutative operators */
        csp=csp+2;
        clearstage(before, 0);
        const2(lval2[4]<<dbltest(lval, lval2));   /* load secondary */
        }
      else {
        const(lval2[4]<<dbltest(lval, lval2));    /* load primary */
        smartpop(lval2, start);
        }
      }
    else {            /* non-constants on both sides */
      smartpop(lval2, start);
      if((oper==add)|(oper==sub)) {
        if(dbltest(lval,lval2)) doublereg();
        if(dbltest(lval2,lval)) {
          swap();
          doublereg();
          if(oper==sub) swap();
          }
        }
      }
    }
  if(oper) {
    if(lval[3]=lval[3]&lval2[3]) {
      lval[4]=calc(lval[4], oper, lval2[4]);
      clearstage(before, 0);  
      lval[5]=0;
      }
    else {
      if((lval[2]==0)&(lval2[2]==0)) {
        (*oper)();
        lval[6]=oper;    /* identify the operator */
        }
      else {
        (*oper2)();
        lval[6]=oper2;   /* identify the operator */
        }
      }
    if(oper==sub) {
      if((lval[2]==CINT)&(lval2[2]==CINT)) {
        swap();
        const(1);
        asr();  /** div by 2 **/
        }
      }
    if((oper==sub)|(oper==add)) result(lval, lval2);
    }
  }

calc(left, oper, right) int left, oper, right; {
       if(oper ==  or) return (left  |  right);
  else if(oper == xor) return (left  ^  right);
  else if(oper == and) return (left  &  right);
  else if(oper ==  eq) return (left  == right);
  else if(oper ==  ne) return (left  != right);
  else if(oper ==  le) return (left  <= right);
  else if(oper ==  ge) return (left  >= right);
  else if(oper ==  lt) return (left  <  right);
  else if(oper ==  gt) return (left  >  right);
  else if(oper == asr) return (left  >> right);
  else if(oper == asl) return (left  << right);
  else if(oper == add) return (left  +  right);
  else if(oper == sub) return (left  -  right);
  else if(oper ==mult) return (left  *  right);
  else if(oper == div) return (left  /  right);
  else if(oper == mod) return (left  %  right);
  else return 0;
  }

expression(const, val) int *const, *val;  {
  int lval[8];
  if(heir1(lval)) rvalue(lval);
  if(lval[3]) {
    *const=1;
    *val=lval[4];
    }
  else *const=0;
  }

heir1(lval)  int lval[];  {
  int k,lval2[8], oper;
  k=plunge1(heir3, lval);
  if(lval[3]) const(lval[4]);
       if(match("|="))  oper=or;
  else if(match("^="))  oper=xor;
  else if(match("&="))  oper=and;
  else if(match("+="))  oper=add;
  else if(match("-="))  oper=sub;
  else if(match("*="))  oper=mult;
  else if(match("/="))  oper=div;
  else if(match("%="))  oper=mod;
  else if(match(">>=")) oper=asr;
  else if(match("<<=")) oper=asl;
  else if(match("="))   oper=0;
  else return k;
  if(k==0) {
    needlval();
    return 0;
    }
  if(lval[1]) {
    if(oper) {
      push();
      rvalue(lval);
      }
    plunge2(oper, oper, heir1, lval, lval2);
    if(oper) pop();
    }
  else {
    if(oper) {
      rvalue(lval);
      plunge2(oper, oper, heir1, lval, lval2);
      }
    else {
      if(heir1(lval2)) rvalue(lval2);
      lval[5]=lval2[5];
      }
    }
  store(lval);
  return 0;
  }

heir3(lval)  int lval[]; {
  return skim("||", eq0, 1, 0, heir4, lval);
  }

heir4(lval)  int lval[]; {
  return skim("&&", ne0, 0, 1, heir5, lval);
  }

heir5(lval)  int lval[]; {
  return plunge("|", 0, heir6, lval);
  }

heir6(lval)  int lval[]; {
  return plunge("^", 1, heir7, lval);
  }

heir7(lval)  int lval[]; {
  return plunge("&", 2, heir8, lval);
  }

heir8(lval)  int lval[];  {
  return plunge("== !=", 3, heir9, lval);
  }

heir9(lval)  int lval[];  {
  return plunge("<= >= < >", 5, heir10, lval);
  }

heir10(lval)  int lval[];  {
  return plunge(">> <<", 9, heir11, lval);
  }

heir11(lval)  int lval[];  {
  return plunge("+ -", 11, heir12, lval);
  }

heir12(lval)  int lval[];  {
  return plunge("* / %", 13, heir13, lval);
  }
!Funky!Stuff!
echo x - cc32.c
cat >cc32.c <<'!Funky!Stuff!'
static char *cc32_id = "%W% %G%";

#include "cc3.c"

heir13(lval)  int lval[];  {
  int k;
  char *ptr;
  if(match("++")) {                   /* ++lval */
    if(heir13(lval)==0) {
      needlval();
      return 0;
      }
    step(inc, lval);
    return 0;
    }
  else if(match("--")) {              /* --lval */
    if(heir13(lval)==0) {
      needlval();
      return 0;
      }
    step(dec, lval);
    return 0;
    }
  else if (match("~")) {              /* ~ */
    if(heir13(lval)) rvalue(lval);
    com();
#ifdef PHASE2
    lval[4] = ~lval[4];
#endif
    return 0;
    }
  else if (match("!")) {              /* ! */
    if(heir13(lval)) rvalue(lval);
    lneg();
#ifdef PHASE2
    lval[4] = !lval[4];
#endif
    return 0;
    }
  else if (match("-")) {              /* unary - */
    if(heir13(lval)) rvalue(lval);
    neg();
    lval[4] = -lval[4];
    return 0;
    }
  else if(match("*")) {               /* unary * */
    if(heir13(lval)) rvalue(lval);
    if(ptr=lval[0])lval[1]=ptr[TYPE];
    else lval[1]=CINT;
    lval[2]=0;  /* flag as not pointer or array */
    lval[3]=0;  /* flag as not constant */
    return 1;
    }
  else if(match("&")) {               /* unary & */
    if(heir13(lval)==0) {
      error("illegal address");
      return 0;
      }
    ptr=lval[0];
    lval[2]=ptr[TYPE];
    if(lval[1]) return 0;
    /* global & non-array */
    address(ptr);
    lval[1]=ptr[TYPE];
    return 0;
    }
  else {
    k=heir14(lval);
    if(match("++")) {                 /* lval++ */
      if(k==0) {
        needlval();
        return 0;
        }
      step(inc, lval);
      dec(lval[2]>>2);
      return 0;
      }
    else if(match("--")) {            /* lval-- */
      if(k==0) {
        needlval();
        return 0;
        }
      step(dec, lval);
      inc(lval[2]>>2);
      return 0;
      }
    else return k;
    }
  }

heir14(lval)  int *lval; {
  int k, const, val, lval2[8];
  char *ptr, *before, *start;
  k=primary(lval);
  ptr=lval[0];
  blanks();
  if((ch=='[')|(ch=='(')) {
    lval[5]=1;    /* secondary register will be used */
    while(1) {
      if(match("[")) {                /* [subscript] */
        if(ptr==0) {
          error("can't subscript");
          junk();
          needtoken("]");
          return 0;
          }
        else if(ptr[IDENT]==POINTER)rvalue(lval);
        else if(ptr[IDENT]!=ARRAY) {
          error("can't subscript");
          k=0;
          }
        setstage(&before, &start);
        lval2[3]=0;
        plunge2(0, 0, heir1, lval2, lval2); /* lval2 deadend */
        needtoken("]");
        if(lval2[3]) {
          clearstage(before, 0);
          if(lval2[4]) {
            if(ptr[TYPE]==CINT) const2(lval2[4]<<LBPW);
            else                const2(lval2[4]);
            add();
            }
          }
        else {
          if(ptr[TYPE]==CINT) doublereg();
          add();
          }
        lval[0]=lval[2]=0;
        lval[1]=ptr[TYPE];
        k=1;
        }
      else if(match("(")) {           /* function(...) */
        if(ptr==0) callfunction(0);
        else if(ptr[IDENT]!=FUNCTION) {
          rvalue(lval);
          callfunction(0);
          }
        else callfunction(ptr);
        k=lval[0]=lval[3]=0;
        }
      else return k;
      }
    }
  if(ptr==0) return k;
  if(ptr[IDENT]==FUNCTION) {
    address(ptr);
    return 0;
    }
  return k;
  }

primary(lval)  int *lval; {
  char *ptr;
  int k;
  if(match("(")) {                    /* (expression) */
    k=heir1(lval);
    needtoken(")");
    return k;
    }
  putint(0, lval, 8<<LBPW); /* clear lval array */
  if(symname(ssname, YES)) {
    if(ptr=findloc(ssname)) {
      if(ptr[IDENT]==LABEL) {
        experr();
        return 0;
        }
      getloc(ptr);
      lval[0]=ptr;
      lval[1]=ptr[TYPE];
      if(ptr[IDENT]==POINTER) {
        lval[1]=CINT;
        lval[2]=ptr[TYPE];
        }
      if(ptr[IDENT]==ARRAY) {
        lval[2]=ptr[TYPE];
        return 0;
        }
      else return 1;
      }
    if(ptr=findglb(ssname))
      if(ptr[IDENT]!=FUNCTION) {
        lval[0]=ptr;
        lval[1]=0;
        if(ptr[IDENT]!=ARRAY) {
          if(ptr[IDENT]==POINTER) lval[2]=ptr[TYPE];
          return 1;
          }
        address(ptr);
        lval[1]=lval[2]=ptr[TYPE];
        return 0;
        }
     if (match("(")) {  /* if user defined function  */
    	 ptr=addsym(ssname, FUNCTION, CINT, 0, &glbptr, STATIC);
		 lval[0] = ptr;
		 lval[1] = 0;
		 nch = ch;      /* these next statements are similar to */
		 ch = '(';      /* 'ungetting' a char from the input line */
		 --lptr;
		 return 0;
     }
     undefined(ssname);   /* outputs the otherwise undefined name */
     return 0;
  }
  if(constant(lval)==0) experr();
  return 0;
  }

experr() {
  error("invalid expression");
  const(0);
  junk();
  }
 
callfunction(ptr)  char *ptr; { /* symbol table entry or 0 */
  int nargs, const, val;
  nargs=0;
  blanks();               /* already saw open paren */
  if(ptr==0) push();      /* calling HL */
  while(streq(lptr,")")==0) {
    if(endst()) break;
    expression(&const, &val);
    if(ptr==0) swapstk(); /* don't push addr */
    push();               /* push argument */
    nargs=nargs+BPW;      /* count args*BPW */
    if (match(",")==0) break;
    }
  needtoken(")");
  if(streq(ptr+NAME, "CCARGC")==0) loadargc(nargs>>LBPW);
  if(ptr) call(ptr+NAME);
  else callstk();
  csp=modstk(csp+nargs, YES);
  }
!Funky!Stuff!
echo x - cc33.c
cat >cc33.c <<'!Funky!Stuff!'
static char *cc33_id = "%W% %G%";

#include "cc3.c"

/*
** true if val1 -> int pointer or int array and val2 not ptr or array
*/
dbltest(val1,val2) int val1[], val2[]; {
  if(val1[2]!=CINT) return 0;
  if(val2[2]) return 0;
  return 1;
  }

/*
** determine type of binary operation
*/
result(lval, lval2) int lval[], lval2[]; {
  if((lval[2]!=0)&(lval2[2]!=0)) {
    lval[2]=0;
    }
  else if(lval2[2]) {
    lval[0]=lval2[0];
    lval[1]=lval2[1];
    lval[2]=lval2[2];
    }
  }

step(oper, lval) int (*oper)(), lval[]; {
  if(lval[1]) {
    if(lval[5]) {
      push();
      rvalue(lval);
      (*oper)(lval[2]>>2);
      pop();
      store(lval);
      return;
      }
    else {
      move();
      lval[5]=1;
      }
    }
  rvalue(lval);
  (*oper)(lval[2]>>2);
  store(lval);
  }

store(lval)  int lval[]; {
  if(lval[1]) putstk(lval);
  else        putmem(lval);
  }

rvalue(lval) int lval[]; {
  if ((lval[0]!=0)&(lval[1]==0)) getmem(lval);
  else                         indirect(lval);
  }

test(label, parens)  int label, parens;  {
  int lval[8];
  char *before, *start;
  if(parens) needtoken("(");
  while(1) {
    setstage(&before, &start);
    if(heir1(lval)) rvalue(lval);
    if(match(",")) clearstage(before, start);
    else break;
    }
  if(parens) needtoken(")");
  if(lval[3]) {  /* constant expression */
    clearstage(before, 0);
    if(lval[4]) return;
    jump(label);
    return;
    }
  if(lval[7]) {  /* stage address of "oper 0" code */
    oper=lval[6];/* operator function address */
         if((oper==eq)|
            (oper==ule)) zerojump(eq0, label, lval);
    else if((oper==ne)|
            (oper==ugt)) zerojump(ne0, label, lval);
    else if (oper==gt)   zerojump(gt0, label, lval);
    else if (oper==ge)   zerojump(ge0, label, lval);
    else if (oper==uge)  clearstage(lval[7],0);
    else if (oper==lt)   zerojump(lt0, label, lval);
    else if (oper==ult)  zerojump(ult0, label, lval);
    else if (oper==le)   zerojump(le0, label, lval);
    else                 testjump(label);
    }
  else testjump(label);
  clearstage(before, start);
  }

constexpr(val) int *val; {
  int const;
  char *before, *start;
  setstage(&before, &start);
  expression(&const, val);
  clearstage(before, 0);  /* scratch generated code */
  if(const==0) error("must be constant expression");
  return const;
  }

const(val) int val; {
  immed();
  outdec(val);
  nl();
  }

const2(val) int val; {
  immed2();
  outdec(val);
  nl();
  }

constant(lval)  int lval[]; {
  lval=lval+3;
  *lval=1;       /* assume it will be a constant */
  if (number(++lval)) immed();
  else if (pstr(lval)) immed();
  else if (qstr(lval)) {
    *(lval-1)=0; /* nope, it's a string address */
    immed();
    printlabel(litlab);
    outbyte('+');
    }
  else return 0;
  outdec(*lval);
  nl();
  return 1;
  }

number(val)  int val[]; {
  int k, minus;
  k=minus=0;
  while(1) {
    if(match("+")) ;
    else if(match("-")) minus=1;
    else break;
    }
  if(numeric(ch)==0)return 0;
  while (numeric(ch)) k=k*10+(inbyte()-'0');
  if (minus) k=(-k);
  val[0]=k;
  return 1;
  }

address(ptr) char *ptr; {
  immed();
  outstr(ptr+NAME);
  nl();
  }

pstr(val)  int val[]; {
  int k;
  k=0;
  if (match("'")==0) return 0;
  while(ch!=39)    k=(k&255)*256 + (litchar()&255);
  ++lptr;
  val[0]=k;
  return 1;
  }

qstr(val)  int val[]; {
  char c;
  if (match('"')==0) return 0;
  val[0]=litptr;
  while (ch!='"') {
    if(ch==0) break;
    stowlit(litchar(), 1);
    }
  gch();
  litq[litptr++]=0;
  return 1;
  }

stowlit(value, size) int value, size; {
  if((litptr+size) >= LITMAX) {
    error("literal queue overflow"); abort(ERRCODE);
    }
  putint(value, litq+litptr, size);
  litptr=litptr+size;
  }

/*
** return current literal char & bump lptr
*/
litchar() {
  int i, oct;
  if((ch!=92)|(nch==0)) return gch();
  gch();
  if(ch=='n') {gch(); return 13;} /* CR */
  if(ch=='t') {gch(); return  9;} /* HT */
  if(ch=='b') {gch(); return  8;} /* BS */
  if(ch=='f') {gch(); return 12;} /* FF */
  i=3; oct=0;
  while(((i--)>0)&(ch>='0')&(ch<='7')) oct=(oct<<3)+gch()-'0';
  if(i==2) return gch(); else return oct;
  }
!Funky!Stuff!
echo x - cc4.c
cat >cc4.c <<'!Funky!Stuff!'
static char *cc4_id = "%W% %G%";
/*
** Small-C Compiler Version 2.0
**
** Copyright 1982 J. E. Hendrix
**
** Part 4
*/

#include "cc.def"

/*
** external references in part 1
*/
extern char
 *macn, optimize, *stagenext, ssname[NAMESIZE];
extern int
  beglab,  csp, output;

/*
** external references in part 2
*/
extern int
  search(), clearstage(),  col(),  cout(),  getint(),  getlabel(),
  nl(),  numeric(),  ol(),  ot(),  printlabel(),
  lout(),  outdec(),  outstr(),  streq();

/*
** external references in part 3
*/
extern int const();
!Funky!Stuff!
echo x - cc41.c
cat >cc41.c <<'!Funky!Stuff!'
static char *cc41_id = "%w% %g%";

#include "cc4.c"

/*
** print all assembler info before any code is generated
*/
header()  {
  beglab=getlabel();
  if(beglab < 3) jump(beglab);
  }

/*
** print any assembler stuff needed at the end
*/
trailer()  {  
  if((beglab == 1)|(beglab > 9000))
  			ol("end");
  }

/*
** load # args before function call
*/
loadargc(val) int val; {
  if (search("NOCCARGC", macn, NAMESIZE + 2, MACNEND, MACNBR, 0) == 0) {
    ot("mvi a,");
    outdec(val);
    nl();
    }
  }

/*
** declare entry point
*/
entry() {
  outstr(ssname);
  col();
  nl();
  }

/*
** declare external reference
*/
external(name) char *name; {
  }

/*
** fetch object indirect to primary register
*/
indirect(lval) int lval[]; {
  if(lval[1]==CCHAR) call("ccgchar");
  else               call("ccgint");
  }

/*
** fetch a static memory cell into primary register
*/
getmem(lval)  int lval[]; {
  char *sym;
  sym=lval[0];
  if((sym[IDENT]!=POINTER)&(sym[TYPE]==CCHAR)) {
    ot("lda ");
    outstr(sym+NAME);
    nl();
    call("ccsxt");
    }
  else {
    ot("lhld ");
    outstr(sym+NAME);
    nl();
    }
  }

/*
** fetch addr of the specified symbol into primary register
*/
getloc(sym)  char *sym; {
  const(getint(sym+OFFSET, OFFSIZE)-csp);
  ol("dad sp");
  }

/*
** store primary register into static cell
*/
putmem(lval)  int lval[]; {
  char *sym;
  sym=lval[0];
  if((sym[IDENT]!=POINTER)&(sym[TYPE]==CCHAR)) {
    ol("mov a,l");
    ot("sta ");
    }
  else ot("shld ");
  outstr(sym+NAME);
  nl();
  }

/*
** put on the stack the type object in primary register
*/
putstk(lval) int lval[]; {
  if(lval[1]==CCHAR) {
    ol("mov a,l");
    ol("stax d");
    }
  else call("ccpint");
  }

/*
** move primary register to secondary
*/
move() {
  ol("mov d,h");
  ol("mov e,l");
  }

/*
** swap primary and secondary registers
*/
swap() {
  ol("xchg;;");  /* peephole() uses trailing ";;" */
  }

/*
** partial instruction to get immediate value
** into the primary register
*/
immed() {
  ot("lxi h,");
  }

/*
** partial instruction to get immediate operand
** into secondary register
*/
immed2() {
  ot("lxi d,");
  }

/*
** push primary register onto stack
*/
push() {
  ol("push h");
  csp=csp-BPW;
  }

/*
** unpush or pop as required
*/
smartpop(lval, start) int lval[]; char *start; {
  if(lval[5])  pop(); /* secondary was used */
  else unpush(start);
  }

/*
** replace a push with a swap
*/
unpush(dest) char *dest; {
  int i;
  char *sour;
  sour="\txchg;;";  /* peephole() uses trailing ";;" */
  while(*sour) *dest++ = *sour++;
  sour=stagenext;
  while(--sour > dest) { /* adjust stack references */
    if(streq(sour,"\tdad sp")) {
      --sour;
      i=BPW;
      while(numeric(*(--sour))) {
        if((*sour = *sour-i) < '0') {
          *sour = *sour+10;
          i=1;
          }
        else i=0;
        }
      }
    }
  csp=csp+BPW;
  }

/*
** pop stack to the secondary register
*/
pop() {
  ol("pop d");
  csp=csp+BPW;
  }

/*
** swap primary register and stack
*/
swapstk() {
  ol("xthl");
  }

/*
** process switch statement
*/
sw() {
  call("ccswitch");
  }

/*
** call specified subroutine name
*/
call(sname)  char *sname; {
  ot("call ");
  outstr(sname);
  nl();
  }

/*
** return from subroutine
*/
ret() {
  ol("ret");
  }

/*
** perform subroutine call to value on stack
*/
callstk() {
  immed();
  outstr("$+5");
  nl();
  swapstk();
  ol("pchl");
  csp=csp+BPW;
  }

/*
** jump to internal label number
*/
jump(label)  int label; {
  ot("jmp ");
  printlabel(label);
  nl();
  }

/*
** test primary register and jump if false
*/
testjump(label)  int label; {
  ol("mov a,h");
  ol("ora l");
  ot("jz ");
  printlabel(label);
  nl();
  }

/*
** test primary register against zero and jump if false
*/
zerojump(oper, label, lval) int (*oper)(), label, lval[]; {
  clearstage(lval[7], 0);  /* purge conventional code */
  (*oper)(label);
  }

/*
** define storage according to size
*/
defstorage(size) int size; {
  if(size==1) ot("db ");
  else        ot("dw ");
  }

/*
** point to following object(s)
*/
point() {
  ol("dw $+2");
  }

/*
** modify stack pointer to value given
*/
modstk(newsp, save)  int newsp, save; {
  int k;
  k=newsp-csp;
  if(k==0)return newsp;
  if(k>=0) {
    if(k<7) {
      if(k&1) {
        ol("inx sp");
        k--;
        }
      while(k) {
        ol("pop b");
        k=k-BPW;
        }
      return newsp;
      }
    }
  if(k<0) {
    if(k>-7) {
      if(k&1) {
        ol("dcx sp");
        k++;
        }
      while(k) {
        ol("push b");
        k=k+BPW;
        }
      return newsp;
      }
    }
  if(save) swap();
  const(k);
  ol("dad sp");
  ol("sphl");
  if(save) swap();
  return newsp;
  }

/*
** double primary register
*/
doublereg() {ol("dad h");}
!Funky!Stuff!
echo x - cc42.c
cat >cc42.c <<'!Funky!Stuff!'
static char *cc42_id = "%w% %g%";

#include "cc4.c"

/*
** add primary and secondary registers (result in primary)
*/
add() {ol("dad d");}

/*
** subtract primary from secondary register (result in primary)
*/
sub() {call("ccsub");}

/*
** multiply primary and secondary registers (result in primary)
*/
mult() {call("ccmult");}

/*
** divide secondary by primary register
** (quotient in primary, remainder in secondary)
*/
div() {call("ccdiv");}

/*
** remainder of secondary/primary
** (remainder in primary, quotient in secondary)
*/
mod() {div();swap();}

/*
** inclusive "or" primary and secondary registers
** (result in primary)
*/
or() {call("ccor");}

/*
** exclusive "or" the primary and secondary registers
** (result in primary)
*/
xor() {call("ccxor");}

/*
** "and" primary and secondary registers
** (result in primary)
*/
and() {call("ccand");}

/*
** logical negation of primary register
*/
lneg() {call("cclneg");}

/*
** arithmetic shift right secondary register
** number of bits given in primary register
** (result in primary)
*/
asr() {call("ccasr");}

/*
** arithmetic shift left secondary register
** number of bits given in primary register
** (result in primary)
*/
asl() {call("ccasl");}

/*
** two's complement primary register
*/
neg() {call("ccneg");}

/*
** one's complement primary register
*/
com() {call("cccom");}

/*
** increment primary register by one object of whatever size
*/
inc(n) int n; {
  while(1) {
    ol("inx h");
    if(--n < 1) break;
    }
  }

/*
** decrement primary register by one object of whatever size
*/
dec(n) int n; {
  while(1) {
    ol("dcx h");
    if(--n < 1) break;
    }
  }
 
/*
** test for equal to
*/
eq()  {call("cceq");}

/*
** test for equal to zero
*/
eq0(label) int label; {
  ol("mov a,h");
  ol("ora l");
  ot("jnz ");
  printlabel(label);
  nl();
  }

/*
** test for not equal to
*/
ne()  {call("ccne");}

/*
** test for not equal to zero
*/
ne0(label) int label; {
  ol("mov a,h");
  ol("ora l");
  ot("jz ");
  printlabel(label);
  nl();
  }

/*
** test for less than (signed)
*/
lt()  {call("cclt");}

/*
** test for less than to zero
*/
lt0(label) int label; {
  ol("xra a");
  ol("ora h");
  ot("jp ");
  printlabel(label);
  nl();
  }

/*
** test for less than or equal to (signed)
*/
le()  {call("ccle");}

/*
** test for less than or equal to zero
*/
le0(label) int label; {
  ol("mov a,h");
  ol("ora l");
  ol("jz $+8");
  ol("xra a");
  ol("ora h");
  ot("jp ");
  printlabel(label);
  nl();
  }

/*
** test for greater than (signed)
*/
gt()  {call("ccgt");}

/*
** test for greater than to zero
*/
gt0(label) int label; {
  ol("xra a");
  ol("ora h");
  ot("jm ");
  printlabel(label);
  nl();
  ol("ora l");
  ot("jz ");
  printlabel(label);
  nl();
  }

/*
** test for greater than or equal to (signed)
*/
ge()  {call("ccge");}

/*
** test for gteater than or equal to zero
*/
ge0(label) int label; {
  ol("xra a");
  ol("ora h");
  ot("jm ");
  printlabel(label);
  nl();
  }

/*
** test for less than (unsigned)
*/
ult()  {call("ccult");}

/*
** test for less than to zero (unsigned)
*/
ult0(label) int label; {
  ot("jmp ");
  printlabel(label);
  nl();
  }

/*
** test for less than or equal to (unsigned)
*/
ule()  {call("ccule");}

/*
** test for greater than (unsigned)
*/
ugt()  {call("ccugt");}

/*
** test for greater than or equal to (unsigned)
*/
uge()  {call("ccuge");}

#ifdef OPTIMIZE
peephole(ptr) char *ptr; {
  while(*ptr) {
    if(streq(ptr, "\tlxi h,0\n\tdad sp\n\tcall ccgint")) {
      if(streq(ptr+31, "xchg;;")) {pp2();ptr=ptr+38;}
      else                        {pp1();ptr=ptr+30;}
      }
    else if(streq(ptr, "\tlxi h,2\n\tdad sp\n\tcall ccgint")) {
      if(streq(ptr+31, "xchg;;")) {pp3();ptr=ptr+38;}
      else                        {pp4();ptr=ptr+30;}
      }
    else if(optimize) {
      if(streq(ptr, "\tdad sp\n\tcall ccgint")) {
        ol("call ccdsgi");
        ptr=ptr+21;
        }
      else if(streq(ptr, "\tdad d\n\tcall ccgint")) {
        ol("call ccddgi");
        ptr=ptr+20;
        }
      else if(streq(ptr, "\tdad sp\n\tcall ccgchar")) {
        ol("call ccdsgc");
        ptr=ptr+22;
        }
      else if(streq(ptr, "\tdad d\n\tcall ccgchar")) {
        ol("call ccddgc");
        ptr=ptr+21;
        }
      else if(streq(ptr,
"\tdad sp\n\tmov d,h\n\tmov e,l\n\tcall ccgint\n\tinx h\n\tcall ccpint")) {
        ol("call ccinci");
        ptr=ptr+59;
        }
      else if(streq(ptr,
"\tdad sp\n\tmov d,h\n\tmov e,l\n\tcall ccgint\n\tdcx h\n\tcall ccpint")) {
        ol("call ccdeci");
        ptr=ptr+59;
        }
      else if(streq(ptr,
"\tdad sp\n\tmov d,h\n\tmov e,l\n\tcall ccgchar\n\tinx h\n\tmov a,l\n\tstax d")) {
        ol("call ccincc");
        ptr=ptr+64;
        }
      else if(streq(ptr,
"\tdad sp\n\tmov d,h\n\tmov e,l\n\tcall ccgchar\n\tdcx h\n\tmov a,l\n\tstax d")) {
        ol("call ccdecc");
        ptr=ptr+64;
        }
      else if(streq(ptr, "\tdad d\n\tpop d\n\tcall ccpint")) {
        ol("call ccddpdpi");
        ptr=ptr+27;
        }
      else if(streq(ptr, "\tdad d\n\tpop d\n\tmov a,l\n\tstax d")) {
        ol("call ccddpdpc");
        ptr=ptr+31;
        }
      else if(streq(ptr, "\tpop d\n\tcall ccpint")) {
        ol("call ccpdpi");
        ptr=ptr+20;
        }
      else if(streq(ptr, "\tpop d\n\tmov a,l\n\tstax d")) {
        ol("call ccpdpc");
        ptr=ptr+24;
        }
      /* additional optimizing logic goes here */
      else cout(*ptr++, output);
      }
    else cout(*ptr++, output);
    }
  }

pp1() {
  ol("pop h");
  ol("push h");
  }

pp2() {
  ol("pop d");
  ol("push d");
  }

pp3() {
  ol("pop b");
  pp2();
  ol("push b");
  }

pp4() {
  ol("pop b");
  pp1();
  ol("push b");
}

#endif
!Funky!Stuff!
echo x - libasm.c
cat >libasm.c <<'!Funky!Stuff!'
#asm
;----- call.a: small-c arithmetic and logical library
;
ccddgc:entry
	dad d
	jmp ccgchar
	;
ccdsgc:entry
	inx h
	inx h
	dad sp
	;
;fetch a single byte from the address in hl and
; sign extend into hl
ccgchar:entry
	mov a,m
	;put the accum into hl and sign extend through h.
ccargc:entry
ccsxt:entry
	mov l,a
	rlc
	sbb a
	mov h,a
	ret
	;
ccddgi:entry
	dad d
	jmp ccgint
	;
ccdsgi:entry
	inx h
	inx h
	dad sp
	;
;fetch a full 16-bit integer from the address in hl
;into hl
ccgint:entry
	mov a,m
	inx h
	mov h,m
	mov l,a
	ret
	;
ccdecc:entry
	inx h
	inx h
	dad sp
	mov d,h
	mov e,l
	call ccgchar
	dcx h
	mov a,l
	stax d
	ret
	;
ccincc:entry
	inx h
	inx h
	dad sp
	mov d,h
	mov e,l
	call ccgchar
	inx h
	mov a,l
	stax d
	ret
	;
ccddpdpc:entry
	dad d
ccpdpc:entry
	pop b;;ret addr
	pop d
	push b
	;
;store a single byte from hl at the address in de
ccpchar:entry
pchar:mov a,l
	stax d
	ret
	;
ccdeci:entry
	inx h
	inx h
	dad sp
	mov d,h
	mov e,l
	call ccgint
	dcx h
	jmp ccpint
	;
ccinci:entry
	inx h
	inx h
	dad sp
	mov d,h
	mov e,l
	call ccgint
	inx h
	jmp ccpint
	;
ccddpdpi:entry
	dad d
ccpdpi:entry
	pop b;;ret addr
	pop d
	push b
	;
;store a 16-bit integer in hl at the address in de
ccpint:entry
pint:mov a,l
	stax d
	inx d
	mov a,h
	stax d
	ret
;inclusive "or" hl and de into hl
ccor:entry
	mov a,l
	ora e
	mov l,a
	mov a,h
	ora d
	mov h,a
	ret
;exclusive "or" hl and de into hl
ccxor:entry
	mov a,l
	xra e
	mov l,a
	mov a,h
	xra d
	mov h,a
	ret
;"and" hl and de into hl
ccand:entry
	mov a,l
	ana e
	mov l,a
	mov a,h
	ana d
	mov h,a
	ret
	;
;in all the following compare routines, hl is set to 1 if the
;condition is true, otherwise it is set to 0 (zero).
;
;test if hl = de
cceq:entry
	call cccmp
	rz
	dcx h
	ret
;test if de != hl
ccne:entry
	call cccmp
	rnz
	dcx h
	ret
;test if de > hl (signed)
ccgt:entry
	xchg
	call cccmp
	rc
	dcx h
	ret
;test if de <= hl (signed)
ccle:entry
	call cccmp
	rz
	rc
	dcx h
	ret
;test if de >= hl (signed)
ccge:entry
	call cccmp
	rnc
	dcx h
	ret
;test if de < hl (signed)
cclt:entry
	call cccmp
	rc
	dcx h
	ret
;common routine to perform a signed compare
; of de and hl
;this routine performs de - hl and sets the conditions:
; carry reflects sign of difference (set means de < hl)
; zero/non-zero set according to equality.
cccmp:entry
	mov a,h;;invert sign of hl
	xri 80h
	mov h,a
	mov a,d;;invert sign of de
	xri 80h
	cmp h;;compare msbs
	jnz cccmp1;;done if neq
	mov a,e;;compare lsbs
	cmp l
cccmp1:lxi h,1;;preset true cond
	ret
	;
;test if de >= hl (unsigned)
ccuge:entry
	call ccucmp
	rnc
	dcx h
	ret
	;
;test if de < hl (unsigned)
ccult:entry
	call ccucmp
	rc
	dcx h
	ret
	;
;test if de > hl (unsigned)
ccugt:entry
	xchg
	call ccucmp
	rc
	dcx h
	ret
	;
;test if de <= hl (unsigned)
ccule:entry
	call ccucmp
	rz
	rc
	dcx h
	ret
;
;common routine to perform unsigned compare
;carry set if de < hl
;zero/nonzero set accordingly
ccucmp:entry
	mov a,d
	cmp h
	jnz ccucmp1
	mov a,e
	cmp l
	ccucmp1: lxi h,1
	ret
	;
;shift de arithmetically right by hl and return in hl
ccasr:entry
	xchg
	dcr e
	rm
	mov a,h
	ral
	mov a,h
	rar
	mov h,a
	mov a,l
	rar
	mov l,a
	jmp ccasr+1
;shift de arithmetically left by hl and return in hl
ccasl:entry
	xchg
	dcr e
	rm
	dad h
	jmp ccasl+1
;subtract hl from de and return in hl
ccsub:entry
	mov a,e
	sub l
	mov l,a
	mov a,d
	sbb h
	mov h,a
	ret
;form the two's complement of hl
ccneg:entry
	call cccom
	inx h
	ret
;form the one's complement of hl
cccom:entry
	mov a,h
	cma
	mov h,a
	mov a,l
	cma
	mov l,a
	ret
;multiply de by hl and return in hl
;(signed multiply)
ccmult:entry
	mult:mov b,h
	mov c,l
	lxi h,0
ccmult1: mov a,c
	rrc
	jnc ccmult2
	dad d
ccmult2: xra a
	mov a,b
	rar
	mov b,a
	mov a,c
	rar
	mov c,a
	ora b
	rz
	xra a
	mov a,e
	ral
	mov e,a
	mov a,d
	ral
	mov d,a
	ora e
	rz
	jmp ccmult1
;divide de by hl and return quotient in hl, remainder in de
;(signed divide)
ccdiv:entry
div:mov b,h
	mov c,l
	mov a,d
	xra b
	push psw
	mov a,d
	ora a
	cm ccdeneg
	mov a,b
	ora a
	cm ccbcneg
	mvi a,16
	push psw
	xchg
	lxi d,0
ccdiv1: dad h
	call ccrdel
	jz ccdiv2
	call cccmpbcde
	jm ccdiv2
	mov a,l
	ori 1
	mov l,a
	mov a,e
	sub c
	mov e,a
	mov a,d
	sbb b
	mov d,a
ccdiv2: pop psw
	dcr a
	jz ccdiv3
	push psw
	jmp ccdiv1
ccdiv3: pop psw
	rp
	call ccdeneg
	xchg
	call ccdeneg
	xchg
	ret
;negate the integer in de
;(internal routine)
ccdeneg: mov a,d
	cma
	mov d,a
	mov a,e
	cma
	mov e,a
	inx d
	ret
;negate the integer in bc
;(internal routine)
ccbcneg: mov a,b
	cma
	mov b,a
	mov a,c
	cma
	mov c,a
	inx b
	ret
;rotate de left one bit
;(internal routine)
ccrdel: mov a,e
	ral
	mov e,a
	mov a,d
	ral
	mov d,a
	ora e
	ret
;compare bc to de
;(internal routine)
cccmpbcde: mov a,e
	sub c
	mov a,d
	sbb b
	ret
	;
; logical negation
cclneg:entry
	mov a,h
	ora l
	jnz $+6
	mvi l,1
	ret
	lxi h,0
	ret
	;
; execute "switch" statement
;
;  hl  =  switch value
; (sp) -> switch table
;         dw addr1, value1
;         dw addr2, value2
;         ...
;         dw 0
;        [jmp default]
;         continuation
;
ccswitch:entry
	xchg;;de =  switch value
	pop h;;hl -> switch table
	swloop:mov c,m
	inx h
	mov b,m;;bc -> case addr, else 0
	inx h
	mov a,b
	ora c
	jz swend;;default or continuation code
	mov a,m
	inx h
	cmp e
	mov a,m
	inx h
	jnz swloop
	cmp d
	jnz swloop
	mov h,b;;case matched
	mov l,c
swend:pchl
	;
#endasm
!Funky!Stuff!
echo x - libsmc.c
cat >libsmc.c <<'!Funky!Stuff!'
/*
** dtoi -- convert signed decimal string to integer nbr
**         returns field length, else ERR on error
*/
dtoi(decstr, nbr)  char *decstr;  int *nbr;  {
  int len, s;
  if((*decstr)=='-') {s=1; ++decstr;} else s=0;
  if((len=utoi(decstr, nbr))<0) return ERR;
  if(*nbr<0) return ERR;
  if(s) {*nbr = -*nbr; return ++len;} else return len;
  }

/*
** itod -- convert nbr to signed decimal string of width sz
**         right adjusted, blank filled; returns str
**
**        if sz > 0 terminate with null byte
**        if sz = 0 find end of string
**        if sz < 0 use last byte for data
*/
itod(nbr, str, sz)  int nbr;  char str[];  int sz;  {
  char sgn;
  if(nbr<0) {nbr = -nbr; sgn='-';}
  else sgn=' ';
  if(sz>0) str[--sz]=NULL;
  else if(sz<0) sz = -sz;
  else while(str[sz]!=NULL) ++sz;
  while(sz) {
    str[--sz]=(nbr%10+'0');
    if((nbr=nbr/10)==0) break;
    }
  if(sz) str[--sz]=sgn;
  while(sz>0) str[--sz]=' ';
  return str;
  }

/*
** itou -- convert nbr to unsigned decimal string of width sz
**         right adjusted, blank filled; returns str
**
**        if sz > 0 terminate with null byte
**        if sz = 0 find end of string
**        if sz < 0 use last byte for data
*/
itou(nbr, str, sz)  int nbr;  char str[];  int sz;  {
  int lowbit;
  if(sz>0) str[--sz]=NULL;
  else if(sz<0) sz = -sz;
  else while(str[sz]!=NULL) ++sz;
  while(sz) {
    lowbit=nbr&1;
    nbr=(nbr>>1)&32767;  /* divide by 2 */
    str[--sz]=((nbr%5)<<1)+lowbit+'0';
    if((nbr=nbr/5)==0) break;
    }
  while(sz) str[--sz]=' ';
  return str;
  }

/*
** itox -- converts nbr to hex string of length sz
**         right adjusted and blank filled, returns str
**
**        if sz > 0 terminate with null byte
**        if sz = 0 find end of string
**        if sz < 0 use last byte for data
*/
itox(nbr, str, sz)  int nbr;  char str[];  int sz;  {
  int digit, offset;
  if(sz>0) str[--sz]=NULL;
  else if(sz<0) sz = -sz;
  else while(str[sz]!=NULL) ++sz;
  while(sz) {
    digit=nbr&15; nbr=(nbr>>4)&4095;
    if(digit<10) offset=48; else offset=55;
    str[--sz]=digit+offset;
    if(nbr==0) break;
    }
  while(sz) str[--sz]=' ';
  return str;
  }

/*
** abs -- returns absolute value of nbr
*/
abs(nbr)  
int nbr;
{
if(nbr<0) return -nbr;
else return stdout;
}

/*
** left -- left adjust and null terminate a string
*/
left(str) char *str; {
  char *str2;
  str2=str;
  while(*str2==' ') ++str2;
  while(*str++ = *str2++);
  }

cout(c, fd) char c; int fd; {
  if(fputc(c, fd)==EOF) xout();
  }

sout(string, fd) char *string; int fd; {
  if(fputs(string, fd)==EOF) xout();
  }

lout(line, fd) char *line; int fd; {
  sout(line, fd);
  cout('\n', fd);
  }

xout() {
  fputs("output error\n", stderr);
  abort(ERRCODE);
  }

/*
** printf(controlstring, arg, arg, ...) -- formatted print
**        operates as described by Kernighan & Ritchie
**        only d, x, c, s, and u specs are supported.
*/
printf(argc) int argc; {
  int i, width, prec, preclen, len, *nxtarg;
  char *ctl, *cx, c, right, str[7], *sptr, pad;
  i = CCARGC();   /* fetch arg count from A reg first */
  nxtarg = &argc + i - 1;
  ctl = *nxtarg;
  while(c=*ctl++) {
    if(c!='%') {cout(c, stdout); continue;}
    if(*ctl=='%') {cout(*ctl++, stdout); continue;}
    cx=ctl;
    if(*cx=='-') {right=0; ++cx;} else right=1;
    if(*cx=='0') {pad='0'; ++cx;} else pad=' ';
    if((i=utoi(cx, &width)) >= 0) cx=cx+i; else continue;
    if(*cx=='.') {
      if((preclen=utoi(++cx, &prec)) >= 0) cx=cx+preclen;
      else continue;
      }
    else preclen=0;
    sptr=str; c=*cx++; i=*(--nxtarg);
    if(c=='d') itod(i, str, 7);
    else if(c=='x') itox(i, str, 7);
    else if(c=='c') {str[0]=i; str[1]=NULL;}
    else if(c=='s') sptr=i;
    else if(c=='u') itou(i, str, 7);
    else continue;
    ctl=cx; /* accept conversion spec */
    if(c!='s') while(*sptr==' ') ++sptr;
    len=-1; while(sptr[++len]); /* get length */
    if((c=='s')&(len>prec)&(preclen>0)) len=prec;
    if(right) while(((width--)-len)>0) cout(pad, stdout);
    while(len) {cout(*sptr++, stdout); --len; --width;}
    while(((width--)-len)>0) cout(pad, stdout);
    }
  }

/*
** sign -- return -1, 0, +1 depending on the sign of nbr
*/
sign(nbr)  int nbr;  {
  if(nbr>0) return 1;
  else if(nbr==0) return 0;
  else return -1;
  }

/*
** strcmp -- return -1, 0, +1 depending on str1 <, =, > str2
*/
strcmp(str1, str2)  char *str1, *str2;  {
  char c1, c2;
  while((c1=*str1++)==(c2=*str2++)) if(c1==NULL) return 0;
  return sign(c1-c2);
  }

/*
** utoi -- convert unsigned decimal string to integer nbr
**          returns field size, else ERR on error
*/
utoi(decstr, nbr)  char *decstr;  int *nbr;  {
  int d,t; d=0;
  *nbr=0;
  while((*decstr>='0')&(*decstr<='9')) {
    t=*nbr;t=(10*t) + (*decstr++ - '0');
    if ((t>=0)&(*nbr<0)) return ERR;
    d++; *nbr=t;
    }
  return d;
  }

/*
** xtoi -- convert hex string to integer nbr
**         returns field size, else ERR on error
*/
xtoi(hexstr, nbr)  char *hexstr;  int *nbr;  {
  int d,t; d=0;
  *nbr=0;
  while(1)
    {
    if((*hexstr>='0')&(*hexstr<='9')) t=48;
    else if((*hexstr>='A')&(*hexstr<='F')) t=55;
    else if((*hexstr>='a')&(*hexstr<='f')) t=87;
    else break;
    if(d<4) ++d; else return ERR;
    *nbr=*nbr<<4;
    *nbr=*nbr+(*hexstr++)-t;
    }
  return d;
  }
!Funky!Stuff!
echo x - stdio.c
cat >stdio.c <<'!Funky!Stuff!'
/*
** stdio.c -- header for local I/O
*/
#define stdin 0
#define stdout 1
#define stderr 2
#define stdport 3
#define stdlist 4
#define ERR -2
#define EOF -1
#define YES 1
#define NO 0
#define NULL 0
#define CR 13
#define LF 10
!Funky!Stuff!

schrein (03/14/83)

#R:wjh12:-18000:uiucdcs:12600005:000:128
uiucdcs!schrein    Mar 13 17:01:00 1983

There is, unfortunately, an old limit of about 64K for the length of a note....
Therefore, these smallc sources are incomplete.