unsigned OptBNegAX1 (CodeSeg* S) /* On a call to bnegax, if X is zero, the result depends only on the value in * A, so change the call to a call to bnega. This will get further optimized * later if possible. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if this is a call to bnegax, and if X is known and zero */ if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) { CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI); CS_InsertEntry (S, X, I+1); CS_DelEntry (S, I); /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptCmp6 (CodeSeg* S) /* Search for calls to compare subroutines followed by a conditional branch * and replace them by cheaper versions, since the branch means that the * boolean value returned by these routines is not needed (we may also check * that explicitly, but for the current code generator it is always true). */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* N; cmp_t Cond; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_ZBRA) != 0 && !CE_HasLabel (N)) { /* The tos... functions will return a boolean value in a/x and * the Z flag says if this value is zero or not. We will call * a cheaper subroutine instead, one that does not return a * boolean value but only valid flags. Note: jeq jumps if * the condition is not met, jne jumps if the condition is met. * Invert the code if we jump on condition not met. */ if (GetBranchCond (N->OPC) == BC_EQ) { /* Jumps if condition false, invert condition */ Cond = CmpInvertTab [Cond]; } /* Replace the subroutine call. */ E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); CS_InsertEntry (S, E, I+1); CS_DelEntry (S, I); /* Replace the conditional branch */ ReplaceCmp (S, I+1, Cond); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptShift2(CodeSeg* S) /* A call to the asrax1 routines may get replaced by something simpler, if * X is not used later: * * cmp #$80 * ror a * */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { unsigned Shift; unsigned Count; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && (Shift = GetShift (E->Arg)) != SHIFT_NONE && SHIFT_TYPE (Shift) == SHIFT_TYPE_ASR && (Count = SHIFT_COUNT (Shift)) > 0 && Count * 100 <= S->CodeSizeFactor && !RegXUsed (S, I+1)) { CodeEntry* X; unsigned J = I+1; /* Generate the replacement sequence */ while (Count--) { /* cmp #$80 */ X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, E->LI); CS_InsertEntry (S, X, J++); /* ror a */ X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, J++); } /* Delete the call to asrax */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptBNegAX3 (CodeSeg* S) /* Search for the sequence: * * lda xx * ldx yy * jsr bnegax * jne/jeq ... * * and replace it by * * lda xx * ora xx+1 * jeq/jne ... */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[3]; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_LDA && CS_GetEntries (S, L, I+1, 3) && L[0]->OPC == OP65_LDX && !CE_HasLabel (L[0]) && CE_IsCallTo (L[1], "bnegax") && !CE_HasLabel (L[1]) && (L[2]->Info & OF_ZBRA) != 0 && !CE_HasLabel (L[2])) { /* ldx --> ora */ CE_ReplaceOPC (L[0], OP65_ORA); /* Invert the branch */ CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC)); /* Delete the subroutine call */ CS_DelEntry (S, I+2); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptBNegA2 (CodeSeg* S) /* Check for * * lda .. * jsr bnega * jeq/jne .. * * Adjust the conditional branch and remove the call to the subroutine. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[2]; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if ((E->OPC == OP65_ADC || E->OPC == OP65_AND || E->OPC == OP65_DEA || E->OPC == OP65_EOR || E->OPC == OP65_INA || E->OPC == OP65_LDA || E->OPC == OP65_ORA || E->OPC == OP65_PLA || E->OPC == OP65_SBC || E->OPC == OP65_TXA || E->OPC == OP65_TYA) && CS_GetEntries (S, L, I+1, 2) && CE_IsCallTo (L[0], "bnega") && !CE_HasLabel (L[0]) && (L[1]->Info & OF_ZBRA) != 0 && !CE_HasLabel (L[1])) { /* Invert the branch */ CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); /* Delete the subroutine call */ CS_DelEntry (S, I+1); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptNegAX1 (CodeSeg* S) /* Search for a call to negax and replace it by * * eor #$FF * clc * adc #$01 * * if X isn't used later. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if this is a call to negax, and if X isn't used later */ if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { CodeEntry* X; /* Add replacement code behind */ X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); CS_InsertEntry (S, X, I+1); X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+2); X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); CS_InsertEntry (S, X, I+3); /* Delete the call to negax */ CS_DelEntry (S, I); /* Skip the generated code */ I += 2; /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptBoolTrans (CodeSeg* S) /* Try to remove the call to boolean transformer routines where the call is * not really needed. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* N; cmp_t Cond; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a boolean transformer */ if (E->OPC == OP65_JSR && (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_ZBRA) != 0) { /* Make the boolean transformer unnecessary by changing the * the conditional jump to evaluate the condition flags that * are set after the compare directly. Note: jeq jumps if * the condition is not met, jne jumps if the condition is met. * Invert the code if we jump on condition not met. */ if (GetBranchCond (N->OPC) == BC_EQ) { /* Jumps if condition false, invert condition */ Cond = CmpInvertTab [Cond]; } /* Check if we can replace the code by something better */ ReplaceCmp (S, I+1, Cond); /* Remove the call to the bool transformer */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
void CS_DelEntries (CodeSeg* S, unsigned Start, unsigned Count) /* Delete a range of code entries. This includes removing references to labels, * labels attached to the entries and so on. */ { /* Start deleting the entries from the rear, because this involves less * memory moving. */ while (Count--) { CS_DelEntry (S, Start + Count); } }
unsigned OptSub3 (CodeSeg* S) /* Search for a call to decaxn and replace it by an 8 bit sub if the X register ** is not used later. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* E; /* Get next entry */ E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && strncmp (E->Arg, "decax", 5) == 0 && IsDigit (E->Arg[5]) && E->Arg[6] == '\0' && !RegXUsed (S, I+1)) { CodeEntry* X; const char* Arg; /* Insert new code behind the sequence */ X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+1); Arg = MakeHexArg (E->Arg[5] - '0'); X = NewCodeEntry (OP65_SBC, AM65_IMM, Arg, 0, E->LI); CS_InsertEntry (S, X, I+2); /* Delete the old code */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
static unsigned OptLoad1 (CodeSeg* S) /* Search for a call to ldaxysp where X is not used later and replace it by ** a load of just the A register. */ { unsigned I; unsigned Changes = 0; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* E; /* Get next entry */ E = CS_GetEntry (S, I); /* Check for the sequence */ if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY) && !RegXUsed (S, I+1)) { CodeEntry* X; /* Reload the Y register */ const char* Arg = MakeHexArg (E->RI->In.RegY - 1); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); CS_InsertEntry (S, X, I+1); /* Load from stack */ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); CS_InsertEntry (S, X, I+2); /* Now remove the call to the subroutine */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptBNegA1 (CodeSeg* S) /* Check for * * ldx #$00 * lda .. * jsr bnega * * Remove the ldx if the lda does not use it. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[2]; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a ldx */ if (E->OPC == OP65_LDX && E->AM == AM65_IMM && (E->Flags & CEF_NUMARG) != 0 && E->Num == 0 && CS_GetEntries (S, L, I+1, 2) && L[0]->OPC == OP65_LDA && (L[0]->Use & REG_X) == 0 && !CE_HasLabel (L[0]) && CE_IsCallTo (L[1], "bnega") && !CE_HasLabel (L[1])) { /* Remove the ldx instruction */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned Opt65C02Stores (CodeSeg* S) /* Use STZ where possible */ { unsigned Changes = 0; unsigned I; /* Generate register info for this step */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a store with a register value of zero and an addressing * mode available with STZ. */ if (((E->OPC == OP65_STA && E->RI->In.RegA == 0) || (E->OPC == OP65_STX && E->RI->In.RegX == 0) || (E->OPC == OP65_STY && E->RI->In.RegY == 0)) && (E->AM == AM65_ZP || E->AM == AM65_ABS || E->AM == AM65_ZPX || E->AM == AM65_ABSX)) { /* Replace by STZ */ CodeEntry* X = NewCodeEntry (OP65_STZ, E->AM, E->Arg, 0, E->LI); CS_InsertEntry (S, X, I+1); /* Delete the old stuff */ CS_DelEntry (S, I); /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Free register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }
unsigned Opt65C02Ind (CodeSeg* S) /* Try to use the indirect addressing mode where possible */ { unsigned Changes = 0; unsigned I; /* Generate register info for this step */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for addressing mode indirect indexed Y where Y is zero. * Note: All opcodes that are available as (zp),y are also available * as (zp), so we can ignore the actual opcode here. */ if (E->AM == AM65_ZP_INDY && E->RI->In.RegY == 0) { /* Replace it by indirect addressing mode */ CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_IND, E->Arg, 0, E->LI); CS_InsertEntry (S, X, I+1); CS_DelEntry (S, I); /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Free register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }
unsigned OptCmp7 (CodeSeg* S) /* Search for a sequence ldx/txa/branch and remove the txa if A is not * used later. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[2]; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if ((E->OPC == OP65_LDX) && CS_GetEntries (S, L, I+1, 2) && L[0]->OPC == OP65_TXA && !CE_HasLabel (L[0]) && (L[1]->Info & OF_FBRA) != 0 && !CE_HasLabel (L[1]) && !RegAUsed (S, I+3)) { /* Remove the txa */ CS_DelEntry (S, I+1); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptTest2 (CodeSeg* S) /* Search for an inc/dec operation followed by a load and a conditional * branch based on the flags from the load. Remove the load if the insn * isn't used later. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[3]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check if it's the sequence we're searching for */ if ((L[0]->OPC == OP65_INC || L[0]->OPC == OP65_DEC) && CS_GetEntries (S, L+1, I+1, 2) && !CE_HasLabel (L[1]) && (L[1]->Info & OF_LOAD) != 0 && (L[2]->Info & OF_FBRA) != 0 && L[1]->AM == L[0]->AM && strcmp (L[0]->Arg, L[1]->Arg) == 0 && (GetRegInfo (S, I+2, L[1]->Chg) & L[1]->Chg) == 0) { /* Remove the load */ CS_DelEntry (S, I+1); ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
static unsigned OptDecouple (CodeSeg* S) /* Decouple operations, that is, do the following replacements: ** ** dex -> ldx #imm ** inx -> ldx #imm ** dey -> ldy #imm ** iny -> ldy #imm ** tax -> ldx #imm ** txa -> lda #imm ** tay -> ldy #imm ** tya -> lda #imm ** lda zp -> lda #imm ** ldx zp -> ldx #imm ** ldy zp -> ldy #imm ** ** Provided that the register values are known of course. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { const char* Arg; /* Get next entry and it's input register values */ CodeEntry* E = CS_GetEntry (S, I); const RegContents* In = &E->RI->In; /* Assume we have no replacement */ CodeEntry* X = 0; /* Check the instruction */ switch (E->OPC) { case OP65_DEA: if (RegValIsKnown (In->RegA)) { Arg = MakeHexArg ((In->RegA - 1) & 0xFF); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); } break; case OP65_DEX: if (RegValIsKnown (In->RegX)) { Arg = MakeHexArg ((In->RegX - 1) & 0xFF); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); } break; case OP65_DEY: if (RegValIsKnown (In->RegY)) { Arg = MakeHexArg ((In->RegY - 1) & 0xFF); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); } break; case OP65_INA: if (RegValIsKnown (In->RegA)) { Arg = MakeHexArg ((In->RegA + 1) & 0xFF); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); } break; case OP65_INX: if (RegValIsKnown (In->RegX)) { Arg = MakeHexArg ((In->RegX + 1) & 0xFF); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); } break; case OP65_INY: if (RegValIsKnown (In->RegY)) { Arg = MakeHexArg ((In->RegY + 1) & 0xFF); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); } break; case OP65_LDA: if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Use & REG_ZP, In)) { case REG_TMP1: Arg = MakeHexArg (In->Tmp1); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); break; case REG_PTR1_LO: Arg = MakeHexArg (In->Ptr1Lo); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); break; case REG_PTR1_HI: Arg = MakeHexArg (In->Ptr1Hi); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); break; case REG_SREG_LO: Arg = MakeHexArg (In->SRegLo); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); break; case REG_SREG_HI: Arg = MakeHexArg (In->SRegHi); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); break; } } break; case OP65_LDX: if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Use & REG_ZP, In)) { case REG_TMP1: Arg = MakeHexArg (In->Tmp1); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); break; case REG_PTR1_LO: Arg = MakeHexArg (In->Ptr1Lo); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); break; case REG_PTR1_HI: Arg = MakeHexArg (In->Ptr1Hi); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); break; case REG_SREG_LO: Arg = MakeHexArg (In->SRegLo); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); break; case REG_SREG_HI: Arg = MakeHexArg (In->SRegHi); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); break; } } break; case OP65_LDY: if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Use, In)) { case REG_TMP1: Arg = MakeHexArg (In->Tmp1); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); break; case REG_PTR1_LO: Arg = MakeHexArg (In->Ptr1Lo); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); break; case REG_PTR1_HI: Arg = MakeHexArg (In->Ptr1Hi); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); break; case REG_SREG_LO: Arg = MakeHexArg (In->SRegLo); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); break; case REG_SREG_HI: Arg = MakeHexArg (In->SRegHi); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); break; } } break; case OP65_TAX: if (E->RI->In.RegA >= 0) { Arg = MakeHexArg (In->RegA); X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); } break; case OP65_TAY: if (E->RI->In.RegA >= 0) { Arg = MakeHexArg (In->RegA); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); } break; case OP65_TXA: if (E->RI->In.RegX >= 0) { Arg = MakeHexArg (In->RegX); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); } break; case OP65_TYA: if (E->RI->In.RegY >= 0) { Arg = MakeHexArg (In->RegY); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); } break; default: /* Avoid gcc warnings */ break; } /* Insert the replacement if we have one */ if (X) { CS_InsertEntry (S, X, I+1); CS_DelEntry (S, I); ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptTest1 (CodeSeg* S) /* Given a sequence * * stx xxx * ora xxx * beq/bne ... * * If X is zero, the sequence may be changed to * * cmp #$00 * beq/bne ... * * which may be optimized further by another step. * * If A is zero, the sequence may be changed to * * txa * beq/bne ... * */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[3]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check if it's the sequence we're searching for */ if (L[0]->OPC == OP65_STX && CS_GetEntries (S, L+1, I+1, 2) && !CE_HasLabel (L[1]) && L[1]->OPC == OP65_ORA && strcmp (L[0]->Arg, L[1]->Arg) == 0 && !CE_HasLabel (L[2]) && (L[2]->Info & OF_ZBRA) != 0) { /* Check if X is zero */ if (L[0]->RI->In.RegX == 0) { /* Insert the compare */ CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI); CS_InsertEntry (S, N, I+2); /* Remove the two other insns */ CS_DelEntry (S, I+1); CS_DelEntry (S, I); /* We had changes */ ++Changes; /* Check if A is zero */ } else if (L[1]->RI->In.RegA == 0) { /* Insert the txa */ CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI); CS_InsertEntry (S, N, I+2); /* Remove the two other insns */ CS_DelEntry (S, I+1); CS_DelEntry (S, I); /* We had changes */ ++Changes; } } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
static unsigned OptLoad2 (CodeSeg* S) /* Replace calls to ldaxysp by inline code */ { unsigned I; unsigned Changes = 0; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[3]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (CE_IsCallTo (L[0], "ldaxysp")) { CodeEntry* X; /* Followed by sta abs/stx abs? */ if (CS_GetEntries (S, L+1, I+1, 2) && L[1]->OPC == OP65_STA && L[2]->OPC == OP65_STX && (L[1]->Arg == 0 || L[2]->Arg == 0 || strcmp (L[1]->Arg, L[2]->Arg) != 0) && !CS_RangeHasLabel (S, I+1, 2) && !RegXUsed (S, I+3)) { /* A/X are stored into memory somewhere and X is not used ** later */ /* lda (sp),y */ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); CS_InsertEntry (S, X, I+3); /* sta abs */ X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); CS_InsertEntry (S, X, I+4); /* dey */ X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); CS_InsertEntry (S, X, I+5); /* lda (sp),y */ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); CS_InsertEntry (S, X, I+6); /* sta abs */ X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); CS_InsertEntry (S, X, I+7); /* Now remove the call to the subroutine and the sta/stx */ CS_DelEntries (S, I, 3); } else { /* Standard replacement */ /* lda (sp),y */ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); CS_InsertEntry (S, X, I+1); /* tax */ X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); CS_InsertEntry (S, X, I+2); /* dey */ X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); CS_InsertEntry (S, X, I+3); /* lda (sp),y */ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); CS_InsertEntry (S, X, I+4); /* Now remove the call to the subroutine */ CS_DelEntry (S, I); } /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
static unsigned OptLoad3 (CodeSeg* S) /* Remove repeated loads from one and the same memory location */ { unsigned Changes = 0; CodeEntry* Load = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Forget a preceeding load if we have a label */ if (Load && CE_HasLabel (E)) { Load = 0; } /* Check if this insn is a load */ if (E->Info & OF_LOAD) { CodeEntry* N; /* If we had a preceeding load that is identical, remove this one. ** If it is not identical, or we didn't have one, remember it. */ if (Load != 0 && E->OPC == Load->OPC && E->AM == Load->AM && ((E->Arg == 0 && Load->Arg == 0) || strcmp (E->Arg, Load->Arg) == 0) && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_CBRA) == 0) { /* Now remove the call to the subroutine */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; /* Next insn */ continue; } else { Load = E; } } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { /* Forget the first load on occurance of any insn we don't like */ Load = 0; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptNegAX2 (CodeSeg* S) /* Search for a call to negax and replace it by * * ldx #$FF * eor #$FF * clc * adc #$01 * bne L1 * inx * L1: * * if X is known and zero on entry. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* P; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if this is a call to negax, and if X is known and zero */ if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "negax") && (P = CS_GetNextEntry (S, I)) != 0) { CodeEntry* X; CodeLabel* L; /* Add replacement code behind */ /* ldx #$FF */ X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); CS_InsertEntry (S, X, I+1); /* eor #$FF */ X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); CS_InsertEntry (S, X, I+2); /* clc */ X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+3); /* adc #$01 */ X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); CS_InsertEntry (S, X, I+4); /* Get the label attached to the insn following the call */ L = CS_GenLabel (S, P); /* bne L */ X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+5); /* inx */ X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+6); /* Delete the call to negax */ CS_DelEntry (S, I); /* Skip the generated code */ I += 5; /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptCmp9 (CodeSeg* S) /* Search for the sequence * * sbc xx * bvs/bvc L * eor #$80 * L: asl a * bcc/bcs somewhere * * If A is not used later (which should be the case), we can branch on the N * flag instead of the carry flag and remove the asl. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[5]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (L[0]->OPC == OP65_SBC && CS_GetEntries (S, L+1, I+1, 4) && (L[1]->OPC == OP65_BVC || L[1]->OPC == OP65_BVS) && L[1]->JumpTo != 0 && L[1]->JumpTo->Owner == L[3] && L[2]->OPC == OP65_EOR && CE_IsKnownImm (L[2], 0x80) && L[3]->OPC == OP65_ASL && L[3]->AM == AM65_ACC && (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_BCS || L[4]->OPC == OP65_JCC || L[4]->OPC == OP65_JCS) && !CE_HasLabel (L[4]) && !RegAUsed (S, I+4)) { /* Replace the branch condition */ switch (GetBranchCond (L[4]->OPC)) { case BC_CC: CE_ReplaceOPC (L[4], OP65_JPL); break; case BC_CS: CE_ReplaceOPC (L[4], OP65_JMI); break; default: Internal ("Unknown branch condition in OptCmp9"); } /* Delete the asl insn */ CS_DelEntry (S, I+3); /* Next sequence is somewhat ahead (if any) */ I += 3; /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptSub2 (CodeSeg* S) /* Search for the sequence ** ** lda xx ** sec ** sta tmp1 ** lda yy ** sbc tmp1 ** sta yy ** ** and replace it by ** ** sec ** lda yy ** sbc xx ** sta yy */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[5]; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_LDA && !CS_RangeHasLabel (S, I+1, 5) && CS_GetEntries (S, L, I+1, 5) && L[0]->OPC == OP65_SEC && L[1]->OPC == OP65_STA && strcmp (L[1]->Arg, "tmp1") == 0 && L[2]->OPC == OP65_LDA && L[3]->OPC == OP65_SBC && strcmp (L[3]->Arg, "tmp1") == 0 && L[4]->OPC == OP65_STA && strcmp (L[4]->Arg, L[2]->Arg) == 0) { /* Remove the store to tmp1 */ CS_DelEntry (S, I+2); /* Remove the subtraction */ CS_DelEntry (S, I+3); /* Move the lda to the position of the subtraction and change the ** op to SBC. */ CS_MoveEntry (S, I, I+3); CE_ReplaceOPC (E, OP65_SBC); /* If the sequence head had a label, move this label back to the ** head. */ if (CE_HasLabel (E)) { CS_MoveLabels (S, E, L[0]); } /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptShift6 (CodeSeg* S) /* Inline the shift subroutines. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { unsigned Shift; unsigned Count; CodeEntry* X; unsigned IP; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a call to one of the shift routine */ if (E->OPC == OP65_JSR && (Shift = GetShift (E->Arg)) != SHIFT_NONE && SHIFT_DIR (Shift) == SHIFT_DIR_LEFT && (Count = SHIFT_COUNT (Shift)) > 0) { /* Code is: * * stx tmp1 * asl a * rol tmp1 * (repeat ShiftCount-1 times) * ldx tmp1 * * which makes 4 + 3 * ShiftCount bytes, compared to the original * 3 bytes for the subroutine call. However, in most cases, the * final load of the X register gets merged with some other insn * and replaces a txa, so for a shift count of 1, we get a factor * of 200, which matches nicely the CodeSizeFactor enabled with -Oi */ if (Count > 1 || S->CodeSizeFactor > 200) { unsigned Size = 4 + 3 * Count; if ((Size * 100 / 3) > S->CodeSizeFactor) { /* Not acceptable */ goto NextEntry; } } /* Inline the code. Insertion point is behind the subroutine call */ IP = (I + 1); /* stx tmp1 */ X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, E->LI); CS_InsertEntry (S, X, IP++); while (Count--) { /* asl a */ X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, IP++); /* rol tmp1 */ X = NewCodeEntry (OP65_ROL, AM65_ZP, "tmp1", 0, E->LI); CS_InsertEntry (S, X, IP++); } /* ldx tmp1 */ X = NewCodeEntry (OP65_LDX, AM65_ZP, "tmp1", 0, E->LI); CS_InsertEntry (S, X, IP++); /* Remove the subroutine call */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } NextEntry: /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptShift4 (CodeSeg* S) /* Calls to the asraxN or shraxN routines may get replaced by one or more lsr * insns if the value of X is zero. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { unsigned Shift; unsigned Count; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && (Shift = GetShift (E->Arg)) != SHIFT_NONE && SHIFT_DIR (Shift) == SHIFT_DIR_RIGHT && E->RI->In.RegX == 0) { CodeEntry* X; /* Shift count may be in Y */ Count = SHIFT_COUNT (Shift); if (Count == SHIFT_COUNT_Y) { CodeLabel* L; if (S->CodeSizeFactor < 200) { /* Not acceptable */ goto NextEntry; } /* Generate: * * L1: lsr a * dey * bpl L1 * rol a * * A negative shift count or one that is greater or equal than * the bit width of the left operand (which is promoted to * integer before the operation) causes undefined behaviour, so * above transformation is safe. */ /* lsr a */ X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); L = CS_GenLabel (S, X); /* dey */ X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+2); /* bpl L1 */ X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+3); /* rol a */ X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+4); } else { /* Insert shift insns */ while (Count--) { X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); } } /* Delete the call to shrax */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } NextEntry: /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptStore3 (CodeSeg* S) /* Search for a call to steaxysp. If the eax register is not used later, and ** the value is constant, just use the A register and store directly into the ** stack. */ { unsigned I; unsigned Changes = 0; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Get the input registers */ const RegInfo* RI = E->RI; /* Check for the call */ if (CE_IsCallTo (E, "steaxysp") && RegValIsKnown (RI->In.RegA) && RegValIsKnown (RI->In.RegX) && RegValIsKnown (RI->In.RegY) && RegValIsKnown (RI->In.SRegLo) && RegValIsKnown (RI->In.SRegHi) && !RegEAXUsed (S, I+1)) { /* Get the register values */ unsigned char A = (unsigned char) RI->In.RegA; unsigned char X = (unsigned char) RI->In.RegX; unsigned char Y = (unsigned char) RI->In.RegY; unsigned char L = (unsigned char) RI->In.SRegLo; unsigned char H = (unsigned char) RI->In.SRegHi; /* Setup other variables */ unsigned Done = 0; CodeEntry* N; unsigned IP = I + 1; /* Insertion point */ /* Replace the store. We will not remove the loads, since this is ** too complex and will be done by other optimizer steps. */ N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (A), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x01; /* Check if we can store one of the other bytes */ if (A == X && (Done & 0x02) == 0) { N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+1), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x02; } if (A == L && (Done & 0x04) == 0) { N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+2), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x04; } if (A == H && (Done & 0x08) == 0) { N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x08; } /* Store the second byte */ if ((Done & 0x02) == 0) { N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (X), 0, E->LI); CS_InsertEntry (S, N, IP++); N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+1), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x02; } /* Check if we can store one of the other bytes */ if (X == L && (Done & 0x04) == 0) { N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+2), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x04; } if (X == H && (Done & 0x08) == 0) { N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x08; } /* Store the third byte */ if ((Done & 0x04) == 0) { N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (L), 0, E->LI); CS_InsertEntry (S, N, IP++); N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+2), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x04; } /* Check if we can store one of the other bytes */ if (L == H && (Done & 0x08) == 0) { N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x08; } /* Store the fourth byte */ if ((Done & 0x08) == 0) { N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (H), 0, E->LI); CS_InsertEntry (S, N, IP++); N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); CS_InsertEntry (S, N, IP++); InsertStore (S, &IP, E->LI); Done |= 0x08; } /* Remove the call */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptBNegAX4 (CodeSeg* S) /* Search for the sequence: * * jsr xxx * jsr bnega(x) * jeq/jne ... * * and replace it by: * * jsr xxx * <boolean test> * jne/jeq ... */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[2]; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && CS_GetEntries (S, L, I+1, 2) && L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg,"bnega",5) == 0 && !CE_HasLabel (L[0]) && (L[1]->Info & OF_ZBRA) != 0 && !CE_HasLabel (L[1])) { CodeEntry* X; /* Check if we're calling bnega or bnegax */ int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0); /* Insert apropriate test code */ if (ByteSized) { /* Test bytes */ X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); CS_InsertEntry (S, X, I+2); } else { /* Test words */ X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI); CS_InsertEntry (S, X, I+2); X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI); CS_InsertEntry (S, X, I+3); } /* Delete the subroutine call */ CS_DelEntry (S, I+1); /* Invert the branch */ CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptCmp3 (CodeSeg* S) /* Search for * * lda/and/ora/eor ... * cmp #$00 * jeq/jne * or * lda/and/ora/eor ... * cmp #$00 * jsr boolxx * * and remove the cmp. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[3]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if ((L[0]->OPC == OP65_ADC || L[0]->OPC == OP65_AND || L[0]->OPC == OP65_ASL || L[0]->OPC == OP65_DEA || L[0]->OPC == OP65_EOR || L[0]->OPC == OP65_INA || L[0]->OPC == OP65_LDA || L[0]->OPC == OP65_LSR || L[0]->OPC == OP65_ORA || L[0]->OPC == OP65_PLA || L[0]->OPC == OP65_SBC || L[0]->OPC == OP65_TXA || L[0]->OPC == OP65_TYA) && !CS_RangeHasLabel (S, I+1, 2) && CS_GetEntries (S, L+1, I+1, 2) && L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0)) { int Delete = 0; /* Check for the call to boolxx. We only remove the compare if * the carry flag is not evaluated later, because the load will * not set the carry flag. */ if (L[2]->OPC == OP65_JSR) { switch (FindBoolCmpCond (L[2]->Arg)) { case CMP_EQ: case CMP_NE: case CMP_GT: case CMP_GE: case CMP_LT: case CMP_LE: /* Remove the compare */ Delete = 1; break; case CMP_UGT: case CMP_UGE: case CMP_ULT: case CMP_ULE: case CMP_INV: /* Leave it alone */ break; } } else if ((L[2]->Info & OF_FBRA) != 0) { /* The following insn branches on the condition of the load, * so the compare instruction might be removed. For safety, * do some more checks if the carry isn't used later, since * the compare does set the carry, but the load does not. */ CodeEntry* E; CodeEntry* N; if ((E = CS_GetNextEntry (S, I+2)) != 0 && L[2]->JumpTo != 0 && (N = L[2]->JumpTo->Owner) != 0 && N->OPC != OP65_BCC && N->OPC != OP65_BCS && N->OPC != OP65_JCC && N->OPC != OP65_JCS && (N->OPC != OP65_JSR || FindBoolCmpCond (N->Arg) == CMP_INV)) { /* The following insn branches on the condition of a load, * and there's no use of the carry flag in sight, so the * compare instruction can be removed. */ Delete = 1; } } /* Delete the compare if we can */ if (Delete) { CS_DelEntry (S, I+1); ++Changes; } } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptCmp8 (CodeSeg* S) /* Check for register compares where the contents of the register and therefore * the result of the compare is known. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { int RegVal; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a compare against an immediate value */ if ((E->Info & OF_CMP) != 0 && (RegVal = GetCmpRegVal (E)) >= 0 && CE_IsConstImm (E)) { /* We are able to evaluate the compare at compile time. Check if * one or more branches are ahead. */ unsigned JumpsChanged = 0; CodeEntry* N; while ((N = CS_GetNextEntry (S, I)) != 0 && /* Followed by something.. */ (N->Info & OF_CBRA) != 0 && /* ..that is a cond branch.. */ !CE_HasLabel (N)) { /* ..and has no label */ /* Evaluate the branch condition */ int Cond; switch (GetBranchCond (N->OPC)) { case BC_CC: Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num); break; case BC_CS: Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num); break; case BC_EQ: Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num); break; case BC_MI: Cond = ((signed char)RegVal) < ((signed char)E->Num); break; case BC_NE: Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num); break; case BC_PL: Cond = ((signed char)RegVal) >= ((signed char)E->Num); break; case BC_VC: case BC_VS: /* Not set by the compare operation, bail out (Note: * Just skipping anything here is rather stupid, but * the sequence is never generated by the compiler, * so it's quite safe to skip). */ goto NextEntry; default: Internal ("Unknown branch condition"); } /* If the condition is false, we may remove the jump. Otherwise * the branch will always be taken, so we may replace it by a * jump (and bail out). */ if (!Cond) { CS_DelEntry (S, I+1); } else { CodeLabel* L = N->JumpTo; const char* LabelName = L? L->Name : N->Arg; CodeEntry* X = NewCodeEntry (OP65_JMP, AM65_BRA, LabelName, L, N->LI); CS_InsertEntry (S, X, I+2); CS_DelEntry (S, I+1); } /* Remember, we had changes */ ++JumpsChanged; ++Changes; } /* If we have made changes above, we may also remove the compare */ if (JumpsChanged) { CS_DelEntry (S, I); } } NextEntry: /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptStore5 (CodeSeg* S) /* Search for the sequence ** ** lda foo ** ldx bar ** sta something ** stx something-else ** ** and replace it by ** ** lda foo ** sta something ** lda bar ** sta something-else ** ** if X is not used later. This replacement doesn't save any cycles or bytes, ** but it keeps the value of X, which may be reused later. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[4]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (L[0]->OPC == OP65_LDA && !CS_RangeHasLabel (S, I+1, 3) && CS_GetEntries (S, L+1, I+1, 3) && L[1]->OPC == OP65_LDX && L[2]->OPC == OP65_STA && L[3]->OPC == OP65_STX && !RegXUsed (S, I+4)) { CodeEntry* X; /* Insert the code after the sequence */ X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); CS_InsertEntry (S, X, I+4); X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI); CS_InsertEntry (S, X, I+5); /* Delete the old code */ CS_DelEntry (S, I+3); CS_DelEntry (S, I+1); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptCmp5 (CodeSeg* S) /* Optimize compares of local variables: * * ldy #o * jsr ldaxysp * cpx #a * bne L1 * cmp #b * jne/jeq L2 */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[6]; /* Get the next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (L[0]->OPC == OP65_LDY && CE_IsConstImm (L[0]) && CS_GetEntries (S, L+1, I+1, 5) && !CE_HasLabel (L[1]) && CE_IsCallTo (L[1], "ldaxysp") && IsImmCmp16 (L+2)) { if ((L[5]->Info & OF_FBRA) != 0 && L[2]->Num == 0 && L[4]->Num == 0) { CodeEntry* X; char Buf[20]; /* The value is zero, we may use the simple code version: * ldy #o-1 * lda (sp),y * ldy #o * ora (sp),y * jne/jeq ... */ sprintf (Buf, "$%02X", (int)(L[0]->Num-1)); X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI); CS_InsertEntry (S, X, I+1); X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); CS_InsertEntry (S, X, I+2); X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI); CS_InsertEntry (S, X, I+3); X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI); CS_InsertEntry (S, X, I+4); CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */ CS_DelEntry (S, I); /* ldy */ } else { CodeEntry* X; char Buf[20]; /* Change the code to just use the A register. Move the load * of the low byte after the first branch if possible: * * ldy #o * lda (sp),y * cmp #a * bne L1 * ldy #o-1 * lda (sp),y * cmp #b * jne/jeq ... */ X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI); CS_InsertEntry (S, X, I+3); X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); CS_InsertEntry (S, X, I+4); X = NewCodeEntry (OP65_CMP, L[2]->AM, L[2]->Arg, 0, L[2]->LI); CS_InsertEntry (S, X, I+5); sprintf (Buf, "$%02X", (int)(L[0]->Num-1)); X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI); CS_InsertEntry (S, X, I+7); X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); CS_InsertEntry (S, X, I+8); CS_DelEntries (S, I, 3); /* ldy/jsr/cpx */ } ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }