Mano pdp8 AssemblerMano.cc

From Minor Miracle Software
Jump to: navigation, search


AssemblerMano.cc -- The assembler interpreter.

// Mano Assembler

#ifndef ASSEMBLERMANO_CC
#define ASSEMBLERMANO_CC

#ifndef MANO_H
  #include "mano.h"
#endif

#ifndef INSTRUCTIONWORD_CC
     #include "InstructionWord.cc"
#endif

#ifndef SYMBOLTABLE_CC
  #include "SymbolTable.cc"
#endif

#ifndef PROGRAMLINE_CC
  #include "ProgramLine.cc"
#endif

#ifndef QUEUE_CC
  #include "queue.cc"
#endif

#ifndef CHARSTRING_CC
  #include "charString.cc"
#endif


class AssemblerMano {

     SymbolTable Mano_SymbolTable;
     queue<ProgramLine> queue_Program;
     queue<ProgramLine> queue_SecondPass;
     queue<InstructionWord> queue_AssemblyCode;

     int i_mHLT; // machine is halted
     int i_mORG; // there is only one ORG

     short LocationCounter_short_m; // LC counter

     void FirstPass( queue<ProgramLine> queue_Program )
     {
          LocationCounter_short_m = 0; // LC <- 0
          int i_test = 0;
          while( !queue_Program.isempty() ) // scan next line of code
          {
          	i_test++;
          	  if( DEBUG ) cout << " AssemblerMano: FirstPass 1" << endl;
              ProgramLine PL;
              queue_Program.dequeue( PL ) ;

	          // Check for Label
	           if( IsLabel( PL ) )
	           {
	                Mano_SymbolTable.LoadSymbol( PL.GetLabel(),
	               								 PL.GetLineNumber(), 
	                 							 LocationCounter_short_m );
	                IncrementLC();
	                queue_SecondPass.enqueue( PL );
	                continue;
	           }

				if( IsORG( PL ) == VALID_ORG_INSTRUCTION )
				{
	                queue_SecondPass.enqueue( PL );
					continue;
				}

				if( IsEND( PL ) == VALID_END_INSTRUCTION )
				{
	                queue_SecondPass.enqueue( PL );
					continue;
				}

                queue_SecondPass.enqueue( PL );
                IncrementLC();
         } // end while

          if( DEBUG ) cout << " AssemblerMano: FirstPass  " << i_test << " line processed." << endl;

     } // FirstPass


// Second Pass Methods
     void SecondPass() // do this
     {
     	 // Set LC to zero.
          SetLC( 0 );

			// Scan the next line of code.
          while( !queue_SecondPass.isempty() )
         {
               if( DEBUG ) cout << " AssemblerMano: SecondPass 1" << endl;
            ProgramLine PL;
            queue_SecondPass.dequeue( PL );

			// Check for Pseudo instruction
            if( IsPseudoInstruction( PL ) )
            {
            	// If true decode and scan next line.
            	if( PseudoInstructionTable( PL ) == VALID_END_INSTRUCTION )
            		break;

                LoadAssemblyCodeQueue( PL );
	            IncrementLC();
            	continue;
            }
            	
            if( IsMemoryReferenceInstruction( PL ) )
            {
				MemoryReferenceInstructionTable( PL );
                LoadAssemblyCodeQueue( PL );
	            IncrementLC();
                continue;
            }

            if( IsNONMemoryReferenceInstruction( PL ) )
            {
				NonMemoryReferenceInstructionTable( PL );
	            IncrementLC();
                continue;
            }

            // If the SecondPass gets to here there is an error.
            // Increment LC.
            // Scan the next code line.
            cout << " AssemblerMano: SecondPass: ERROR Line "  << LocationCounter_short_m << endl;
            IncrementLC();
         } // while LoadAssemblyCodeQueue
     } // SecondPass


	void LoadAssemblyCodeQueue( ProgramLine &PL )
	{
		InstructionWord InstructionWord_Temp;
		// set LocationCounter
        InstructionWord_Temp.setlocation( LocationCounter_short_m );
        // set Instruction Word
        InstructionWord_Temp.setdata( PL.GetInstWord() );
		// load address at the right memory address
        queue_AssemblyCode.enqueue( InstructionWord_Temp );
    }

