You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kpicosim/src/cassembler.cpp

755 lines
19 KiB

/* 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 <stdio.h>
#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", &reg ) != 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<CSourceLine*>::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<CConstant*>::iterator it0 ;
for ( it0 = m_constantTable.begin() ; it0 != m_constantTable.end() ; it0++ ) {
cout << (*it0)->name << " = " << (*it0)->value << "\r\n" ;
}
cout << "Namereg :\r\n" ;
list<CNamereg*>::iterator it1 ;
for ( it1 = m_registerTable.begin() ; it1 != m_registerTable.end() ; it1++ ) {
cout << (*it1)->reg << " = " << (*it1)->name << "\r\n" ;
}
cout << "labels :\r\n" ;
list<CLabel*>::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<CNamereg*>::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<CConstant*>::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<CLabel*>::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<CSourceLine*>::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<CSourceLine*>::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 ;
}