/*
 ap.c -- amiga assembler preprocessor
 (c) 1989, Risto Paasivirta
 */

#include <stdio.h>
#include <ctype.h>

#define COPYRIGHT	"AP V0.2 -- (c) 1989, Risto Paasivirta\n"

#define MAXW 200	/* maximim number of stored words */
#define WLEN 32		/* max lenght of word */
#define MAXS 20		/* max entries in control structure stack */

int debug=0;		/* debug mode flag */
int verbose=0;		/* verbose mode flag */

#define OPNO 36		/* number of operands */

char *ops[]={" ",	/* operand memnonics */
	"}",".","+","-","*","/","*/","=",
	";","&","|","^","~","%","0=","==",
	"ifeq","ifne","ifgt","iflt","ifge","ifle","then",
	"else","begin","until","while","repeat","<?",
	"<^","<0","<1",":=:","!","<<",">>" };


char *brs[]={		/* branch opcodes for if-constucts */
	"bne","beq","ble","bge","blt","bgt" };

char *bop[]={		/* bit opcodes */
	"btst","bchg","bclr","bset" };

char line[200];		/* line buffer */
int linenum=0;		/* line to compile */

char tmps[32];		/* temp string */
int tmpn=0;		/* count for label generation */

char wrd[MAXW][WLEN];	/* storage for scanned words */ 
int wrdp=0;		/* index for next free word storage */

char stk[MAXS][WLEN];	/* stack for control structures */
int stkp=0;		/* index of next free stack entry */

main(argc,argv)
int argc;
char **argv;
{
	while(--argc)	/* loop through options */
	switch(**++argv)
	{	
	case 'd':	/* option debug, set debug mode */
		debug=1;
	break;

	case 'v':	/* option v, set verbose mode */
		verbose=1;
	break;

	default:	/* unnown option, print usage */
		fprintf(stderr,"usage: ap [<inputfile] [>outputfile] [d] [v]");
		exit(1);
	break;
	}

	if(verbose)fprintf(stderr,COPYRIGHT);


	while(gets(line)) /* parse source */
	{
		linenum++; /* count lines */
		if(verbose)fprintf(stderr,"line %3d: asm\r",linenum);
		if(*line!='{') puts(line); /* copy stuff to assembler */
		else while(gets(line))	/* compile stuff */	
		{
			linenum++; /* count lines */
			if(verbose)fprintf(stderr,"line %3d: ap \r",linenum);
			if(parse())break; /* compile until end of ap-block */
		}
		if(feof(stdin)) break; /* break if eof */
	}
	if(wrdp>1) /* warn if expression not closed with '.' */
	{
		fprintf(stderr,"eof while expression open\n");
	} 

	if(stkp) /* warn if missing then, until or repeat */
	{
		fprintf(stderr,"eof while control structures open!\n");
		exit(1);
	}
	if(verbose)fprintf(stderr,"%d lines compiled ok.\n",linenum);
	exit(0);
}

/* need(x) -- check if there are enough stuff to compile */ 

void
need(x) 
{
	if(wrdp<x)
	{
		fprintf(stderr,"line %d out of labels\n",linenum);
		exit(1); /* no recovery */
	}
}

#define arg1 wrd[wrdp-1] 	/* arguments to pass to assembler code */
#define arg2 wrd[wrdp-2]
#define arg3 wrd[wrdp-3]

/* parse() -- compile line, return 1 if end of apl block */