	int IsAddress( int i_Number )
	{
	if( DEBUG ) cout << " AssemblerMano: IsAddress 0" << endl;
	
	     if( i_Number > MAX_ADDRESS )
	     {
	if( DEBUG ) cout << " AssemblerMano: IsAddress 1" << endl;
	        return 0;
	     }
	
	     if( i_Number < MIN_ADDRESS )
	     {
	if( DEBUG ) cout << " AssemblerMano: IsAddress 2" << endl;
	         return 0;
	     }
	if( DEBUG ) cout << " AssemblerMano: IsAddress 3" << endl;
	
	     return 1;
	} // IsAddress( unsigned short sl_Number )


	int IsNumber( int ul_Number )
	{
	if( DEBUG ) cout << " AssemblerMano: IsNumber 0" << endl;
	
	     if( ul_Number > MAX_PLUS )
	         return  NUMBER_OVER_MAX;
	
	     if( ul_Number < MAX_MINUS )
	         return NUMBER_UNDER_MINUS_MIN;
	
	     return VALID_NUMBER;
	} // IsNumber( int ul_Number )

// Label Methods

	int IsLabel( ProgramLine &PL )
	{
		// The first character must be a letter.
		// The label can be one, two or three
		// characters long.
		charString charString_Temp = PL.GetLabel();
		
		if( charString_Temp.IsAlphaNum( 1 ) &&
		    ( charString_Temp.length() <= LABEL_LENGTH ) )
		    return true;
		else
			return false;
	} // int IsLabel( ProgramLine &PL )


// Memory Methods
	int IsMemoryReferenceInstruction( ProgramLine &PL )
	{
		// Valid Memory Reference Instructions from
		// chapter 5, table 5-4 on page 145.
	    if( PL.GetInstruction() == "AND" )
	    	return true;
	
	    if( PL.GetInstruction() == "ADD" )
	    	return true;
	
	    if( PL.GetInstruction() == "LDA" )
	    	return true;
	
	    if( PL.GetInstruction() == "STA" )
	    	return true;
	
	    if( PL.GetInstruction() == "BUN" )
	    	return true;
	
	    if( PL.GetInstruction() == "BSA" )
	    	return true;
	
	    if( PL.GetInstruction() == "ISZ" )
	    	return true;
	
		return false;
	} // IsMemoryReferenceInstruction( ProgramLine &PL )


	int IsNONMemoryReferenceInstruction( ProgramLine &PL )
	{
		// Register Reference Instructions
	    if( PL.GetInstruction() == "CIL" )
	        return 1;
	
	    if( PL.GetInstruction() == "CIR" )
	        return 1;
	
	    if( PL.GetInstruction() == "CLA" )
	        return 1;
	
	    if( PL.GetInstruction() == "CLE" )
	        return 1;
	
	    if( PL.GetInstruction() == "CMA" )
	        return 1;
	
	    if( PL.GetInstruction() == "CME" )
	        return 1;
	
	    if( PL.GetInstruction() == "HLT" )
	        return 1;
	
	    if( PL.GetInstruction() == "INC" )
	         return 1;
	
	    if( PL.GetInstruction() == "SNA" )
	        return 1;
	
	    if( PL.GetInstruction() == "SPA" )
	        return 1;
	
	    if( PL.GetInstruction() == "SZA" )
	        return 1;
	
	    if( PL.GetInstruction() == "SZE" )
	        return 1;
	
	// Input-Output Instructions
	    if( PL.GetInstruction() == "INP" )
	        return 1;
	
	    if( PL.GetInstruction() == "OUT" )
	        return 1;
	
	    if( PL.GetInstruction() == "SKI" )
	        return 1;
	
	    if( PL.GetInstruction() == "SKO" )
	        return 1;
	
	    if( PL.GetInstruction() == "IOF" )
	        return 1;
	
	    if( PL.GetInstruction() == "ION" )
	        return 1;

		return 0;		
	} // IsNONMemoryReferenceInstruction( ProgramLine &PL )


