+NextEntry:
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+unsigned OptUnusedStores (CodeSeg* S)
+/* Remove stores into zero page registers that aren't used later */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < CS_GetEntryCount (S)) {
+
+ /* Get next entry */
+ CodeEntry* E = CS_GetEntry (S, I);
+
+ /* Check if it's a register load or transfer insn */
+ if ((E->Info & OF_STORE) != 0 &&
+ E->AM == AM65_ZP &&
+ (E->Chg & REG_ZP) != 0) {
+
+ /* Check for the zero page location. We know that there cannot be
+ * more than one zero page location involved in the store.
+ */
+ unsigned R = E->Chg & REG_ZP;
+
+ /* Get register usage and check if the register value is used later */
+ if ((GetRegInfo (S, I+1, R) & R) == 0) {
+
+ /* Register value is not used, remove the load */
+ CS_DelEntry (S, I);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ }
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+unsigned OptDupLoads (CodeSeg* S)
+/* Remove loads of registers where the value loaded is already in the register. */
+{
+ 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)) {
+
+ CodeEntry* N;
+
+ /* Get next entry */
+ CodeEntry* E = CS_GetEntry (S, I);
+
+ /* Assume we won't delete the entry */
+ int Delete = 0;
+
+ /* Get a pointer to the input registers of the insn */
+ const RegContents* In = &E->RI->In;
+
+ /* Handle the different instructions */
+ switch (E->OPC) {
+
+ case OP65_LDA:
+ if (RegValIsKnown (In->RegA) && /* Value of A is known */
+ CE_KnownImm (E) && /* Value to be loaded is known */
+ In->RegA == (long) E->Num && /* Both are equal */
+ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */
+ !CE_UseLoadFlags (N)) { /* Which does not use the flags */
+ Delete = 1;
+ }
+ break;
+
+ case OP65_LDX:
+ if (RegValIsKnown (In->RegX) && /* Value of X is known */
+ CE_KnownImm (E) && /* Value to be loaded is known */
+ In->RegX == (long) E->Num && /* Both are equal */
+ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */
+ !CE_UseLoadFlags (N)) { /* Which does not use the flags */
+ Delete = 1;
+ }
+ break;
+
+ case OP65_LDY:
+ if (RegValIsKnown (In->RegY) && /* Value of Y is known */
+ CE_KnownImm (E) && /* Value to be loaded is known */
+ In->RegY == (long) E->Num && /* Both are equal */
+ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */
+ !CE_UseLoadFlags (N)) { /* Which does not use the flags */
+ Delete = 1;
+ }
+ break;
+
+ case OP65_STA:
+ /* If we store into a known zero page location, and this
+ * location does already contain the value to be stored,
+ * remove the store.
+ */
+ if (RegValIsKnown (In->RegA) && /* Value of A is known */
+ E->AM == AM65_ZP && /* Store into zp */
+ In->RegA == RegVal (E->Chg, In)) { /* Value identical */
+
+ Delete = 1;
+ }
+ break;
+
+ case OP65_STX:
+ /* If we store into a known zero page location, and this
+ * location does already contain the value to be stored,
+ * remove the store.
+ */
+ if (RegValIsKnown (In->RegX) && /* Value of A is known */
+ E->AM == AM65_ZP && /* Store into zp */
+ In->RegX == RegVal (E->Chg, In)) { /* Value identical */
+
+ Delete = 1;
+
+ /* If the value in the X register is known and the same as
+ * that in the A register, replace the store by a STA. The
+ * optimizer will then remove the load instruction for X
+ * later. STX does support the zeropage,y addressing mode,
+ * so be sure to check for that.
+ */
+ } else if (RegValIsKnown (In->RegX) &&
+ In->RegX == In->RegA &&
+ E->AM != AM65_ABSY &&
+ E->AM != AM65_ZPY) {
+ /* Use the A register instead */
+ CE_ReplaceOPC (E, OP65_STA);
+ }
+ break;
+
+ case OP65_STY:
+ /* If we store into a known zero page location, and this
+ * location does already contain the value to be stored,
+ * remove the store.
+ */
+ if (RegValIsKnown (In->RegY) && /* Value of Y is known */
+ E->AM == AM65_ZP && /* Store into zp */
+ In->RegY == RegVal (E->Chg, In)) { /* Value identical */
+
+ Delete = 1;
+
+ /* If the value in the Y register is known and the same as
+ * that in the A register, replace the store by a STA. The
+ * optimizer will then remove the load instruction for Y
+ * later. If replacement by A is not possible try a
+ * replacement by X, but check for invalid addressing modes
+ * in this case.
+ */
+ } else if (RegValIsKnown (In->RegY)) {
+ if (In->RegY == In->RegA) {
+ CE_ReplaceOPC (E, OP65_STA);
+ } else if (In->RegY == In->RegX &&
+ E->AM != AM65_ABSX &&
+ E->AM != AM65_ZPX) {
+ CE_ReplaceOPC (E, OP65_STX);
+ }
+ }
+ break;
+
+ case OP65_STZ:
+ /* If we store into a known zero page location, and this
+ * location does already contain the value to be stored,
+ * remove the store.
+ */
+ if (CPU >= CPU_65C02 && E->AM == AM65_ZP) {
+ if (RegVal (E->Chg, In) == 0) {
+ Delete = 1;
+ }
+ }
+ break;
+
+ case OP65_TAX:
+ if (RegValIsKnown (In->RegA) &&
+ In->RegA == In->RegX &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
+ !CE_UseLoadFlags (N)) {
+ /* Value is identical and not followed by a branch */
+ Delete = 1;
+ }
+ break;
+
+ case OP65_TAY:
+ if (RegValIsKnown (In->RegA) &&
+ In->RegA == In->RegY &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
+ !CE_UseLoadFlags (N)) {
+ /* Value is identical and not followed by a branch */
+ Delete = 1;
+ }
+ break;
+
+ case OP65_TXA:
+ if (RegValIsKnown (In->RegX) &&
+ In->RegX == In->RegA &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
+ !CE_UseLoadFlags (N)) {
+ /* Value is identical and not followed by a branch */
+ Delete = 1;
+ }
+ break;
+
+ case OP65_TYA:
+ if (RegValIsKnown (In->RegY) &&
+ In->RegY == In->RegA &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
+ !CE_UseLoadFlags (N)) {
+ /* Value is identical and not followed by a branch */
+ Delete = 1;
+ }
+ break;
+
+ default:
+ break;
+
+ }
+
+ /* Delete the entry if requested */
+ if (Delete) {
+
+ /* Register value is not used, remove the load */
+ CS_DelEntry (S, I);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ } else {
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ }
+
+ /* Free register info */
+ CS_FreeRegInfo (S);
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+unsigned OptStoreLoad (CodeSeg* S)
+/* Remove a store followed by a load from the same location. */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < CS_GetEntryCount (S)) {
+
+ CodeEntry* N;
+ CodeEntry* X;
+
+ /* Get next entry */
+ CodeEntry* E = CS_GetEntry (S, I);
+
+ /* Check if it is a store instruction followed by a load from the
+ * same address which is itself not followed by a conditional branch.
+ */
+ if ((E->Info & OF_STORE) != 0 &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
+ !CE_HasLabel (N) &&
+ E->AM == N->AM &&
+ ((E->OPC == OP65_STA && N->OPC == OP65_LDA) ||
+ (E->OPC == OP65_STX && N->OPC == OP65_LDX) ||
+ (E->OPC == OP65_STY && N->OPC == OP65_LDY)) &&
+ strcmp (E->Arg, N->Arg) == 0 &&
+ (X = CS_GetNextEntry (S, I+1)) != 0 &&
+ !CE_UseLoadFlags (X)) {
+
+ /* Register has already the correct value, remove the load */
+ CS_DelEntry (S, I+1);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+unsigned OptTransfers (CodeSeg* S)
+/* Remove transfers from one register to another and back */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < CS_GetEntryCount (S)) {
+
+ CodeEntry* N;
+ CodeEntry* X;
+ CodeEntry* P;
+
+ /* Get next entry */
+ CodeEntry* E = CS_GetEntry (S, I);
+
+ /* Check if it is a store instruction followed by a load from the
+ * same address which is itself not followed by a conditional branch.
+ */
+ if ((E->Info & OF_XFR) != 0 &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
+ !CE_HasLabel (N) &&
+ (N->Info & OF_XFR) != 0) {
+
+ /* Check if it's a transfer and back */
+ if ((E->OPC == OP65_TAX && N->OPC == OP65_TXA && !RegXUsed (S, I+2)) ||
+ (E->OPC == OP65_TAY && N->OPC == OP65_TYA && !RegYUsed (S, I+2)) ||
+ (E->OPC == OP65_TXA && N->OPC == OP65_TAX && !RegAUsed (S, I+2)) ||
+ (E->OPC == OP65_TYA && N->OPC == OP65_TAY && !RegAUsed (S, I+2))) {
+
+ /* If the next insn is a conditional branch, check if the insn
+ * preceeding the first xfr will set the flags right, otherwise we
+ * may not remove the sequence.
+ */
+ if ((X = CS_GetNextEntry (S, I+1)) == 0) {
+ goto NextEntry;
+ }
+ if (CE_UseLoadFlags (X)) {
+ if (I == 0) {
+ /* No preceeding entry */
+ goto NextEntry;
+ }
+ P = CS_GetEntry (S, I-1);
+ if ((P->Info & OF_SETF) == 0) {
+ /* Does not set the flags */
+ goto NextEntry;
+ }
+ }
+
+ /* Remove both transfers */
+ CS_DelEntry (S, I+1);
+ CS_DelEntry (S, I);
+
+ /* Remember, we had changes */
+ ++Changes;
+ }
+ }
+