int
parse()
{
	char *ln,*s;
	int i;

	ln=line; /* set ptr to start of line buffer */

	while(*ln) /* while there are any chars */
	{
		while(isspace(*ln))ln++;	/* skip spaces */

		s=wrd[wrdp]; 			/* get word */
		for(i=WLEN-1;!isspace(*ln)&& i && *ln;i--)*s++ = *ln++;
		*s=0; /* ending zero */

		if(i==WLEN-1) break; 		/* end of line */

		while(!isspace(*ln)&&*ln)ln++;	/* skip rest of word */

						/* search word */
		for(i=OPNO;i>0;i--)if(strcmp(ops[i],wrd[wrdp])==0)break;

		switch(i) /* switch by operand, 0=label/assembler expression */
		{
		case 0: /* label */
			if(++wrdp>=MAXW) /* leave label in storage */
			{
				fprintf(stderr,"out of space!\n");
				exit(1);
			}
		break;

		case 1: /* } end of code */
			return 1;
		break;

		case 2: /* . (end of expression) */
			if(wrdp>1) /* check if there are extra labels */
			{
				fprintf(stderr,"line %d: too much stuff: ",
					linenum);
				for(;--wrdp;)fprintf(stderr,"%s ",
					wrd[wrdp-1]);
				fprintf(stderr,"\n");
			}
			wrdp=0; /* clean storage */
		break;

		case 3: /* + */
			need(2);	
			printf("\tadd.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;	

		case 4: /* - */
			need(2);	
			printf("\tsub.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;	
		
		case 5: /* * */
			need(2);	
			printf("\tmuls.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;	

		case 6: /* / */
			need(2);
			printf("\text.l %s\n\tdivs.w %s,%s\n",arg2,arg1,arg2);
			wrdp-=1;
		break;

		case 7: /* muldiv */
			need(3);
			printf("\tmuls.w %s,%s\n\tdivs.w %s,%s\n"
				,arg2,arg3,arg1,arg3);
			wrdp-=2;
		break;

		case 8: /* = move */
			need(2);	
			printf("\tmove.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;

		case 9: /* ; comment */
			if(debug)printf("; %03d: %s\n",linenum,line);
			else printf("\t; %s\n",ln);
			return 0;
		break;


		case 10: /* & and */
			need(2);	
			printf("\tand.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;	

		case 11: /* | or */
			need(2);	
			printf("\tor.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;	

		case 12: /* ^ eor */
			need(2);	
			printf("\teor.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;	

		case 13: /* ~ not */
			need(1);	
			printf("\tnot.w %s\n",arg1);
		break;	

		case 14: /* % swap/mod */
			need(1);	
			printf("\tswap %s\n",arg1);
		break;	

		case 15: /* 0= */
			need(1);	
			printf("\ttst.w %s\n",arg1);
		break;

		case 16: /* == */
			need(2);	
			printf("\tcmp.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;

		case 17: case 18: case 19: case 20: case 21: case 22: /* if */
			if(stkp>=MAXS)
			{
				fprintf(stderr,"line %d stack full\n",
				linenum);
				exit(1);
			}
			sprintf(tmps,"%03d",tmpn++); /* create label */
			strcpy(stk[stkp++],tmps); /* copy label in stack */
			/* compile 'bcc label' */	
			printf("\t%s if.%s\n",brs[i-17],tmps);
		break;

		case 23: /* then */
			if(stkp<1)
			{
				fprintf(stderr,"line %d then without if\n",
				linenum);
				exit(1);
			}
			printf("if.%s\n",stk[--stkp]); /* copy label there */
		break;
			
		case 24: /* else */
			if(stkp<1)
			{
				fprintf(stderr,"line %d else without if\n",
				linenum);
				exit(1);
			}
			sprintf(tmps,"%03d",tmpn++); /* create label */
			/* compile 'bra newlabel \noldlablel' */
			printf("\tbra if.%s\nif.%s\n",tmps,stk[--stkp]);
			strcpy(stk[stkp++],tmps); /* push new label */
		break;

		case 25: /* begin */
			if(stkp>=MAXS)
			{
				fprintf(stderr,"line %d stack full\n",
				linenum);
				exit(1);
			}
			sprintf(tmps,"%03d",tmpn++); /* create label */
			strcpy(stk[stkp++],tmps); /* push label */
			printf("beg.%s\n",tmps); /* compile label */
		break;

		case 26: /* until */
			if(stkp<1)
			{
				fprintf(stderr,"line %d until without begin\n",
				linenum);
				exit(1);
			}
			printf("\tbeq beg.%s\n",stk[--stkp]); /* beq label */
		break;

		case 27: /* while */
			if(stkp<1)
			{
				fprintf(stderr,"line %d while without begin\n",
				linenum);
				exit(1);
			}

			if(stkp>=MAXS)
			{
				fprintf(stderr,"line %d stack full\n",
				linenum);
				exit(1);
			}

			sprintf(tmps,"%03d",tmpn++); /* create label */
			printf("\tbeq whl.%s\n",tmps); /* beq label */
			strcpy(stk[stkp++],tmps); /* push label */
		break;

		case 28: /* repeat */
			if(stkp<2)
			{
				fprintf(stderr,"line %d repeat without while\n",
				linenum);
				exit(1);
			}

			/* compile 'bra label2\nlabel1' */

			printf("\tbra beg.%s\nwhl.%s\n",
				stk[stkp-2],stk[stkp-1]);
			stkp-=2;
		break;


		case 29: case 30: case 31: case 32: /* bit opers */
			need(2);
			printf("\t%s %s,%s\n",bop[i-29],arg1,arg2);
			wrdp-=1;
		break;

		case 33: /* :=: exhange */
			need(2);	
			printf("\texg %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;

		case 34: /* ! logical not */
			printf("\teor #4,CCR\n");
		break;

		case 35: /* << shift right */
			need(2);	
			printf("\tlsr.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;

		case 36: /* >> arithmetic shift left */
			need(2);	
			printf("\tasl.w %s,%s\n",arg1,arg2);
			wrdp-=1;
		break;

		default: /* this never happens */
			fprintf(stderr,"%s not yet supported\n",ops[i]);
		break;
		}

	}
	/* if debug mode copy source as comment to output */
	if(debug)printf("; %03d: %s\n",linenum,line);
	return 0;
}

