// Copyright (C) 1999-2003 Core Technologies. // // This file is part of tpasm. // // tpasm is free software; you can redistribute it and/or modify // it under the terms of the tpasm LICENSE AGREEMENT. // // tpasm 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 // tpasm LICENSE AGREEMENT for more details. // // You should have received a copy of the tpasm LICENSE AGREEMENT // along with tpasm; see the file "LICENSE.TXT". // Generate code for Rockwell 6502 (with or without 65C02 enhancements) #include "include.h" static SYMTABLE *pseudoOpcodeSymbols, *opcodeSymbols, *opcode65C02Symbols; static PROCESSOR *currentProcessor; // enumerated addressing modes #define OT_IMPLIED 0 // no operands #define OT_IMMEDIATE 1 // #xx #define OT_ZEROPAGE 2 // xx #define OT_ZEROPAGE_OFF_X 3 // xx,X #define OT_ZEROPAGE_OFF_Y 4 // xx,Y #define OT_EXTENDED 5 // xxxx #define OT_EXTENDED_OFF_X 6 // xxxx,X #define OT_EXTENDED_OFF_Y 7 // xxxx,Y #define OT_INDIRECT_OFF_X 8 // (xx,X) #define OT_INDIRECT_OFF_Y 9 // (xx),Y #define OT_INDIRECT_WORD 10 // (xxxx) #define OT_RELATIVE 11 // one byte relative offset #define OT_IMPLIED_2 12 // two-byte implied opcode (second byte is ignored) #define OT_NUM OT_IMPLIED_2+1 // number of addressing modes // masks for the various addressing modes #define M_IMPLIED (1<typeMask&M_IMMEDIATE) { CheckByteRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_IMMEDIATE],listingRecord)) { fail=!GenerateByte(value,listingRecord); } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleZeroPage(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // deal with zero page mode output only { bool fail; fail=false; if(theOpcode->typeMask&M_ZEROPAGE) { CheckUnsignedByteRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_ZEROPAGE],listingRecord)) { fail=!GenerateByte(value,listingRecord); } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleExtended(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // deal with extended mode output only { bool fail; fail=false; if(theOpcode->typeMask&M_EXTENDED) { CheckUnsignedWordRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_EXTENDED],listingRecord)) { if(GenerateByte(value&0xFF,listingRecord)) { fail=!GenerateByte(value>>8,listingRecord); } else { fail=true; } } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleZeroPageOrExtended(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // a zero page or extended address has been parsed { bool fail; fail=false; if(theOpcode->typeMask&M_ZEROPAGE) { if(((value>=0)&&(value<256))||!(theOpcode->typeMask&M_EXTENDED)) { fail=!HandleZeroPage(theOpcode,value,unresolved,listingRecord); } else { fail=!HandleExtended(theOpcode,value,unresolved,listingRecord); } } else { fail=!HandleExtended(theOpcode,value,unresolved,listingRecord); } return(!fail); } static bool HandleRelative(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // a relative operand has been parsed { bool fail; int offset; fail=false; if(GenerateByte(theOpcode->baseOpcode[OT_RELATIVE],listingRecord)) { offset=0; if(!unresolved&¤tSegment) { offset=value-(currentSegment->currentPC+currentSegment->codeGenOffset)-1; Check8RelativeRange(offset,true,true); } fail=!GenerateByte(offset,listingRecord); } else { fail=true; } return(!fail); } static bool HandleValue(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // a value has been parsed, it might be relative, zero page, or extended { bool fail; fail=false; if(theOpcode->typeMask&M_RELATIVE) { fail=!HandleRelative(theOpcode,value,unresolved,listingRecord); } else { fail=!HandleZeroPageOrExtended(theOpcode,value,unresolved,listingRecord); } return(!fail); } static bool HandleZeroPageOffX(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // deal with zero page offset by X { bool fail; fail=false; if(theOpcode->typeMask&M_ZEROPAGE_OFF_X) { CheckUnsignedByteRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_ZEROPAGE_OFF_X],listingRecord)) { fail=!GenerateByte(value,listingRecord); } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleExtendedOffX(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // deal with extended mode offset by X { bool fail; fail=false; if(theOpcode->typeMask&M_EXTENDED_OFF_X) { CheckUnsignedWordRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_EXTENDED_OFF_X],listingRecord)) { if(GenerateByte(value&0xFF,listingRecord)) { fail=!GenerateByte(value>>8,listingRecord); } else { fail=true; } } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleValueOffX(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // possible extended or zeropage value offset by X { bool fail; fail=false; if(theOpcode->typeMask&M_ZEROPAGE_OFF_X) { if(((value>=0)&&(value<256))||!(theOpcode->typeMask&M_EXTENDED_OFF_X)) { fail=!HandleZeroPageOffX(theOpcode,value,unresolved,listingRecord); } else { fail=!HandleExtendedOffX(theOpcode,value,unresolved,listingRecord); } } else { fail=!HandleExtendedOffX(theOpcode,value,unresolved,listingRecord); } return(!fail); } static bool HandleZeroPageOffY(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // deal with zero page offset by Y { bool fail; fail=false; if(theOpcode->typeMask&M_ZEROPAGE_OFF_Y) { CheckUnsignedByteRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_ZEROPAGE_OFF_Y],listingRecord)) { fail=!GenerateByte(value,listingRecord); } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleExtendedOffY(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // deal with extended mode offset by Y { bool fail; fail=false; if(theOpcode->typeMask&M_EXTENDED_OFF_Y) { CheckUnsignedWordRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_EXTENDED_OFF_Y],listingRecord)) { if(GenerateByte(value&0xFF,listingRecord)) { fail=!GenerateByte(value>>8,listingRecord); } else { fail=true; } } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleValueOffY(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // possible extended or zero page value offset by Y { bool fail; fail=false; if(theOpcode->typeMask&M_ZEROPAGE_OFF_Y) { if(((value>=0)&&(value<256))||!(theOpcode->typeMask&M_EXTENDED_OFF_Y)) { fail=!HandleZeroPageOffY(theOpcode,value,unresolved,listingRecord); } else { fail=!HandleExtendedOffY(theOpcode,value,unresolved,listingRecord); } } else { fail=!HandleExtendedOffY(theOpcode,value,unresolved,listingRecord); } return(!fail); } static bool HandleIndirect(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // a indirect value has been parsed, it must be indirect word { bool fail; fail=false; if(theOpcode->typeMask&M_INDIRECT_WORD) { CheckUnsignedWordRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_INDIRECT_WORD],listingRecord)) { if(GenerateByte(value&0xFF,listingRecord)) { fail=!GenerateByte(value>>8,listingRecord); } else { fail=true; } } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleIndirectOffX(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // a indirect value offset by X has been parsed, it must be zero page { bool fail; fail=false; if(theOpcode->typeMask&M_INDIRECT_OFF_X) { CheckUnsignedByteRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_INDIRECT_OFF_X],listingRecord)) { fail=!GenerateByte(value,listingRecord); } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleIndirectOffY(OPCODE *theOpcode,int value,bool unresolved,LISTING_RECORD *listingRecord) // a indirect value offset by Y has been parsed, it must be zero page { bool fail; fail=false; if(theOpcode->typeMask&M_INDIRECT_OFF_Y) { CheckUnsignedByteRange(value,true,true); if(GenerateByte(theOpcode->baseOpcode[OT_INDIRECT_OFF_Y],listingRecord)) { fail=!GenerateByte(value,listingRecord); } else { fail=true; } } else { ReportBadOperands(); } return(!fail); } static bool HandleOpcode(void *theOpcode,char *theLine,int *lineIndex,LISTING_RECORD *listingRecord) // look at the type of opcode available, parse operands as allowed // return false only if there was a 'hard' error { OPCODE *actualOpcode; bool fail; int elementType; int value; bool unresolved; fail=false; actualOpcode=(OPCODE *)theOpcode; if(!ParseComment(theLine,lineIndex)) { if(ParseOperand(theLine,lineIndex,&elementType,&value,&unresolved)) { switch(elementType) { case POT_IMMEDIATE: return(HandleImmediate(actualOpcode,value,unresolved,listingRecord)); break; case POT_VALUE: return(HandleValue(actualOpcode,value,unresolved,listingRecord)); break; case POT_VALUE_OFF_X: return(HandleValueOffX(actualOpcode,value,unresolved,listingRecord)); break; case POT_VALUE_OFF_Y: return(HandleValueOffY(actualOpcode,value,unresolved,listingRecord)); break; case POT_INDIRECT: return(HandleIndirect(actualOpcode,value,unresolved,listingRecord)); break; case POT_INDIRECT_OFF_X: return(HandleIndirectOffX(actualOpcode,value,unresolved,listingRecord)); break; case POT_INDIRECT_OFF_Y: return(HandleIndirectOffY(actualOpcode,value,unresolved,listingRecord)); break; } } else { ReportBadOperands(); } } else { if(actualOpcode->typeMask&M_IMPLIED) { fail=!GenerateByte(actualOpcode->baseOpcode[OT_IMPLIED],listingRecord); } else if(actualOpcode->typeMask&M_IMPLIED_2) { fail=!GenerateByte(actualOpcode->baseOpcode[OT_IMPLIED_2],listingRecord); fail|=!GenerateByte(0x00,listingRecord); // two-byte opcode, second byte is ignored } else { ReportBadOperands(); } } return(!fail); } static void *MatchOpcode(char *theOpcode) // match opcodes for this processor, return NULL if none matched { void *result; result=NULL; if(currentProcessor->processorData) { result=STFindDataForName(*((SYMTABLE **)(currentProcessor->processorData)),theOpcode); // search enhanced opcodes first } if(!result) { result=STFindDataForName(opcodeSymbols,theOpcode); // fallback to here if nothing located } return(result); } static bool HandlePseudoOpcode(PSEUDO_OPCODE *theOpcode,char *theLine,int *lineIndex,bool haveLabel,bool isLocal,char *lineLabel,LISTING_RECORD *listingRecord) // look at the type of opcode available, parse operands as allowed // return false only if there was a 'hard' error { return(theOpcode->theFunction(theOpcode,theLine,lineIndex,haveLabel,isLocal,lineLabel,listingRecord)); } static PSEUDO_OPCODE *MatchPseudoOpcode(char *theOpcode) // match pseudo opcodes for this processor, return NULL if none matched { return((PSEUDO_OPCODE *)STFindDataForName(pseudoOpcodeSymbols,theOpcode)); } static bool SelectProcessor(PROCESSOR *theProcessor) // A processor in this family is being selected to assemble with { currentProcessor=theProcessor; return(true); } static void DeselectProcessor(PROCESSOR *theProcessor) // A processor in this family is being deselected { } static void UnInitFamily() // undo what InitFamily did { STDisposeSymbolTable(opcode65C02Symbols); STDisposeSymbolTable(opcodeSymbols); STDisposeSymbolTable(pseudoOpcodeSymbols); } static bool InitFamily() // initialize symbol tables { unsigned int i; bool fail; fail=false; currentProcessor=NULL; if((pseudoOpcodeSymbols=STNewSymbolTable())) { for(i=0;!fail&&(i