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 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 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; }
unsigned OptSub1 (CodeSeg* S) /* Search for the sequence ** ** sbc ... ** bcs L ** dex ** L: ** ** and remove the handling of the high byte if X is not used later. */ { 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_SBC && CS_GetEntries (S, L, I+1, 3) && (L[0]->OPC == OP65_BCS || L[0]->OPC == OP65_JCS) && L[0]->JumpTo != 0 && !CE_HasLabel (L[0]) && L[1]->OPC == OP65_DEX && !CE_HasLabel (L[1]) && L[0]->JumpTo->Owner == L[2] && !RegXUsed (S, I+3)) { /* Remove the bcs/dex */ CS_DelEntries (S, I+1, 2); /* 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 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; }
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; }
unsigned OptShift1 (CodeSeg* S) /* A call to the shlaxN routine may get replaced by one or more asl insns * if the value of X is not used later. If X is used later, but it is zero * on entry and it's a shift by one, it may get replaced by: * * asl a * bcc L1 * inx * L1: * */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { unsigned Shift; CodeEntry* N; CodeEntry* X; CodeLabel* L; /* 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_LEFT) { unsigned Count = SHIFT_COUNT (Shift); if (!RegXUsed (S, I+1)) { if (Count == SHIFT_COUNT_Y) { CodeLabel* L; if (S->CodeSizeFactor < 200) { goto NextEntry; } /* Change into * * L1: asl a * dey * bpl L1 * ror a */ /* asl a */ X = NewCodeEntry (OP65_ASL, 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); /* ror a */ X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+4); } else { /* Insert shift insns */ while (Count--) { X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); } } } else if (E->RI->In.RegX == 0 && Count == 1 && (N = CS_GetNextEntry (S, I)) != 0) { /* asl a */ X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); /* bcc L1 */ L = CS_GenLabel (S, N); X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+2); /* inx */ X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+3); } else { /* We won't handle this one */ goto NextEntry; } /* Delete the call to shlax */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } NextEntry: /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }