OpenCores
URL https://opencores.org/ocsvn/copyblaze/copyblaze/trunk

Subversion Repositories copyblaze

[/] [copyblaze/] [trunk/] [copyblaze/] [sw/] [tools/] [asm/] [pBlazASM/] [pBlazASM/] [pbParser.c] - Rev 46

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 *  Copyright © 2003..2010 : Henk van Kampen <henk@mediatronix.com>
 *
 *	This file is part of pBlazASM.
 *
 *  pBlazASM is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  pBlazASM is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with pBlazASM.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <string.h>
 
#include "pbTypes.h"
#include "pbSymbols.h"
#include "pbLexer.h"
#include "pbErrors.h"
 
//#ifdef _MSC_VER	//Microsoft Visual C doesn't have strcasemcp, but has stricmp instead
#define strcasecmp	stricmp
//#endif
 
 
/**
 * pBlazASM work horse
 * @file pBParser.c
 */
 
/**
 * assemler states
 */
typedef enum {
	bsINIT, bsLABEL, bsLABELED, bsSYMBOL, bsOPCODE, bsDIRECTIVE, bsOPERAND, bsEND
} build_state ;
 
static bool bMode = false ; //! KCPSM mode, accepts 'NAMEREG' etc
static bool bCode = true ; // list code
static uint32_t gCode[ 1024 + 256 / 2 ] ; // code space and scratchpad space (in words)
static char * gSource ; // source filename for error printer
static int gLinenr = 0 ; // current line number
static uint32_t gPC = 0 ; // current code counter
static uint32_t gSCR = 2048 ; // current scratchpad counter
 
/**
 * error strings
 */
static const char * s_errors[] =
	{
		"<none>", "unexpected tokens", "doubly defined", "undefined", "phasing error", "missing symbol",
		"syntax error", "syntax error in expression", "syntax error in operand", "syntax error in value", "value out of range",
		"syntax error in operator", "syntax error, register expected", "comma expected", "unexpected characters",
		"expression expected", "code size > 1024", "<not-implemented>", "<internal error>" } ;
 
static error_t expression( uint32_t * result ) ;
 
/**
 * processOperator
 * handle 'expression' <operator> 'term'
 * @param result value to operate on
 * @param term value to operate with
 * @param oper ref to operator to be used
 * @return success
 */
static bool processOperator( unsigned int * result, unsigned int term, symbol_t ** oper ) {
	if ( *oper == NULL ) {
		*result = term ;
		return true ;
	}
	switch ( ( *oper )->subtype ) {
	case stMUL :
		*result *= term ;
		break ;
	case stDIV :
		*result /= term ;
		break ;
	case stMOD :
		*result %= term ;
		break ;
	case stADD :
		*result += term ;
		break ;
	case stSUB :
		*result -= term ;
		break ;
	case stIOR :
		*result |= term ;
		break ;
	case stXOR :
		*result ^= term ;
		break ;
	case stAND :
		*result &= term ;
		break ;
	case stSHL :
		*result <<= term ;
		break ;
	case stSHR :
		*result >>= term ;
		break ;
	default :
		return false ;
	}
	*oper = NULL ;
	return true ;
}
 
/**
 * convert escaped char's
 * @param p ref to character sequence
 * @return converted character
 */
static char convert_char( char * * p ) {
	int r = 0 ;
	char * s = *p ;
 
	if ( *s == '\\' ) { // '\r' or '\013'
		s++ ;
		switch ( *s++ ) { // \a \b \f \n \r \t \v
		case '\'' :
			r = '\'' ;
			break ;
		case '\\' :
			r = '\\' ;
			break ;
		case '"' :
			r = '"' ;
			break ;
		case 'a' :
		case 'A' :
			r = '\a' ;
			break ;
		case 'b' :
		case 'B' :
			r = '\b' ;
			break ;
		case 'f' :
		case 'F' :
			r = '\f' ;
			break ;
		case 'n' :
		case 'N' :
			r = '\n' ;
			break ;
		case 'r' :
		case 'R' :
			r = '\r' ;
			break ;
		case 't' :
		case 'T' :
			r = '\t' ;
			break ;
		case 'v' :
		case 'V' :
			r = '\v' ;
			break ;
 
		case 'x' :
		case 'X' :
			if ( sscanf( s, "%x", &r ) != 1 )
				return etLEX ;
			while ( isxdigit( *s ) )
				s++ ;
			break ;
		case '0' :
			--s ;
			if ( sscanf( s, "%o", &r ) != 1 )
				return etLEX ;
			while ( isdigit( *s ) && *s != '8' && *s != '9' )
				s++ ;
			break ;
		default :
			return etLEX ;
		}
	} else
		r = *s++ ;
	*p = s ;
	return r ;
}
 
/**
 * convert escaped char's in a string
 * @param s string to be converted
 * @return new string with result (needs to be freeed)
 */
static char * convert_string( char * s ) {
	char * r = calloc( 1, 256 ), *p ;
 
	for ( p = r ; *s != '\0' ; )
		*p++ = convert_char( &s ) ;
	*p++ = '\0' ;
	return r ;
}
 
/**
 * term processing
 * @param resulting value of term
 * @result error code
 */
