//***********************************************************************************************************
//**
//**	hp21asm - Main Program & Assembly Processing
//**
//**	2014 Nov: split from single file / bh
//**
//***********************************************************************************************************
#include <stdio.h>
#include <string.h>
#include "sym.h"
#include "util.h"

extern void			Fls_init();
extern void			Fls_append();
extern void			Fls_restart();
extern void			Fls_push();
extern int			Fls_getLn();

extern void			Sym_init();
extern void			Sym_define();
extern int			Sym_find();
extern void			Sym_list();
extern int			Sym_validChr();

extern char			*parsePtr;
extern int			parse_escChr();
extern int			parse_arg();
extern int			parse_oct();
extern int			parse_dec();
extern void			parse_sym();
extern int			parse_try();
extern int			parse_mod();
extern int			parse_comma();
extern void			parse_space();
extern int			parse_atEOT();

extern int			Abs_oCreate();
extern void			Abs_oClose();
extern void			Abs_oWord();

extern int			List_create( char* fname );
extern void			List_close();
extern void			List_lines( int n );
extern void			List_addrSrc( int addr );
extern void			List_valSrc( int val );
extern void			List_addrCodeSrc( int addr, int code );
extern void			List_err( char* text );
extern void			List_lnStart( char* srcLn );
extern void			List_lnFin();
extern int			Err_count;

extern int			Mch2100_instr();


//* END Refs
//***********************************************************************************************************
//* Globals

int				doModern;
int				CurAddr;					// current memory address location counter


//* END Globals
//***********************************************************************************************************
//* Assembly Processing

//----------------------------------------------------

static void
Asm_emit( word )
	int			word;
 {
	List_addrCodeSrc( CurAddr, word );

	Abs_oWord( CurAddr++, word );
 }

	
//----------------------------------------------------

static void
Asm_pass( pass, ifn_flag, ifz_flag )
	int			pass;
	int			ifn_flag, ifz_flag;
 {
	char			srcLn[150];						// buffer for current source line
	char			label[SYM_MAXLEN*2+1], strArg[SYM_MAXLEN*2+1];
	int			rep_count, count, xif_flag;
	int			word1, word2, numWords, arg, c1, c2, i;

	doModern = FALSE;
	CurAddr = 0;

	rep_count = 1;									// repeat count for REP directive
	xif_flag = FALSE;								// TRUE if we are skipping until XIF

	while( Fls_getLn(srcLn,sizeof(srcLn)) )
	 {	List_lnStart( srcLn );

		if( srcLn[0]=='*' || strstr(srcLn,"ASMB")==srcLn )			// skip comment lines & ASMB control statement
		 {	List_lnFin( pass==2 );
			continue;
		 }

 		for( count=rep_count,rep_count=1; count>0; --count )			// extra loop just for the REP directive
		 {	parsePtr = srcLn;						// start parsing at beginning of line
			parse_sym( label );						// collect label if present, must be at BOL
			parse_space();
											// start looking for a directive
			if( parse_try("INC") )						// include a file
			 {	parse_space();
				parse_sym( strArg );					// cheap out and use the sym function !!
				Fls_push( strArg );					// insert the file in the line stream
			 }
			else if( parse_try("MDN") )					// switch to modern syntax
			 {	doModern = TRUE;
			 }
			else if( parse_try("IFN") )					// N flag conditional area 
			 {	if( !ifn_flag) xif_flag = TRUE;
			 }
			else if( parse_try("IFZ") )					// Z flag conditional area
			 {	if( !ifz_flag) xif_flag = TRUE;
			 }
			else if( parse_try("XIF") )					// end of conditional area
			 {	xif_flag = FALSE;
			 }
			else if( xif_flag )						// skip line in conditional area
			 {
			 }
			else if( parse_try("HED") || parse_try("SUP") || parse_try("UNS") || 	// ignore these listing directives
				 parse_try("UNL") || parse_try("LST") )
			 {
			 }
			else if( parse_try("SKP") )					// properly should be skip to top of page
			 {	if( pass == 2 ) List_lines( 3 );
			 }
			else if( parse_try("SPC") )					// put blank lines in listing
			 {	arg = parse_arg();
				if( pass == 2 ) List_lines( arg );
			 }
			else if( parse_try("END") )					// END - ignore
			 {
			 }
			else if( parse_try("ORG") )					// ORiGin - set current memory address
			 {	CurAddr = parse_arg();
				if( pass == 2 )
				 {	List_addrSrc( CurAddr );
					if( strlen(label) ) List_err( "Unexpected label" );
				 }
			 }
			else if( parse_try("EQU") )					// EQUate <expr> - define symbolic value
			 {	arg = parse_arg();
				if( strlen(label) ) Sym_define( label, arg, pass==1 );
				if( pass == 2 )
				 {	List_valSrc( arg );
					if( !strlen(label) ) List_err( "Label required" );
				 }
			 }
			else
			 {	if( strlen(label) ) Sym_define( label, CurAddr, pass==1 );

				if( parse_try("REP") )					// REPeat <numTimes> - repeat following statement
				 {	rep_count = parse_arg();
					if( rep_count < 1 || rep_count > 9999 )
					 {	List_err( "Illegal repeat count");
						rep_count = 1;
					 }
				 }
				else if( parse_try("ABS") )				// ABSolute <expr> - fill a word with a value
				 {	if( pass == 1 )
						++CurAddr;
					else
						Asm_emit( parse_arg() );
				 }
				else if( parse_try("DEF") )				// DEFine <expr>[,I] - fill a word with a value
				 {	if( pass == 1 )
						++CurAddr;
					else
					 {	word1 = parse_arg();
						if( parse_mod('I') ) word1 |= 0x8000;
						Asm_emit( word1 );
					 }
				 }
				else if( parse_try("OCT") )				// OCTal <num>,... - fill words with octal-interpreted values
				 {	do
					 {	word1 = parse_oct();
						if( pass == 1 )
							++CurAddr;
						else
							Asm_emit( word1 );
					 }
					while( parse_comma() );
				 }
				else if( parse_try("DEC") )				// DECimal <num>,... - fill words with decimal-interpreted values
				 {	do
					 {	numWords = parse_dec( &word1, &word2 );
						if( pass == 1 )
							CurAddr += numWords;
						else
						 {	if( numWords > 0 ) Asm_emit( word1 );
							if( numWords > 1 ) Asm_emit( word2 );
						 }
					 }
					while( parse_comma() );
				 }
				else if( parse_try("DEX") )
				 {	if( pass == 2) List_err( "Not implemented" );
				 }
				else if( parse_try("ASC") )				// ASCii <numWords>,<string> - fill words with string
				 {	numWords = parse_arg();
					if( pass == 1 )
						CurAddr += numWords;
					else
					 {	if( parse_comma() )
							for( ; numWords>0; --numWords )
							 {	c1 = *parsePtr>31 ? *parsePtr++ : ' ';
								c2 = *parsePtr>31 ? *parsePtr++ : ' ';
								Asm_emit( (c1<<8) | c2 );
							 }
						else
							List_err( "Missing comma" );
					 }
				 }
				else if( parse_try("STR") )				// STRing "<string>" - fill words with null-terminated string
				 {	parse_space();
					if( *parsePtr == '"' ) ++parsePtr;

					for( i=0; *parsePtr && (c2=*parsePtr++)!='"'; ++i )
					 {	if( c2 == '\\' ) c2 = parse_escChr();
						if( (i & 1) == 0 )
							c1 = c2;
						else
						 {	if( pass == 1 )
								++CurAddr;
							else
								Asm_emit( (c1<<8) | c2 );
						 }
					 }
					if( pass == 1 )
						++CurAddr;
					else
					 {	if( (i & 1) == 0 ) c1 = 0;
						Asm_emit( (c1<<8) | 0 );
					 }
				 }
				else if( parse_try("BSS") )				// BSS <numWords> - Block Started by Symbol, reserve memory words without altering them
				 {	if( pass == 2 ) List_addrSrc( CurAddr );
					numWords = parse_arg();
					CurAddr += numWords;
				 }
				else if( *parsePtr == 0 )				// skip empty lines
				 {
				 }
				else							// finally, check for an actual machine instruction
				 {	if( pass == 1 )
						++CurAddr;
					else
						Asm_emit( Mch2100_instr(&word1) ? word1 : 0 );
				 }
			 }

			if( pass==2 && !parse_atEOT() ) List_err( "Syntax error" );
		 }

		List_lnFin( pass==2 );
	 }
 }


//* END Asm
//***********************************************************************************************************
//* Main

char				useMsg[] =
	"  -- Use: %s  [-u] [-n] [-z] [-m <maxBlk>] [-o <absFile>] [-l <listFile>] <asmFile>*\n"
	"	-u		: just display this use message\n"
	"	-n		: set IFN conditional flag\n"
	"	-z		: set IFZ conditional flag\n"
	"	-m <maxBlk>	: maximum number of data words per absolute block (default 255)\n"
	"	-o <absFile>	: binary output file in HP Absolute format\n"
	"	-l <listFile>	: listing output file\n"
	"	<asmFile>	: assembly source files\n";


int
main( argc, argv )
	int			argc;
	char			*argv[];
{
	char			*absFname, *listFname;
	int			ifn_flag, ifz_flag, blkMax, i;

	//---------------------------------------
	// Configuration

	absFname 	= "/dev/null";
	listFname	= "/dev/null";
	ifn_flag	= FALSE;						// TRUE if IFN does not skip
	ifz_flag	= FALSE;						// TRUE if IFZ does not skip
	blkMax		= -1;
	Fls_init();

	for( i=1; i<argc; ++i )
		if( streql(argv[i],"-o") )					// absolute output
		 {	if( ++i == argc ) goto useFail;
			absFname = argv[i];
		 }
		else if( streql(argv[i],"-l") )					// listing output
		 {	if( ++i == argc ) goto useFail;
			listFname = argv[i];
		 }
		else if( streql(argv[i],"-m") )					// ABS block size
		 {	if( ++i == argc ) goto useFail;
			if( sscanf(argv[i],"%d",&blkMax) != 1 ) goto useFail;
		 }
		else if( streql(argv[i],"-n") )					// N conditional flag
		 {	ifn_flag = TRUE;
		 }
		else if( streql(argv[i],"-z") )					// Z conditional flag
		 {	ifz_flag = TRUE;
		 }
		else if( streql(argv[i],"-u") )					// use message
		 {	goto useFail;
		 }
		else if( argv[i][0] != '-' )
		 {	Fls_append( argv[i] );					// source file arg
		 }
		else
		 {useFail:
			printf( useMsg, argv[0] );
			return( -1 );
		 }

	//---------------------------------------
	// Initialisation

	Sym_init();

	if( !List_create( listFname ) )
	 {	printf( " -- failed to open list file: %s\n", listFname );
		return( -1 );
	 }

	if( !Abs_oCreate( absFname, blkMax ) )
	 {	List_close();
		printf( " -- failed to open abs file: %s\n", absFname );
		return( -1 );
	 }

	//---------------------------------------
	// Processing

	Asm_pass( 1, ifn_flag, ifz_flag );
	Fls_restart();
	Asm_pass( 2, ifn_flag, ifz_flag );

	Sym_list();

	//---------------------------------------
	// Termination

	if( Err_count > 0 ) printf( "%d ERRORS\n", Err_count );

	Abs_oClose();
	List_close();

	return( 0 );
}


//* EOF
//***********************************************************************************************************
