static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) /* Set the Use and Chg in E */ { const ZPInfo* Info; /* If this is a subroutine call, or a jump to an external function, ** lookup the information about this function and use it. The jump itself ** does not change any registers, so we don't need to use the data from D. */ if ((E->Info & (OF_UBRA | OF_CALL)) != 0 && E->JumpTo == 0) { /* A subroutine call or jump to external symbol (function exit) */ GetFuncInfo (E->Arg, &E->Use, &E->Chg); } else { /* Some other instruction. Use the values from the opcode description ** plus addressing mode info. */ E->Use = D->Use | GetAMUseInfo (E->AM); E->Chg = D->Chg; /* Check for special zero page registers used */ switch (E->AM) { case AM65_ACC: if (E->OPC == OP65_ASL || E->OPC == OP65_DEC || E->OPC == OP65_INC || E->OPC == OP65_LSR || E->OPC == OP65_ROL || E->OPC == OP65_ROR) { /* A is changed by these insns */ E->Chg |= REG_A; } break; case AM65_ZP: case AM65_ABS: /* Be conservative: */ case AM65_ZPX: case AM65_ABSX: case AM65_ABSY: Info = GetZPInfo (E->Arg); if (Info && Info->ByteUse != REG_NONE) { if (E->OPC == OP65_ASL || E->OPC == OP65_DEC || E->OPC == OP65_INC || E->OPC == OP65_LSR || E->OPC == OP65_ROL || E->OPC == OP65_ROR || E->OPC == OP65_TRB || E->OPC == OP65_TSB) { /* The zp loc is both, input and output */ E->Chg |= Info->ByteUse; E->Use |= Info->ByteUse; } else if ((E->Info & OF_STORE) != 0) { /* Just output */ E->Chg |= Info->ByteUse; } else { /* Input only */ E->Use |= Info->ByteUse; } } break; case AM65_ZPX_IND: case AM65_ZP_INDY: case AM65_ZP_IND: Info = GetZPInfo (E->Arg); if (Info && Info->ByteUse != REG_NONE) { /* These addressing modes will never change the zp loc */ E->Use |= Info->WordUse; } break; default: /* Keep gcc silent */ break; } } }
static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) /* Parse an instruction nnd generate a code entry from it. If the line contains * errors, output an error message and return NULL. * For simplicity, we don't accept the broad range of input a "real" assembler * does. The instruction and the argument are expected to be separated by * white space, for example. */ { char Mnemo[IDENTSIZE+10]; const OPCDesc* OPC; am_t AM = 0; /* Initialize to keep gcc silent */ char Arg[IDENTSIZE+10]; char Reg; CodeEntry* E; CodeLabel* Label; /* Read the first token and skip white space after it */ L = SkipSpace (ReadToken (L, " \t:", Mnemo, sizeof (Mnemo))); /* Check if we have a label */ if (*L == ':') { /* Skip the colon and following white space */ L = SkipSpace (L+1); /* Add the label */ CS_AddLabel (S, Mnemo); /* If we have reached end of line, bail out, otherwise a mnemonic * may follow. */ if (*L == '\0') { return 0; } L = SkipSpace (ReadToken (L, " \t", Mnemo, sizeof (Mnemo))); } /* Try to find the opcode description for the mnemonic */ OPC = FindOP65 (Mnemo); /* If we didn't find the opcode, print an error and bail out */ if (OPC == 0) { Error ("ASM code error: %s is not a valid mnemonic", Mnemo); return 0; } /* Get the addressing mode */ Arg[0] = '\0'; switch (*L) { case '\0': /* Implicit or accu */ if (OPC->Info & OF_NOIMP) { AM = AM65_ACC; } else { AM = AM65_IMP; } break; case '#': /* Immidiate */ StrCopy (Arg, sizeof (Arg), L+1); AM = AM65_IMM; break; case '(': /* Indirect */ L = ReadToken (L+1, ",)", Arg, sizeof (Arg)); /* Check for errors */ if (*L == '\0') { Error ("ASM code error: syntax error"); return 0; } /* Check the different indirect modes */ if (*L == ',') { /* Expect zp x indirect */ L = SkipSpace (L+1); if (toupper (*L) != 'X') { Error ("ASM code error: `X' expected"); return 0; } L = SkipSpace (L+1); if (*L != ')') { Error ("ASM code error: `)' expected"); return 0; } L = SkipSpace (L+1); if (*L != '\0') { Error ("ASM code error: syntax error"); return 0; } AM = AM65_ZPX_IND; } else if (*L == ')') { /* zp indirect or zp indirect, y */ L = SkipSpace (L+1); if (*L == ',') { L = SkipSpace (L+1); if (toupper (*L) != 'Y') { Error ("ASM code error: `Y' expected"); return 0; } L = SkipSpace (L+1); if (*L != '\0') { Error ("ASM code error: syntax error"); return 0; } AM = AM65_ZP_INDY; } else if (*L == '\0') { AM = AM65_ZP_IND; } else { Error ("ASM code error: syntax error"); return 0; } } break; case 'a': case 'A': /* Accumulator? */ if (L[1] == '\0') { AM = AM65_ACC; break; } /* FALLTHROUGH */ default: /* Absolute, maybe indexed */ L = ReadToken (L, ",", Arg, sizeof (Arg)); if (*L == '\0') { /* Absolute, zeropage or branch */ if ((OPC->Info & OF_BRA) != 0) { /* Branch */ AM = AM65_BRA; } else if (GetZPInfo(Arg) != 0) { AM = AM65_ZP; } else { /* Check for subroutine call to local label */ if ((OPC->Info & OF_CALL) && IsLocalLabelName (Arg)) { Error ("ASM code error: " "Cannot use local label `%s' in subroutine call", Arg); } AM = AM65_ABS; } } else if (*L == ',') { /* Indexed */ L = SkipSpace (L+1); if (*L == '\0') { Error ("ASM code error: syntax error"); return 0; } else { Reg = toupper (*L); L = SkipSpace (L+1); if (Reg == 'X') { if (GetZPInfo(Arg) != 0) { AM = AM65_ZPX; } else { AM = AM65_ABSX; } } else if (Reg == 'Y') { AM = AM65_ABSY; } else { Error ("ASM code error: syntax error"); return 0; } if (*L != '\0') { Error ("ASM code error: syntax error"); return 0; } } } break; } /* If the instruction is a branch, check for the label and generate it * if it does not exist. This may lead to unused labels (if the label * is actually an external one) which are removed by the CS_MergeLabels * function later. */ Label = 0; if (AM == AM65_BRA) { /* Generate the hash over the label, then search for the label */ unsigned Hash = HashStr (Arg) % CS_LABEL_HASH_SIZE; Label = CS_FindLabel (S, Arg, Hash); /* If we don't have the label, it's a forward ref - create it */ if (Label == 0) { /* Generate a new label */ Label = CS_NewCodeLabel (S, Arg, Hash); } } /* We do now have the addressing mode in AM. Allocate a new CodeEntry * structure and initialize it. */ E = NewCodeEntry (OPC->OPC, AM, Arg, Label, LI); /* Return the new code entry */ return E; }