static error_t term( uint32_t * result ) {
	symbol_t * oper = NULL ;
	const char * p = NULL ;
	symbol_t * h = NULL ;
	error_t e = etNONE ;
	char * s = NULL ;
	uint32_t val ;
 
	// full expression handling
	if ( tok_current()->type == tNONE )
		return etEXPR ;
 
	if ( tok_current()->type == tOPERATOR )
		oper = tok_next() ;
 
	s = tok_current()->text ;
	switch ( tok_current()->type ) {
	case tDEC :
		if ( sscanf( s, "%d", &val ) != 1 )
			return etEXPR ;
		break ;
	case tCHAR :
		val = convert_char( &s ) ;
		break ;
	case tHEX :
		if ( sscanf( s, "%X", &val ) != 1 )
			return etEXPR ;
		break ;
	case tBIN :
		// parse a binary value
		val = 0 ;
		for ( p = s ; *p != 0 ; p++ ) {
			val <<= 1 ;
			if ( *p == '1' )
				val |= 1 ;
		}
		break ;
	case tIDENT :
		h = find_symbol( s, false ) ;
		if ( h == NULL )
			return etUNDEF ;
		val = h->value ;
		if ( h->type != tVALUE && h->type != tLABEL )
			return etVALUE ;
		break ;
	case tLPAREN :
		tok_next() ;
		e = expression( &val ) ;
		if ( e != etNONE )
			return e ;
		if ( tok_current()->type != tRPAREN )
			return etEXPR ;
		break ;
	default :
		return etEXPR ;
	}
	tok_next() ;
	if ( oper != NULL ) {
		switch ( oper->subtype ) {
		case stSUB :
			*result = -val ;
			break ;
		case stTILDA :
			*result = ~val ;
			break ;
		default :
			return etOPERATOR ;
		}
	} else
		*result = val ;
	return etNONE ;
}
 
/**
 * expression processing
 * depending of current bMode
 * @param result resulting value of expression
 * @return error code
 */
static error_t expression( uint32_t * result ) {
	symbol_t * h = NULL ;
	char * s = NULL ;
	symbol_t * oper = NULL ;
	error_t e = etNONE ;
	uint32_t val ;
 
	*result = 0 ;
 
	// crippled expression handling
	while ( bMode && tok_current()->type != tNONE ) {
		switch ( tok_current()->type ) {
		case tLPAREN :
			tok_next() ;
			e = expression( &val ) ;
			if ( e != etNONE )
				return e ;
			if ( !processOperator( result, val, &oper ) )
				return etOPERATOR ;
		case tRPAREN :
			tok_next() ;
			return etNONE ;
		case tCOMMA :
			return etNONE ;
		case tIDENT :
			s = tok_current()->text ;
			h = find_symbol( s, false ) ;
			if ( h != NULL ) {
				val = h->value ;
			} else if ( sscanf( s, "%X", &val ) != 1 )
				return etEXPR ;
			tok_next() ;
			*result = val ;
			return etNONE ;
		default :
			return etEXPR ;
		}
	}
 
	if ( tok_current()->type == tNONE ) {
		return etEMPTY ;
	}
 
	// full expression handling
	while ( tok_current()->type != tNONE ) {
		switch ( tok_current()->type ) {
		case tLPAREN :
			tok_next() ;
			e = expression( &val ) ;
			if ( e != etNONE )
				return e ;
			if ( tok_current()->type == tRPAREN ) {
				tok_next() ;
			} else
				return etEXPR ;
			break ;
		case tOPERATOR :
		case tDEC :
		case tCHAR :
		case tHEX :
		case tBIN :
		case tIDENT :
			e = term( &val ) ;
			if ( e != etNONE )
				return e ;
			break ;
		default :
			return etNONE ;
		}
		if ( oper != NULL ) {
			if ( !processOperator( result, val, &oper ) )
				return etOPERATOR ;
			oper = NULL ;
		} else
			*result = val ;
		if ( tok_current()->type == tOPERATOR )
			oper = tok_next() ;
		else
			break ;
	}
	return etNONE ;
}
 
/**
 * destreg: process destination register
 * @param result value of destination register, shifted already in position
 * @return success
 */
static bool destreg( uint32_t * result ) {
	symbol_t * h ;
 
	if ( result == NULL )
		return false ;
	*result = 0 ;
 
	h = find_symbol( tok_current()->text, false ) ;
	if ( h != NULL && h->type == tREGISTER ) {
		tok_next() ;
		*result = h->value << 8 ;
		return true ;
	}
	return false ;
}
 
/**
 * srcreg: process source register, accepts parens
 * @param result value of source register, shifted already in position
 * @return success
 */
static bool srcreg( uint32_t * result ) {
	symbol_t * h ;
	bool bpar = false ;
	bool retval = true ;
	symbol_t * back = tok_current() ;
 
	if ( result == NULL )
		return false ;
	*result = 0 ;
 
	if ( tok_current()->type == tLPAREN ) {
		bpar = true ;
		tok_next() ;
	}
 
	h = find_symbol( tok_current()->text, false ) ;
	if ( h == NULL || h->type != tREGISTER ) {
		retval = false ;
		goto finally ;
	}
	*result = h->value << 4 ;
 
	tok_next() ;
	if ( bpar ) {
		if ( tok_current()->type == tRPAREN )
			tok_next() ;
		else {
			retval = false ;
			goto finally ;
		}
	}
 
	finally: {
		if ( !retval )
			tok_back( back ) ;
		return retval ;
	}
}
 
/**
 * eat comma in token stream
 * @return success
 */
static bool comma( void ) {
	if ( tok_current()->type == tCOMMA ) {
		tok_next() ;
		return true ;
	} else
		return false ;
}
 
/**
 * process condition token
 * @param result value of condition, already in position
 * @return success
 */
static bool condition( uint32_t * result ) {
	symbol_t * h ;
 
	if ( result == NULL )
		return false ;
	*result = 0 ;
 
	if ( tok_current()->type != tNONE ) {
		h = find_symbol( tok_current()->text, true ) ;
		if ( h != NULL && h->type == tCONDITION ) {
			tok_next() ;
			*result = h->value ;
			return true ;
		}
	}
	return false ;
}
 
/**
 * process enable token
 * @param result value of enable, already in position
 * @return success
 */
static bool enadis( uint32_t * result ) {
	symbol_t * h ;
 
	if ( result == NULL )
		return false ;
	*result = 0 ;
 
	h = find_symbol( tok_current()->text, true ) ;
	if ( h != NULL && h->type == tOPCODE && h->subtype == stINTI ) {
		tok_next() ;
		*result = h->value & 1 ;
		return true ;
	}
	return false ;
}
 
/**
 * process indexed token
 * @param result value of indexed construct, already in position
 * @return success
 */
static bool indexed( uint32_t * result ) {
	symbol_t * h ;
 
	if ( result == NULL )
		return false ;
	*result = 0 ;
 
	if ( tok_current()->type != tNONE ) {
		h = find_symbol( tok_current()->text, true ) ;
		if ( h != NULL && h->type == tINDEX ) {
			tok_next() ;
			*result = h->value ;
			return true ;
		}
	}
	return false ;
}
 
 
/**
 * first pass of assembler
 * process all source lines and build symbol table
 * @return error code
 */
static error_t build( void ) {
	build_state state = bsINIT ;
	symbol_t * symtok = NULL ;
	symbol_t * h = NULL ;
	symbol_t * r = NULL ;
	uint32_t result = 0 ;
	error_t e = etNONE ;
 
	// process statement
	for ( tok_first(), state = bsINIT ; state != bsEND ; tok_next() ) {
		switch ( tok_current()->type ) {
		case tNONE :
			// empty line?
			if ( state != bsINIT && state != bsLABELED )
				return etSYNTAX ;
			state = bsEND ;
			break ;
 
		// opcode or symbol definition
		case tIDENT :
			// opcode or directive?
			h = find_symbol( tok_current()->text, false ) ;
			if ( h == NULL ) {
				h = find_symbol( tok_current()->text, true ) ;
				if ( h == NULL || h->type != tOPCODE )
					h = NULL ;
			}
			if ( h != NULL ) {
				switch ( h->type ) {
				case tLABEL :
					if ( state != bsINIT )
						return etSYNTAX ;
					if ( h->subtype != stDOT )
						return etSYNTAX ;
					h->value = gPC ;
					symtok = tok_current() ;
					state = bsLABEL ;
					break ;
 
				case tOPCODE :
					if ( state != bsINIT && state != bsLABELED )
						return etSYNTAX ;
					gPC += 1 ;
					state = bsEND ; // we know enough for now
					break ;
 
				case tDIRECTIVE :
					switch ( h->subtype ) {
 
					case stPAGE :
						state = bsEND ;
						break ;
 
					// ORG
					case stADDRESS :
					case stORG :
						if ( state != bsINIT )
							return etSYNTAX ;
						tok_next() ;
						if ( ( e = expression( &result ) ) == etNONE ) {
							if ( result >= 1024 ) // within code range
								return etRANGE ;
							gPC = result ;
						} else
							return e ;
						state = bsEND ;
						break ;
 
						// _SRC
					case stSCRATCHPAD :
						if ( state != bsINIT )
							return etSYNTAX ;
						tok_next() ;
						if ( ( e = expression( &result ) ) == etNONE ) {
							if ( result >= 1024 + 128 ) // within code + scratchpad range
								return etRANGE ;
							gSCR = result * 2 ;
						} else
							return e ;
						state = bsEND ;
						break ;
 
						// EQU
					case stEQU :
						if ( state != bsSYMBOL )
							return etSYNTAX ;
						tok_next() ;
						if ( symtok != NULL ) {
							// register clone?
							r = find_symbol( tok_current()->text, false ) ;
							if ( r != NULL && r->type == tREGISTER ) {
								if ( !add_symbol( tREGISTER, stCLONE, symtok->text, r->value ) )
									return etDOUBLE ;
								else {
									state = bsEND ;
									break ;
								}
							} else if ( ( e = expression( &result ) ) == etNONE ) {
								// normal expression?
								if ( !add_symbol( tVALUE, stINT, symtok->text, result ) )
									return etDOUBLE ;
							} else
								return e ;
						} else
							return etMISSING ;
						state = bsEND ;
						break ;
 
					case stCONSTANT :
						if ( state != bsINIT )
							return etSYNTAX ;
						tok_next() ;
						symtok = tok_next() ;
						if ( symtok->type != tIDENT )
							return etSYNTAX ;
						if ( !comma() )
							return etCOMMA ;
						// normal expression?
						if ( ( e = expression( &result ) ) == etNONE ) {
							if ( !add_symbol( tVALUE, stINT, symtok->text, result ) )
								return etDOUBLE ;
						} else
							return e ;
						state = bsEND ;
						break ;
 
					case stNAMEREG :
						if ( state != bsINIT )
							return etSYNTAX ;
						tok_next() ;
						symtok = tok_next() ;
						if ( symtok->type != tIDENT )
							return etSYNTAX ;
						r = find_symbol( symtok->text, true ) ;
						if ( r == NULL || r->type != tREGISTER )
							return etREGISTER ;
						if ( !comma() )
							return etCOMMA ;
						if ( tok_current()->type == tIDENT ) {
							if ( !add_symbol( tREGISTER, stCLONE, tok_current()->text, r->value ) )
								return etDOUBLE ;
						} else
							return etSYNTAX ;
						state = bsEND ;
						break ;
 
					case stSFR :
						// DS, pBlazIDE support
					case stDS :
					case stDSIN :
					case stDSOUT :
					case stDSIO :
					case stDSRAM :
					case stDSROM :
						if ( state != bsSYMBOL )
							return etSYNTAX ;
						tok_next() ;
						if ( symtok != NULL ) {
							if ( ( e = expression( &result ) ) == etNONE ) {
								if ( !add_symbol( tVALUE, stINT, symtok->text, result ) )
									return etDOUBLE ;
							} else
								return e ;
						} else
							return etMISSING ;
						state = bsEND ;
						break ;
 
						// .BYT etc
					case stBYTE :
					case stWORD_BE : case stWORD_LE :
					case stLONG_BE : case stLONG_LE :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						tok_next() ;
						if ( symtok && !add_symbol( tVALUE, stINT, symtok->text, ( gSCR ) & 0xFF ) )
							return etDOUBLE ;
						do {
							if ( ( e = expression( &result ) ) != etNONE ) {
								if ( e == etEMPTY )
									break ; // allow an empty expression list for generating a symbol only
								else
									return e ;
							}
							switch ( h->subtype ) {
							case stLONG_BE :
							case stLONG_LE :
								gSCR += 2 ;
							case stWORD_BE :
							case stWORD_LE :
								gSCR += 1 ;
							default :
								gSCR += 1 ;
							}
						} while ( comma() ) ;
						state = bsEND ;
						break ;
 
						// .BUF
					case stBUFFER :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						tok_next() ;
						if ( symtok && !add_symbol( tVALUE, stINT, symtok->text, ( gSCR ) & 0xFF ) )
							return etDOUBLE ;
						if ( ( e = expression( &result ) ) == etNONE ) {
							if ( result < 256 )
								gSCR += result ;
							else
								return etRANGE ;
						} else
							return e ;
						state = bsEND ;
						break ;
 
						// .TXT
					case stTEXT :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						tok_next() ;
						if ( symtok && !add_symbol( tVALUE, stINT, symtok->text, ( gSCR ) & 0xFF ) )
							return etDOUBLE ;
						if ( tok_current()->type == tSTRING ) {
							char * dup = convert_string( tok_current()->text ) ;
							gSCR += strlen( dup ) + 1 ;
							free( dup ) ;
						} else
							return etEXPR ;
						state = bsEND ;
						break ;
 
					case stVHDL :
					case stHEX :
					case stMEM :
					case stCOE :
						if ( state != bsINIT )
							return etSYNTAX ;
						state = bsEND ;
						break ;
 
					default :
						return etSYNTAX ;
					}
					break ;
				default :
					return etDOUBLE ;
				}
			} else if ( state == bsINIT ) {
				// not known yet, label or symbol definition?
				symtok = tok_current() ;
				state = bsSYMBOL ;
			} else {
				h = find_symbol( tok_current()->text, true ) ;
				// opcode mnemonic in lower/mixed case?
				if ( h != NULL && h->type == tOPCODE ) {
					if ( state != bsINIT && state != bsLABELED )
						return etSYNTAX ;
					gPC += 1 ;
					state = bsEND ; // we know enough for now
				}
			}
			break ;
 
		case tCOLON :
			if ( state == bsLABEL )
				;
			else if ( state != bsSYMBOL )
				return etSYNTAX ;
			else if ( !add_symbol( tLABEL, symtok->subtype, symtok->text, gPC ) )
				return etDOUBLE ;
			state = bsLABELED ;
			break ;
 
		default :
			return etSYNTAX ;
		}
	}
	return etNONE ;
}
 
/**
 * second pass of assembler
 * process all source lines and build code and scratchpad contents
 * @param addr ref to address value for listing
 * @param code ref to code value for listing
 * @return error code
 */
static error_t assemble( uint32_t * addr, uint32_t * code ) {
	build_state state = bsINIT ;
	symbol_t * h = NULL ;
	uint32_t result = 0 ;
	uint32_t operand1 = 0 ;
	uint32_t operand2 = 0 ;
	uint32_t opcode = 0 ;
	uint32_t oPC = 0 ;
	error_t e = etNONE ;
 
	*addr = 0xFFFFFFFF ;
	*code = 0xFFFFFFFF ;
 
	// process statement
	for ( tok_first(), state = bsINIT ; state != bsEND ; ) {
		switch ( tok_current()->type ) {
		case tNONE :
			// empty line?
			if ( state != bsINIT && state != bsLABELED )
				return etSYNTAX ;
			state = bsEND ;
			break ;
 
		case tIDENT :
			h = find_symbol( tok_current()->text, false ) ;
			// opcode mnemonic in lower/mixed case?
			if ( h == NULL ) {
				h = find_symbol( tok_current()->text, true ) ;
				if ( h == NULL || h->type != tOPCODE )
					h = NULL ;
			}
			if ( h != NULL ) {
				switch ( h->type ) {
				// opcodes
				case tOPCODE :
					if ( state != bsINIT && state != bsLABELED )
						return etSYNTAX ;
					tok_next() ;
					opcode = 0xFFFFFFFF ;
					operand1 = 0 ;
					operand2 = 0 ;
					oPC = gPC ;
					gPC += 1 ;
 
					switch ( h->subtype ) {
					case stMOVE :
						if ( !destreg( &operand1 ) )
							return etREGISTER ;
						if ( !comma() )
							return etCOMMA ;
						if ( !srcreg( &operand2 ) ) {
							if ( ( e = expression( &operand2 ) ) != etNONE )
								return e ;
							opcode = h->value | operand1 | ( operand2 & 0xFF ) ;
						} else
							opcode = h->value | operand1 | ( operand2 & 0xFF ) | 0x01000 ;
						break ;
 
					case stCJMP :
						if ( condition( &operand1 ) ) {
							if ( !comma() )
								return etCOMMA ;
						}
						if ( ( e = expression( &operand2 ) ) != etNONE )
							return e ;
						opcode = h->value | operand1 | ( operand2 & 0x3FF ) ;
						break ;
 
					case stCSKP :
						condition( &operand1 ) ;
						opcode = h->value | operand1 | ( oPC + 2 ) ;
						break ;
 
					case stCRET :
						condition( &operand1 ) ;
						opcode = h->value | operand1 ;
						break ;
 
					case stINT :
						opcode = h->value ;
						break ;
 
					case stINTI :
						if ( !bMode )
							return etNOTIMPL ;
						opcode = h->value ;
						if ( tok_current()->type != tIDENT || strcasecmp( tok_current()->text, "INTERRUPT" ) != 0 )
							return etMISSING ;
						tok_next() ;
						break ;
 
					case stINTE :
						opcode = h->value ;
						if ( enadis( &operand1 ) )
							opcode = ( h->value & 0xFFFFE ) | operand1 ;
						break ;
 
					case stIO :
						if ( !destreg( &operand1 ) )
							return etREGISTER ;
						if ( !comma() )
							return etCOMMA ;
						if ( !srcreg( &operand2 ) ) {
							if ( !indexed( &operand2 ) ) {
								if ( ( e = expression( &operand2 ) ) != etNONE )
									return e ;
								opcode = h->value | operand1 | ( operand2 & 0xFF ) ;
							} else
								opcode = h->value | operand1 | operand2 ;
						} else
							opcode = h->value | operand1 | ( operand2 & 0xFF ) | 0x01000 ;
						break ;
 
					case stSHIFT :
						if ( !destreg( &operand1 ) )
							return etREGISTER ;
						opcode = h->value | operand1 ;
						break ;
 
					case stINST :
						if ( ( e = expression( &opcode ) ) != etNONE )
							return e ;
						break ;
 
					default :
						return etNOTIMPL ;
					}
					if ( opcode == 0xFFFFFFFF )
						return etINTERNAL ;
					if ( oPC < 1024 ) {
						gCode[ oPC ] = opcode ;
						*addr = oPC ;
						*code = opcode ;
					} else
						return etRANGE ;
					state = bsEND ;
					break ;
 
					// directives
				case tDIRECTIVE :
					tok_next() ;
					switch ( h->subtype ) {
 
					case stPAGE :
						break ;
 
					case stADDRESS :
						if ( !bMode )
							return etNOTIMPL ;
					case stORG :
						if ( state != bsINIT )
							return etSYNTAX ;
						if ( ( e = expression( &result ) ) == etNONE ) {
							if ( result >= 1024 )
								return etRANGE ;
							gPC = result ;
							*addr = gPC ;
						} else
							return e ;
						break ;
 
					case stSCRATCHPAD :
						if ( state != bsINIT )
							return etSYNTAX ;
						if ( ( e = expression( &result ) ) == etNONE ) {
							// within code range
							if ( result >= 1024 + 256 / 2 )
								return etRANGE ;
							gSCR = result * 2 ;
							*addr = gSCR ;
						} else
							return e ;
						break ;
 
					case stEQU :
						if ( state != bsSYMBOL )
							return etSYNTAX ;
						// NO-OP, just eat tokens in an orderly way
						if ( destreg( &result ) ) {
						} else if ( ( e = expression( &result ) ) == etNONE ) {
							*code = result ;
						} else
							return e ;
						break ;
 
					case stSFR :
					case stDS :
					case stDSIN :
					case stDSOUT :
					case stDSIO :
					case stDSRAM :
					case stDSROM :
						if ( state != bsSYMBOL )
							return etSYNTAX ;
						// NO-OP, just eat tokens in an orderly way
						do {
							if ( ( e = expression( &result ) ) == etNONE ) {
							} else
								return e ;
						} while ( comma() ) ;
						break ;
 
					case stBYTE :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						*addr = gSCR ;
						*code = 0xFFFFFFFF ;
						do {
							if ( ( e = expression( &result ) ) == etNONE ) {
								if ( result > 0xFF )
									return etOVERFLOW ;
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >> 0 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result << 8 ) & 0xFF00 ) ;
								gSCR += 1 ;
							} else if ( e == etEMPTY ) {
								// allow an empty expression list for generating a symbol only
								break ;
							} else
								return e ;
							// only show the first 2 bytes as a 'uint18'
							if ( ( ( gSCR - 1 ) & 0xFFFE ) == ( *addr & 0xFFFE ) )
								*code = gCode[ ( gSCR - 1 ) / 2 ] ;
						} while ( comma() ) ;
						break ;
 
					case stWORD_BE :
					case stWORD_LE :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						*addr = gSCR ;
						*code = 0xFFFFFFFF ;
						do {
							if ( ( e = expression( &result ) ) == etNONE ) {
								if ( result > 0xFFFF )
									return etOVERFLOW ;
								result &= 0xFFFF ;
								if ( h->subtype == stWORD_BE )
									result = ( ( result & 0xFF00FF00 ) >> 8 ) | ( ( result & 0x00FF00FF ) << 8 ) ;
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >> 0 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result << 8 ) & 0xFF00 ) ;
								gSCR += 1 ;
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >> 8 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result >> 0 ) & 0xFF00 ) ;
								gSCR += 1 ;
							} else if ( e == etEMPTY ) {
								// allow an empty expression list for generating a symbol only
								break ;
							} else
								return e ;
							// only show the first 2 bytes as a 'uint18'
							if ( ( ( gSCR - 2 ) & 0xFFFE ) == ( *addr & 0xFFFE ) )
								*code = gCode[ ( gSCR - 2 ) / 2 ] ;
						} while ( comma() ) ;
						break ;
 
					case stLONG_BE :
					case stLONG_LE :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						*addr = gSCR ;
						*code = 0xFFFFFFFF ;
						do {
							if ( ( e = expression( &result ) ) == etNONE ) {
								if ( h->subtype == stLONG_BE ) {
									result = ( ( result & 0xFFFF0000 ) >> 16 ) | ( ( result & 0x0000FFFF ) << 16 ) ;
									result = ( ( result & 0xFF00FF00 ) >> 8 ) | ( ( result & 0x00FF00FF ) << 8 ) ;
								}
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >>  0 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result <<  8 ) & 0xFF00 ) ;
								gSCR += 1 ;
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >>  8 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result >>  0 ) & 0xFF00 ) ;
								gSCR += 1 ;
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >> 16 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result >>  8 ) & 0xFF00 ) ;
								gSCR += 1 ;
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( ( result >> 24 ) & 0x00FF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( result >> 16 ) & 0xFF00 ) ;
								gSCR += 1 ;
							} else if ( e == etEMPTY ) {
								// allow an empty expression list for generating a symbol only
								break ;
							} else
								return e ;
							// only show the first 2 bytes as a 'uint18'
							if ( ( ( gSCR - 4 ) & 0xFFFE ) == ( *addr & 0xFFFE ) )
								*code = gCode[ ( gSCR - 4 ) / 2 ] ;
						} while ( comma() ) ;
						break ;
 
					case stBUFFER :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						if ( ( e = expression( &result ) ) == etNONE ) {
							if ( result < 256 ) {
								*addr = gSCR ;
								gSCR += result ;
								*code = 0xFFFF0000 | result ;
							} else
								return etRANGE ;
						} else
							return e ;
						break ;
 
						// _TXT
					case stTEXT :
						if ( state != bsINIT && state != bsSYMBOL )
							return etSYNTAX ;
						if ( tok_current()->type == tSTRING ) {
							char * dup = convert_string( tok_current()->text ) ;
							int i = 0 ;
 
							*addr = gSCR ;
							for ( i = 0 ; i < strlen( dup ) + 1 ; i += 1 ) {
								if ( ( gSCR & 1 ) == 0 )
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x0FF00 ) | ( dup[ i ] & 0xFF ) ;
								else
									gCode[ gSCR / 2 ] = ( gCode[ gSCR / 2 ] & 0x000FF ) | ( ( dup[ i ] & 0xFF ) << 8 ) ;
								// only show the first 2 bytes as a 'uint18'
								if ( ( gSCR & 0xFFFE ) == ( *addr & 0xFFFE ) )
									*code = gCode[ gSCR / 2 ] ;
								gSCR += 1 ;
							}
							free( dup ) ;
						} else
							return etEXPR ;
						tok_next() ;
						break ;
 
					case stVHDL :
					case stHEX :
					case stMEM :
					case stCOE :
						if ( state != bsINIT )
							return etSYNTAX ;
						//						if ( bKCPSM_mode )
						return etNOTIMPL ;
						break ;
 
					case stCONSTANT :
						if ( state != bsINIT )
							return etSYNTAX ;
						tok_next() ;
						if ( !comma() )
							return etCOMMA ;
						// normal expression?
						if ( ( e = expression( &result ) ) == etNONE ) {
							*code = result ;
						} else
							return e ;
						break ;
 
					case stNAMEREG :
						if ( state != bsINIT )
							return etSYNTAX ;
						if ( !bMode )
							return etNOTIMPL ;
						tok_next() ;
						tok_next() ;
						tok_next() ;
						break ;
 
					default :
						return etSYNTAX ;
					}
					state = bsEND ;
					break ;
 
					// labels
				case tLABEL :
					if ( state != bsINIT )
						return etSYNTAX ;
					if ( h->value != gPC && h->subtype != stDOT )
						return etPHASING ;
					tok_next()->type = tLABEL ; // just for formatting
					h->value = gPC ;
					*addr = h->value ;
					state = bsLABEL ;
					break ;
 
					// equated values
				case tVALUE :
				case tREGISTER :
					if ( state != bsINIT )
						return etSYNTAX ;
					tok_next()->subtype = stEQU ; // just for formatting
					*code = h->value & 0xFFFF ;
					state = bsSYMBOL ;
					break ;
 
				default :
					return etSYNTAX ;
				}
			} else
				return etUNDEF ;
			break ;
 
		case tCOLON :
			// if we have a potential label, we need a ':'
			if ( state != bsLABEL )
				return etSYNTAX ;
			tok_next() ;
			state = bsLABELED ;
			break ;
 
		default :
			return etSYNTAX ;
		}
	}
	// only comment may follow
	if ( tok_current()->type != tNONE )
		return etEND ;
	return etNONE ;
}
 
// error printer
static bool error( const error_t e ) {
	if ( e != etNONE ) {
		fprintf( stdout, "%s:%d: %s\n", gSource, gLinenr, s_errors[ e ] ) ;
		return false ;
	} else
		return true ;
}
 
// dump code in mem file format
static void dump_code( FILE * f, bool hex, bool zeros ) {
	int h, l = 0 ;
	bool b_addr = true ;
 
	if ( hex ) {
		// find last used entry
		for ( h = 0, l = 1024 ; h < 1024 ; h += 1 )
			if ( gCode[ h ] != 0xFFFC0000 && ! zeros )
				l = h ;
		// list all
		for ( h = 0 ; h <= l ; h += 1 )
			fprintf( f, "%05X\n", gCode[ h ] & 0x3FFFF ) ;
	} else {
		// list used code entries, prepend an origin
		for ( h = 0 ; h < 1024 ; h += 1 ) {
			if ( gCode[ h ] == 0xFFFC0000 && ! zeros )
				b_addr = true ;
			else {
				if ( b_addr ) {
					fprintf( f, "@%08X\n", h ) ;
					b_addr = false ;
				}
				fprintf( f, "%05X\n", gCode[ h ] & 0x3FFFF ) ;
			}
		}
		// list used scratchpad entries, prepend an origin
		b_addr = true ;
		for ( h = 1024 ; h < 1024 + 256 / 2 ; h += 1 ) {
			if ( gCode[ h ] == 0xFFFC0000 )
				b_addr = true ;
			else {
				if ( b_addr ) {
						fprintf( f, "@%08X\n", h ) ;
					b_addr = false ;
				}
				fprintf( f, "%05X\n", gCode[ h ] & 0x3FFFF ) ;
			}
		}
	}
}
 