	void MemoryReferenceInstructionTable( ProgramLine &PL )
	{
		if( DEBUG ) cout << " AssemblerMano: Memory_Reference_Instruction_Table 0" << endl;
	
	    short us_data = 0;
	    short s_Idicator = 0;
		// determine Indirection
	     if( PL.GetIndirection() == "I" )
	     {
	         us_data += def_I;
	     } // add indirection
	
	     s_Idicator = Mano_SymbolTable.Search( PL.GetLabel() );
	     if( s_Idicator > -1 )
	     {
	         us_data += s_Idicator;
	     }
	
	     if( PL.GetInstruction() == "AND"  )
	     {
	         us_data += def_AND;
	         PL.SetInstWord( us_data );
	     }
	
	     if( PL.GetInstruction() == "ADD" )
	     {
	         us_data += def_ADD;
	         PL.SetInstWord( us_data );
	     }
	
	     if( PL.GetInstruction() == "LDA" )
	     {
	         us_data += def_LDA;
	         PL.SetInstWord( us_data );
	     }
	
	     if( PL.GetInstruction() == "STA" )
	     {
	         us_data += def_STA;
	         PL.SetInstWord( us_data );
	     }
	
	     if( PL.GetInstruction() == "BUN" )
	     {
	         us_data += def_BUN;
	         PL.SetInstWord( us_data );
	     }
	
	     if( PL.GetInstruction() == "BSA" )
	     {
	         us_data += def_BSA;
	         PL.SetInstWord( us_data );
	     }
	
	     if( PL.GetInstruction() == "ISZ" )
	     {
	         us_data += def_ISZ;
	         PL.SetInstWord( us_data );
	     }
	} // void Memory_Reference_Instruction_Table


	void NonMemoryReferenceInstructionTable( ProgramLine PL )
	{
	     if( DEBUG ) cout << " AssemblerMano: Non_Memory_Reference_Instruction_Table 0" << endl;
	
	// Register Reference Instructions
	    if( PL.GetInstruction() == "CIL" )
	        PL.SetInstWord( def_CIL );
	
	    if( PL.GetInstruction() == "CIR" )
	        PL.SetInstWord( def_CIR );
	
	    if( PL.GetInstruction() == "CLA" )
	        PL.SetInstWord( def_CLA );
	
	    if( PL.GetInstruction() == "CLE" )
	        PL.SetInstWord( def_CLE );
	
	    if( PL.GetInstruction() == "CMA" )
	        PL.SetInstWord( def_CMA );
	
	    if( PL.GetInstruction() == "CME" )
	        PL.SetInstWord( def_CME );
	
	    if( PL.GetInstruction() == "HLT" )
	    {
	        i_mHLT = VALID_HLT_INSTRUCTION;
	        PL.SetInstWord( def_HLT );
	    }
	
	    if( PL.GetInstruction() == "INC" )
	         PL.SetInstWord( def_INC );
	
	    if( PL.GetInstruction() == "SNA" )
	        PL.SetInstWord( def_SNA );
	
	    if( PL.GetInstruction() == "SPA" )
	        PL.SetInstWord( def_SPA );
	
	    if( PL.GetInstruction() == "SZA" )
	        PL.SetInstWord( def_SZA );
	
	    if( PL.GetInstruction() == "SZE" )
	        PL.SetInstWord( def_SZE );
	
	// Input-Output Instructions
	    if( PL.GetInstruction() == "INP" )
	        PL.SetInstWord( def_INP );
	
	    if( PL.GetInstruction() == "OUT" )
	        PL.SetInstWord( def_OUT );
	
	    if( PL.GetInstruction() == "SKI" )
	        PL.SetInstWord( def_SKI );
	
	    if( PL.GetInstruction() == "SKO" )
	        PL.SetInstWord( def_SKO );
	
	    if( PL.GetInstruction() == "IOF" )
	        PL.SetInstWord( def_IOF );
	
	    if( PL.GetInstruction() == "ION" )
	        PL.SetInstWord( def_ION );
	
	} // NonMemoryReferenceInstructionTable


// Pseudo Instruction Methods
	int IsPseudoInstruction( ProgramLine PL )
	{
		// Valid Pseudo Instructions are.
        //   ORG  Origin Address
        //   END  End program
        //   HEX  Hexadecimal Number
        //   DEC  Decimal Number
        //   OCT  Octal Number
		if(  PL.GetInstruction() == "ORG" )
			return true;

		if(  PL.GetInstruction() == "HEX" )
			return true;

		if(  PL.GetInstruction() == "DEC" )
			return true;

		if(  PL.GetInstruction() == "OCT" )
			return true;

		if(  PL.GetInstruction() == "END" )	
			return true;
			
		return false;
	} // IsPseudoInstruction( ProgramLine PL )


     int PseudoInstructionTable( ProgramLine &PL )
     {
          // charString charString_Instruction, charString charString_Command, short s_Location
          // Decode Pseudo Instruction
          // Carry out appropriate action
          // Instruction  Action
          //   ORG        Decode number and set LocationCounter_short_m to it.
          //   END        set i_mEND to VALID_END_INSTRUCTION
          //   HEX        Decode hexadcimal number and place in location pointed to by LocationCounter_short_m
          //   DEC        Decode decimal number and place in location pointed to by LocationCounter_short_m
          //   OCT        Decode octal number and place in location pointed to by LocationCounter_short_m
          // Default return value is NON_PSEUDO_INSTRUCTION
          // Success return value is VALID_PSEUDO_INSTRCTION
          if( DEBUG ) cout << " AssemblerMano: PseudoInstructionTable 0" << endl;

          int i_ReturnValue = 0;

          if( IsORG( PL ) == VALID_ORG_INSTRUCTION)
               return VALID_ORG_INSTRUCTION;

          if( IsEND( PL ) == VALID_END_INSTRUCTION )
               return VALID_END_INSTRUCTION;

          // Check for DEC, HEX
          if( PL.GetInstruction() == "HEX" )
          {
                DecodeNumber( PL, 16 );
              return VALID_PSEUDO_INSTRUCTION;
         }

         if( PL.GetInstruction() == "DEC" )
         {
                DecodeNumber( PL, 10 );
              return VALID_PSEUDO_INSTRUCTION;
         }

          if( PL.GetInstruction() == "OCT" )
          {
                DecodeNumber( PL, 8 );
             return VALID_PSEUDO_INSTRUCTION;
         }

          return NON_PSEUDO_INSTRUCTION;
     } // PseudoInstructionTable



     int IsEND( ProgramLine &PL )
     {
          // Check for END
          if(  PL.GetInstruction() == "END" )
          {
               PL.SetInstWord( VALID_END_INSTRUCTION );
              return VALID_END_INSTRUCTION;
          }
          return INVALID_END_INSTRUCTION;
     } // IsEND( ProgramLine PL )

     int IsORG( ProgramLine &PL )
     {
          // Check for ORG
          if( DEBUG ) cout << " AssemblerMano: IsORG 0" << endl;

         if( PL.GetInstruction() == "ORG" )
         {
         	// ORG is always a hexadecimal number
               DecodeNumber( PL, 16 );
             int i_ReturnValue = PL.GetInstWord();

               if( IsAddress( i_ReturnValue ) )
               {
	               SetLC( i_ReturnValue );
                   return VALID_ORG_INSTRUCTION;
               }
               else
                   return INVALID_ORG_INSTRUCTION;
          }
          return INVALID_ORG_INSTRUCTION;
     } // IsORG( ProgramLine PL )


     void DecodeNumber( ProgramLine &PL, int Base_int )
     {
     // Convert string to valid number and return
     // Assume number without a pseudoinstruction is hexadecimal.
     // Assume strings are trimmed front and back
         long l_ReturnValue = 0;
         l_ReturnValue = PL.GetOperand().toLong( Base_int );
         PL.SetInstWord( l_ReturnValue );
     } // DecodeNumber

// LocationCounter Utilities
	void SetLC( short s )
	{
		LocationCounter_short_m = s;
	}

	void IncrementLC()
	{
		LocationCounter_short_m++;
	}

public:

     AssemblerMano()
     {
          LocationCounter_short_m = 0;
          i_mHLT = 0;
          i_mORG = 0;
     }

     ~AssemblerMano(){}

     SymbolTable GetSymbolTable()
     {
     	return Mano_SymbolTable;
     }

     queue<InstructionWord> GetAssemblyCode()
     {
     	return queue_AssemblyCode;
     }


     // Error check and assemble the program
     short AssembleProgram( queue<ProgramLine> queue_Program )
     {
          if( DEBUG ) cout << " AssemblerMano: AssembleProgram 0" << endl;

          // check for more than MAX_ADDRESS
         if( !IsAddress( queue_Program.census() ) )
         {
               if( DEBUG ) cout << " AssemblerMano: Assembler 1" << endl;
             return 0;
          }

          // First Pass
          if( DEBUG ) cout << " AssemblerMano: Assembler 2" << endl;

          FirstPass( queue_Program );

          // Second Pass
          if( DEBUG ) cout << " AssemblerMano: Assembler 3" << endl;
          SecondPass();
          if( DEBUG ) cout << " AssemblerMano: Assembler 4" << endl;

        return 1;
     } // Assembler( queue)
};
#endif // AssemblerMano_CC


Internal Links

Parent Article: Mano PDP-8 Simulation Project Source Code