/* The assembler works, but it ain't a pretty sight. * Some day, I'll rewrite this part. * -- M6 */ #include "cassembler.h" #include "cpicoblaze.h" #include #define NO_LINE_NR 0xFFFFFFFF const char *instructions[] = { "ADD", "ADDCY", "AND", "CALL", "COMPARE", "DISABLE", "ENABLE", "FETCH", "INPUT", "JUMP", "LOAD", "OR", "OUTPUT", "RETURN", "RETURNI", "ROTATE", "RL", "RR", "SL0", "SL1", "SLA", "SLX", "SR0", "SR1", "SRA", "SRX", "STORE", "SUB", "SUBCY", "TEST", "XOR" } ; /* Helper function to make a string uppercase */ string toUpper( string str ) { string upperStr ; unsigned int i ; upperStr = "" ; for ( i = 0 ; i < str.length() ; i++ ) upperStr += toupper( str[ i ] ) ; return upperStr ; } CAssembler::CAssembler() { m_messageList = 0 ; } CAssembler::~CAssembler() { } void CAssembler::error( unsigned int line, const char *description ) { cout << line << ": " << description << "\r\n" ; if ( m_messageList ) { char str[ 128 ] ; sprintf( str, "%u", line + 1 ) ; TQListViewItem *item = new TQListViewItem( m_messageList, m_messageList->lastChild() ) ; if ( line != NO_LINE_NR ) item->setText( 0, str ) ; item->setText( 1, description ) ; } } int CAssembler::getRegister( string name ) { if ( name[ 0 ] != 's' || name.length() <= 1 ) return -1 ; int reg ; if ( sscanf( name.c_str() + 1, "%X", ® ) != 1 ) return -1 ; if ( reg < 0 || reg > 15 ) return -1 ; return reg ; } int CAssembler::getInstruction( string name ) { unsigned int i ; string str = toUpper( name ) ; for ( i = 0 ; i < sizeof( instructions ) / sizeof( char *); i++ ) if ( str == instructions[ i ] ) return i ; return -1 ; } bool CAssembler::buildSymbolTable() { list::iterator it ; unsigned int address = 0 ; bool ret = TRUE ; for ( it = m_source.begin() ; it != m_source.end() ; it++ ) { string name = toUpper( (*it)->getColumn( 0 ) ) ; // case insensitive if ( name == "NAMEREG" ) { if ( !(*it)->isColumn( 3 ) ) { error( (*it)->m_lineNr, "'NAMEREG registername, newname' expected" ) ; ret = FALSE ; } if ( (*it)->isColumn( 4 ) ) { error( (*it)->m_lineNr, "Rubbish found at end of line" ) ; ret = FALSE ; } if ( (*it)->getColumn( 2 ) != "," ) { error( (*it)->m_lineNr, "Comma expected" ) ; ret = FALSE ; } CNamereg *nr = new CNamereg ; nr->reg = (*it)->getColumn( 1 ) ; nr->name = (*it)->getColumn( 3 ) ; m_registerTable.push_back( nr ) ; (*it)->m_type = CSourceLine::stNamereg ; } else if ( name == "CONSTANT" ) { if ( !(*it)->isColumn( 3 ) ) { error( (*it)->m_lineNr, "'CONSTANT name, valued' expected" ) ; ret = FALSE ; } if ( (*it)->isColumn( 4 ) ) { error( (*it)->m_lineNr, "Rubbish found at end of line" ) ; ret = FALSE ; } if ( (*it)->getColumn( 2 ) != "," ) { error( (*it)->m_lineNr, "Comma expected" ) ; ret = FALSE ; } CConstant *nr = new CConstant ; nr->value = (*it)->getColumn( 3 ) ; nr->name = (*it)->getColumn( 1 ) ; ; m_constantTable.push_back( nr ) ; (*it)->m_type = CSourceLine::stConstant ; } else if ( name == "ADDRESS" ) { if ( !(*it)->isColumn( 1 ) ) { error( (*it)->m_lineNr, "Value expected" ) ; ret = FALSE ; } if ( (*it)->isColumn( 4 ) ) { error( (*it)->m_lineNr, "Rubbish found at end of line" ) ; ret = FALSE ; } if ( sscanf( (*it)->getColumn( 1 ).c_str(), "%X", &address ) != 1 ) { error( (*it)->m_lineNr, "Invalid address" ) ; ret = FALSE ; } (*it)->m_type = CSourceLine::stAddress ; (*it)->m_address = address ; } else if ( getInstruction( (*it)->getColumn( 0 ) ) < 0 ) { CLabel *label = new CLabel ; label->name = (*it)->getColumn( 0 ) ; char buf[ 32 ] ; sprintf( buf, "%d", address ) ; label->value = buf ; m_labelTable.push_back( label ) ; (*it)->m_type = CSourceLine::stLabel ; (*it)->m_address = address ; if ( (*it)->isColumn( 1 ) && (*it)->getColumn( 1 ) == ":" ) { if ( (*it)->isColumn( 2 ) ) { if ( getInstruction( (*it)->getColumn( 2 ) ) < 0 ) { error( (*it)->m_lineNr, "Instruction expected" ) ; ret = FALSE ; } else { address = address + 1 ; } } } else { error( (*it)->m_lineNr, "Label or Instruction expected" ) ; ret = FALSE ; } } else { (*it)->m_address = address ; address = address + 1 ; } } cout << "Constants :\r\n" ; list::iterator it0 ; for ( it0 = m_constantTable.begin() ; it0 != m_constantTable.end() ; it0++ ) { cout << (*it0)->name << " = " << (*it0)->value << "\r\n" ; } cout << "Namereg :\r\n" ; list::iterator it1 ; for ( it1 = m_registerTable.begin() ; it1 != m_registerTable.end() ; it1++ ) { cout << (*it1)->reg << " = " << (*it1)->name << "\r\n" ; } cout << "labels :\r\n" ; list::iterator it2 ; for ( it2 = m_labelTable.begin() ; it2 != m_labelTable.end() ; it2++ ) { cout << (*it2)->name << " = " << (*it2)->value << "\r\n" ; } return ret ; } string CAssembler::translateRegister( string name ) { list::iterator it1 ; for ( it1 = m_registerTable.begin() ; it1 != m_registerTable.end() ; it1++ ) { if ( (*it1)->name == name ) return (*it1)->reg ; } return name ; } string CAssembler::translateConstant( string name ) { list::iterator it1 ; for ( it1 = m_constantTable.begin() ; it1 != m_constantTable.end() ; it1++ ) { if ( (*it1)->name == name ) return (*it1)->value ; } return name ; } string CAssembler::translateLabel( string label ) { list::iterator it1 ; for ( it1 = m_labelTable.begin() ; it1 != m_labelTable.end() ; it1++ ) { if ( (*it1)->name == label ) return (*it1)->value ; } return label ; } bool CAssembler::addInstruction( instrNumber instr, CSourceLine sourceLine, int offset ) { char err_desc[ 256 ] ; unsigned int address = sourceLine.m_address ; int maxColumn = 0 ; string s1 = sourceLine.getColumn( offset + 1 ) ; string s2 = sourceLine.getColumn( offset + 2 ) ; string s3 = sourceLine.getColumn( offset + 3 ) ; int line = sourceLine.m_lineNr ; uint32_t code ; string s ; bool b ; switch( instr ) { case ENABLE: case DISABLE: if ( toUpper( s1 ) != "INTERRUPT" ) { error( line, "'INTERRUPT' expected" ) ; return FALSE ; } if ( instr == ENABLE ) code = instrENABLE_INTERRUPT ; else code = instrDISABLE_INTERRUPT ; maxColumn = 2 ; break ; case RETURNI: if ( toUpper( s1 ) == "ENABLE" ) { code = instrRETURNI_ENABLE ; } else if ( toUpper( s1 ) == "DISABLE" ) { code = instrRETURNI_DISABLE ; } else { error( line, "'ENABLE' or 'DISABLE' expected" ) ; } maxColumn = 2 ; break ; // Almost the same instructions case CALL: case JUMP: case RETURN: b = TRUE ; maxColumn= 2 ; if ( toUpper( s1 ) == "C" ) { switch( instr ) { case CALL : code = instrCALLC ; break ; case JUMP : code = instrJUMPC ; break ; case RETURN : code = instrRETURNC ; break ; default: error( line, "'CALL', 'JUMP' or 'RETURN' expected" ) ; return FALSE ; } } else if ( toUpper( s1 ) == "NC" ) { switch( instr ) { case CALL : code = instrCALLNC ; break ; case JUMP : code = instrJUMPNC ; break ; case RETURN : code = instrRETURNNC ; break ; default: error( line, "'CALL', 'JUMP' or 'RETURN' expected" ) ; return FALSE ; } } else if ( toUpper( s1 ) == "NZ" ) { switch( instr ) { case CALL : code = instrCALLNZ ; break ; case JUMP : code = instrJUMPNZ ; break ; case RETURN : code = instrRETURNNZ ; break ; default: error( line, "'CALL', 'JUMP' or 'RETURN' expected" ) ; return FALSE ; } } else if ( toUpper( s1 ) == "Z" ) { switch( instr ) { case CALL : code = instrCALLZ ; break ; case JUMP : code = instrJUMPZ ; break ; case RETURN : code = instrRETURNZ ; break ; default: error( line, "'CALL', 'JUMP' or 'RETURN' expected" ) ; return FALSE ; } } else { switch( instr ) { case CALL : code = instrCALL ; break ; case JUMP : code = instrJUMP ; break ; case RETURN : code = instrRETURN ; break ; default: error( line, "'CALL', 'JUMP' or 'RETURN' expected" ) ; return FALSE ; } b = FALSE ; maxColumn = 1 ; } if ( instr != RETURN ) { if ( b ) { if ( s2 != "," ) { error( line, "Comma expected" ) ; return FALSE ; } s = s3 ; } else s = s1 ; maxColumn = b ? 4 : 2 ; s = translateLabel( s ) ; int labelVal ; if ( sscanf( s.c_str(), "%d", &labelVal ) != 1 ) { error( line, "Invalid label" ) ; return FALSE ; } code |= labelVal ; } break ; // Instruction that expect first an registername default: int reg = getRegister( translateRegister( s1 ) ) ; if ( reg < 0 ) { error( line, "Registername expected" ) ; return FALSE ; } code = instrROTATE | (reg<<8) ; maxColumn = 2 ; switch ( instr ) { case RL: code |= instrRL_SX ; break ; case RR: code |= instrRR_SX ; break ; case SL0: code |= instrSL0_SX ; break ; case SL1: code |= instrSL1_SX ; break ; case SLA: code |= instrSLA_SX ; break ; case SLX: code |= instrSLX_SX ; break ; case SR0: code |= instrSR0_SX ; break ; case SR1: code |= instrSR1_SX ; break ; case SRA: code |= instrSRA_SX ; break ; case SRX: code |= instrSRX_SX ; break ; // Instructions that expect a registername and then a comma default: if ( s2 != "," ) { error( line, "Comma expected" ) ; return FALSE ; } switch( instr ) { // Instruction Register Comma '(' or value case STORE: case OUTPUT: case INPUT: case FETCH: if ( sourceLine.getColumn( offset + 3 ) == "(" ) { if ( !sourceLine.isColumn( offset + 5 ) || sourceLine.getColumn( offset + 5 ) != ")" ) { error( line, "')' expected" ) ; return FALSE ; } int reg2 = getRegister( translateRegister( sourceLine.getColumn( offset + 4 ) ) ) ; if ( reg2 < 0 ) { error( line, "Register expected" ) ; return FALSE ; } code = (reg << 8) | (reg2 << 4) ; switch( instr ) { case STORE : code |= instrSTORE_SX_SY ; break ; case OUTPUT: code |= instrOUTPUT_SX_SY ; break ; case INPUT : code |= instrINPUT_SX_SY ; break ; case FETCH : code |= instrFETCH_SX_SY ; break ; default: error( line, "'STORE', 'OUTPUT', 'INPUT' or 'FETCH' expected" ) ; return FALSE ; } maxColumn = 6 ; } else { unsigned int value ; if ( sscanf( translateConstant( s3 ).c_str(), "%X", &value ) != 1 ) { sprintf( err_desc, "Value or (regname) expected, but \"%s\" found.", s3.c_str() ) ; error( line, err_desc ) ; return FALSE ; } code = (reg << 8) | value ; switch( instr ) { case STORE : code |= instrSTORE_SX_SS ; break ; case OUTPUT: code |= instrOUTPUT_SX_PP ; break ; case INPUT : code |= instrINPUT_SX_PP ; break ; case FETCH : code |= instrFETCH_SX_SS ; break ; default: error( line, "'STORE', 'OUTPUT', 'INPUT' or 'FETCH' expected" ) ; return FALSE ; } maxColumn = 4 ; } break ; default: // Instruction register comma register or value int reg2 = getRegister( translateRegister( s3 ) ) ; maxColumn = 4 ; if ( reg2 < 0 ) { unsigned int value ; if ( sscanf( translateConstant( s3 ).c_str(), "%X", &value ) != 1 ) { sprintf( err_desc, "Value expected, but \"%s\" found.", s3.c_str() ) ; error( line, err_desc ) ; return FALSE ; } code = (reg << 8) | value ; switch( instr ) { case ADD : code |= instrADD_SX_KK ; break ; case ADDCY : code |= instrADDCY_SX_KK ; break ; case AND : code |= instrAND_SX_KK ; break ; case COMPARE : code |= instrCOMPARE_SX_KK ; break ; case LOAD : code |= instrLOAD_SX_KK ; break ; case OR : code |= instrOR_SX_KK ; break ; case SUB : code |= instrSUB_SX_KK ; break ; case SUBCY : code |= instrSUBCY_SX_KK ; break ; case TEST : code |= instrTEST_SX_KK ; break ; case XOR : code |= instrXOR_SX_KK ; break ; default : error( line, "Unknown instruction" ) ; return FALSE ; } } else { code = ( reg << 8 ) | ( reg2 << 4 ) ; switch( instr ) { case ADD : code |= instrADD_SX_SY ; break ; case ADDCY : code |= instrADDCY_SX_SY ; break ; case AND : code |= instrAND_SX_SY ; break ; case COMPARE: code |= instrCOMPARE_SX_SY ; break ; case LOAD : code |= instrLOAD_SX_SY ; break ; case OR : code |= instrOR_SX_SY ; break ; case SUB : code |= instrSUB_SX_SY ; break ; case SUBCY : code |= instrSUBCY_SX_SY ; break ; case TEST : code |= instrTEST_SX_SY ; break ; case XOR : code |= instrXOR_SX_SY ; break ; default : error( line, "Unknown instruction" ) ; return FALSE ; } } } } } // Check if there's is rubbish at the end of the line if ( sourceLine.isColumn( maxColumn + offset ) ) { sprintf( err_desc, "'%s' found at end of instruction", sourceLine.getColumn( maxColumn + offset ).c_str() ) ; error( line, err_desc ) ; return FALSE ; } // Finally m_code->setInstruction( address, code, line ) ; return TRUE ; } bool CAssembler::exportVHDL( string templateFile, string outputDir, string entityName ) { int addr, i, j, k, l, n ; unsigned char INIT[ 32 ][ 64 ] ; /* 32 * 64 = 2048 bytes */ unsigned char INITP[ 32 ][ 8 ] ; /* 32 * 8 = 256 bytes (Parity Table)*/ unsigned int d ; /* 2304 Bytes Total = (18b * 1024 ) / 8 (1 instr = 18 bits )*/ CInstruction *instr ; for ( i = 0 ; i < 32 ; i++ ) for( j = 0 ; j < 32 ; j++ ) INIT[ i ][ j ] = 0 ; for ( i = 0 ; i < 32 ; i++ ) for ( j = 0 ; j < 8 ; j++ ) INITP[ i ][ j ] = 0 ; /* Build up BRAM in memory */ for ( addr = i = j = k = l = 0, n = 0 ; addr < 1024 ; addr++ ) { instr = m_code->getInstruction( addr ) ; if ( instr == NULL ) d = 0 ; else d = instr->getHexCode() ; INIT[ i++ ][ j ] = d ; // instruction( 15 downto 0 ) INIT[ i++ ][ j ] = d >> 8; INITP[ k ][ l ] |= ( ( d >> 16 ) & 0x3 ) << n ; // instruction( 17 downto 16 ) ; n += 2 ; if ( n >= 8 ) { n = 0 ; k++ ; if ( k >= 32 ) { l++ ; k = 0 ; } } if ( i >= 32 ) { i = 0 ; j++ ; } } FILE * infile = fopen( templateFile.c_str(), "r" ) ; if ( infile == NULL ) { error( NO_LINE_NR, string( "Unable to open VHDL template file '" + templateFile + "'" ).c_str() ) ; return FALSE ; } string exportFile = outputDir + "/" + entityName + ".vhd" ; FILE * outfile = fopen( exportFile.c_str(), "w" ) ; if ( outfile == NULL ) { error( NO_LINE_NR , string( "Unable to open VHDL template file '%s'" + exportFile + ".vhd").c_str() ) ; return FALSE ; } bool store = false, copy = false; char varname[ 64 ] ; int p = 0 ; int line, c ; while ( ( c = fgetc( infile ) ) != EOF ) { if ( store && p < 64 ) varname[ p++ ] = c ; if ( c == '{' ) { store = true ; p = 0 ; } if ( !store && copy ) fputc( c, outfile ) ; if ( c == '}' ) { store = false ; if ( p > 0 ) varname[ p - 1 ] = '\0' ; else varname[ 0 ] = '\0' ; if ( strncmp( "INIT_", varname, 5 ) == 0 ) { sscanf( varname, "INIT_%02X", &line ) ; if ( line >= 0 && line < 64 ) { for( j = 31 ; j >= 0 ; j-- ) fprintf( outfile, "%02X", INIT[ j ][ line ] ) ; } } else if ( strncmp( "INITP_", varname, 6 ) == 0 ) { sscanf( varname, "INITP_%02X", &line ) ; if ( line >= 0 && line < 8 ) for( j = 31 ; j >= 0 ; j-- ) fprintf( outfile, "%02X", INITP[ j ][ line ] ) ; } else if ( strcmp( "name", varname ) == 0 ) { fprintf( outfile, "%s", entityName.c_str() ) ; } else if ( strcmp( "begin template", varname ) == 0 ) { copy = true ; } } } fclose( infile ) ; fclose( outfile ) ; return TRUE ; } bool CAssembler::exportHEX( string filename, bool mem ) { FILE * file = fopen( filename.c_str(), "w" ) ; if ( file == NULL ) { error( NO_LINE_NR , string( "Unable to write to file '" + filename + "'").c_str() ) ; return FALSE ; } CInstruction * instr ; uint32_t d ; uint32_t addr ; /* A mem file requires the @ sign */ if ( mem ) fprintf( file, "@0\r\n" ) ; for ( addr = 0 ; addr < 1024 ; addr++ ) { instr = m_code->getInstruction( addr ) ; if ( instr == NULL ) d = 0 ; else d = instr->getHexCode() ; fprintf( file, "%05X\r\n", d ) ; } fclose( file ) ; return TRUE ; } bool CAssembler::createOpcodes() { list::iterator it ; int columnOffset ; bool ret = TRUE ; for ( it = m_source.begin() ; it != m_source.end() ; it++ ) { if ( (*it)->m_type == CSourceLine::stNamereg || (*it)->m_type == CSourceLine::stConstant || (*it)->m_type == CSourceLine::stAddress ) continue ; if ( (*it)->m_type == CSourceLine::stLabel ) columnOffset = 2 ; else columnOffset = 0 ; if ( !(*it)->isColumn( columnOffset + 0 ) ) // just a label continue ; int instr = getInstruction( (*it)->getColumn( columnOffset + 0 ) ) ; if ( instr < 0 ) { error( (*it)->m_lineNr, "Unknown instruction" ) ; ret = FALSE ; } if ( addInstruction( (instrNumber) instr, **it, columnOffset ) == FALSE ) ret = FALSE ; } return ret ; } bool CAssembler::assemble( ) { bool r1, r2 ; if ( loadFile() == FALSE ) return FALSE ; r1 = buildSymbolTable() ; // Even continue if symbol table failed.. r2 = createOpcodes() ; // .. this way we get the most errors/warnings in 1 compile cycle. return ( r1 && r2 ) ; } char * CAssembler::getWord( char *s, char *word ) { char *start, *end ; *word = '\0' ; while ( *s == ' ' || *s == '\t' ) // skip whitespaces s++ ; start = s ; if ( *start == '\0' || *start == '\r' || *start == '\n' || *start == ';' ) // end of line return NULL ; while ( *s != ' ' && *s != '\t' && *s != '\0' && *s != '\r' && *s != '\n' && *s != ';' && *s != ',' && *s != ':' && *s != '(' && *s != ')' ) s++ ; end = s ; if ( start != end ) { while ( start < end ) *word++ = *start++ ; *word = '\0' ; return end ; } else if ( *s == ',' || *s == ':' || *s == '(' || *s == ')' ) { *word++ = *s ; *word = '\0' ; return end + 1 ; } else return NULL ; } CSourceLine * CAssembler::formatLine( int lineNr, char *s ) { CSourceLine *sourceLine = new CSourceLine( lineNr ) ; char *next, word[ 256 ] ; next = getWord( s, word ) ; if ( word[ 0 ] == '\0' ) { // empty line delete sourceLine ; return NULL ; } do { sourceLine->addColumn( word ) ; next = getWord( next, word ) ; if ( word[ 0 ] == '\0' ) break ; } while ( next != NULL ) ; return sourceLine ; } bool CAssembler::loadFile() { FILE *f ; f = fopen( m_filename.c_str(), "r" ) ; if ( f == NULL ) { string str = "Unable to load file '" + m_filename + "'"; error( NO_LINE_NR, str.c_str() ) ; // No linenumber information return FALSE ; } char buf[ 256 ] ; int linenr = 0 ; while( fgets( buf, sizeof( buf ), f ) ) { CSourceLine *sourceLine = formatLine( linenr++, buf ) ; if ( sourceLine != NULL ) m_source.push_back( sourceLine ) ; } list::iterator it ; for ( it = m_source.begin() ; it != m_source.end() ; it++ ) { cout << "(" << (*it)->m_lineNr << ")" ; int j = 0 ; while ( (*it)->isColumn( j ) ) cout << "[" << (*it)->getColumn( j++ ) << "]"; cout << "\r\n" ; } cout << "File " << m_filename << " succesfully loaded\r\n" ; return TRUE ; }