// format list file
static void print_line( FILE * f, error_t e, uint32_t addr, uint32_t code ) {
	int n = 0 ;
	char * s = NULL ;
 
	tok_first() ;
 
	if ( e != etNONE )
		fprintf( f, "?? %s:\n", s_errors[ e ] ) ;
 
	if ( tok_current()->type == tDIRECTIVE && tok_current()->subtype == stPAGE ) {
		fprintf( f, "\f" ) ;
		return ;
	}
 
	if ( bCode ) {
		// code info
		if ( code != 0xFFFFFFFF ) {
			if ( addr != 0xFFFFFFFF ) {
		        // address info
				n += fprintf( f, "%03X ", addr ) ;
				if ( code <= 0xFFFFF )
					n += fprintf( f, "%05X ", code ) ;
				else
					n += fprintf( f, "  %02X  ", code & 0xFF ) ;
			} else if ( code > 0xFFFF )
				n += fprintf( f, "%08X  ", code ) ;
			else if ( code > 0xFF )
				n += fprintf( f, "    %04X  ", code & 0xFFFF ) ;
			else
				n += fprintf( f, "      %02X  ", code & 0xFF ) ;
		} else {
			if ( addr != 0xFFFFFFFF ) {
				// address info
				n += fprintf( f, "%03X       ", addr ) ;
			} else
				n += fprintf( f, "          " ) ;
		}
	}
 
	if ( tok_current()->type == tLABEL ) {
		// labels in the margin
		n += fprintf( f, "%*s", -16, tok_next()->text ) ;
		n += fprintf( f, "%s", tok_next()->text ) ;
	} else if ( tok_current()->subtype == stEQU ) {
		// print EQUates in the label margin
		n += fprintf( f, "%*s", -( 16 + 1 ), tok_next()->text ) ;
	} else if ( tok_current()->type != tNONE )
		// else print a blank margin
		n += fprintf( f, "%*s", 16 + 1, "" ) ;
 
	// opcode
	if ( tok_current()->type != tNONE && tok_current()->text != NULL ) {
		for ( s = tok_current()->text ; s != NULL && isalpha( *s ) ; s++ )
			*s = toupper( *s ) ;
		n += fprintf( f, " %*s", -6, tok_next()->text ) ;
	}
 
	// operand
	for ( ; tok_current()->type != tNONE ; tok_next() ) {
		if ( tok_current()->text != NULL ) {
			if ( tok_current()->type != tCOMMA )
				n += fprintf( f, " " ) ;
			switch ( tok_current()->type ) {
			case tHEX :
				n += fprintf( f, "0x%s", tok_current()->text ) ;
				break ;
			case tBIN :
				n += fprintf( f, "0b%s", tok_current()->text ) ;
				break ;
			case tCHAR :
				n += fprintf( f, "'%s'", tok_current()->text ) ;
				break ;
			case tSTRING :
				n += fprintf( f, "\"%s\"", tok_current()->text ) ;
				break ;
			default :
				n += fprintf( f, "%s", tok_current()->text ) ;
				break ;
			}
		}
	}
 
	// comment
	if ( tok_current()->type == tNONE && tok_current()->subtype == stCOMMENT ) {
		if ( tok_current()->text != NULL ) {
			if ( n <= 10 )
				// at the start
				fprintf( f, "%s", tok_current()->text ) ;
			else if ( n < 60 ) {
				// at column 60
				fprintf( f, "%*s", 60 - n, "" ) ;
				fprintf( f, "%s", tok_current()->text ) ;
			} else
				// after the rest
				fprintf( f, " %s", tok_current()->text ) ;
		}
	}
	fprintf( f, "\n" ) ;
}
 
// main entry for the 2-pass assembler
bool assembler( char ** sourcefilenames, char * codefilename, char * listfilename, bool mode, bool listcode, bool hex, bool zeros ) {
	FILE * fsrc = NULL ;
	FILE * fmem = NULL ;
	FILE * flist = NULL ;
	char ** Sources = NULL ;
	char line[ 256 ] ;
	error_t e = etNONE ;
	int h = 0 ;
	bool result = true ;
 
	uint32_t addr, code ;
 
	// set up symbol table with keywords
	init_symbol() ;
 
	// clear code
	for ( h = 0 ; h < 1024 + 256 / 2 ; h += 1 )
		gCode[ h ] = 0xFFFC0000 ;
 
	Sources = sourcefilenames ;
	gPC = 0 ;
	gSCR = 2048 ;
	bMode = mode ;
	for ( gSource = *Sources++ ; gSource != NULL ; gSource = *Sources++ ) {
		// open source file
		fsrc = fopen( gSource, "r" ) ;
		if ( fsrc == NULL ) {
			fprintf( stderr, "? unable to open source file '%s'", gSource ) ;
			result = false ;
			goto finally ;
		}
 
		// pass 1, add symbols from source
		for ( gLinenr = 1 ; fgets( line, sizeof( line ), fsrc ) != NULL ; gLinenr += 1 ) {
			if ( lex( line, mode ) ) {
				result &= error( build() ) ;
				tok_free() ;
			} else {
				result &= error( etLEX ) ;
			}
		}
		fclose( fsrc ) ;
	}
 
	// give up if errors in pass 1
	if ( !result )
		goto finally ;
 
	if ( strlen( listfilename ) > 0 ) {
		flist = fopen( listfilename, "w" ) ;
		if ( flist == NULL ) {
			fprintf( stderr, "? unable to create LST file '%s'", listfilename ) ;
			result = false ;
		}
	}
 
	bCode = listcode ;
	Sources = sourcefilenames ;
	for ( gSource = *Sources++, gPC = 0, gSCR = 2048, bMode = mode ; gSource != NULL ; gSource = *Sources++ ) {
 
		fsrc = fopen( gSource, "r" ) ;
		if ( fsrc == NULL ) {
			fprintf( stderr, "? unable to re-open source file '%s'", gSource ) ;
			result = false ;
			goto finally ;
		}
 
		// pass 2, build code and scratchpad
		for ( gLinenr = 1 ; fgets( line, sizeof( line ), fsrc ) != NULL ; gLinenr += 1 ) {
			if ( lex( line, mode ) ) {
				result &= error( e = assemble( &addr, &code ) ) ;
				if ( flist != NULL )
					print_line( flist, e, addr, code ) ;
			} else {
				result &= error( etLEX ) ;
				if ( flist != NULL )
					print_line( flist, etLEX, 0xFFFFFFFF, 0xFFFFFFFF ) ;
			}
			tok_free() ;
		}
		fclose( fsrc ) ;
	}
 
	// dump code and scratch pad
	if ( strlen( codefilename ) > 0 ) {
		fmem = fopen( codefilename, "w" ) ;
		if ( fmem == NULL ) {
			fprintf( stderr, "? unable to create MEM file '%s'", codefilename ) ;
			result = false ;
		} else {
			dump_code( fmem, hex, zeros ) ;
			fclose( fmem ) ;
		}
	}
 
	finally: {
		if ( flist != NULL )
			fclose( flist ) ;
		free_symbol() ;
	}
 
	return result ;
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.