+static int IsSpLoad (const CodeEntry* E)
+/* Return true if this is the load of A from the stack */
+{
+ return E->OPC == OPC_LDA && E->AM == AM_ZP_INDY && strcmp (E->Arg, "sp") == 0;
+}
+
+
+
+static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
+ CodeEntry** L, unsigned Count)
+/* Check if a 16 bit load of a local variable follows:
+ *
+ * ldy #$xx
+ * lda (sp),y
+ * tax
+ * dey
+ * lda (sp),y
+ *
+ * If so, read Count entries following the first ldy into L and return true
+ * if this is possible. Otherwise return false.
+ */
+{
+ /* Be sure we read enough entries for the check */
+ CHECK (Count >= 5);
+
+ /* Read the first entry */
+ L[0] = GetCodeEntry (S, Index);
+
+ /* Check for the sequence */
+ return (L[0]->OPC == OPC_LDY &&
+ L[0]->AM == AM_IMM &&
+ (L[0]->Flags & CEF_NUMARG) != 0 &&
+ GetCodeEntries (S, L+1, Index+1, Count-1) &&
+ IsSpLoad (L[1]) &&
+ !CodeEntryHasLabel (L[1]) &&
+ L[2]->OPC == OPC_TAX &&
+ !CodeEntryHasLabel (L[2]) &&
+ L[3]->OPC == OPC_DEY &&
+ !CodeEntryHasLabel (L[3]) &&
+ IsSpLoad (L[4]) &&
+ !CodeEntryHasLabel (L[4]));
+}
+
+
+
+static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
+/* Check if the instructions at L are an immidiate compare of a/x:
+ *
+ *
+ */
+{
+ return (L[0]->OPC == OPC_CPX &&
+ L[0]->AM == AM_IMM &&
+ (L[0]->Flags & CEF_NUMARG) != 0 &&
+ !CodeEntryHasLabel (L[0]) &&
+ (L[1]->OPC == OPC_JNE || L[1]->OPC == OPC_BNE) &&
+ L[1]->JumpTo != 0 &&
+ !CodeEntryHasLabel (L[1]) &&
+ L[2]->OPC == OPC_CMP &&
+ L[2]->AM == AM_IMM &&
+ (L[2]->Flags & CEF_NUMARG) != 0 &&
+ (L[3]->Info & OF_ZBRA) != 0 &&
+ L[3]->JumpTo != 0 &&
+ (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
+}
+
+
+
/*****************************************************************************/
-/* Remove calls to the bool transformer subroutines */
+/* Remove calls to the bool transformer subroutines */
/*****************************************************************************/
GetCodeEntries (S, L, I+1, 5) &&
L[0]->OPC == OPC_LDX &&
!CodeEntryHasLabel (L[0]) &&
- L[1]->OPC == OPC_CPX &&
- L[1]->AM == AM_IMM &&
- (L[1]->Flags & CEF_NUMARG) != 0 &&
- !CodeEntryHasLabel (L[1]) &&
- (L[2]->OPC == OPC_JNE || L[2]->OPC == OPC_BNE) &&
- L[2]->JumpTo != 0 &&
- !CodeEntryHasLabel (L[2]) &&
- L[3]->OPC == OPC_CMP &&
- L[3]->AM == AM_IMM &&
- (L[3]->Flags & CEF_NUMARG) != 0 &&
- (L[4]->Info & OF_ZBRA) != 0 &&
- L[4]->JumpTo != 0 &&
- (L[2]->JumpTo->Owner == L[4] || L[2]->JumpTo == L[4]->JumpTo)) {
-
- /* Get the compare value */
- unsigned Val = ((L[1]->Num & 0xFF) << 8) | (L[3]->Num & 0xFF);
-
- if (Val == 0) {
+ IsImmCmp16 (S, L+1)) {
+
+ if (L[1]->Num == 0 && L[3]->Num == 0) {
/* The value is zero, we may use the simple code version. */
ReplaceOPC (L[0], OPC_ORA);
DelCodeEntries (S, I+2, 3);
} else {
- /* Move the lda instruction after the first branch */
- CodeEntry* N = RetrieveCodeEntry (S, I);
- InsertCodeEntry (S, N, I+3);
+ /* Move the lda instruction after the first branch. This will
+ * improve speed, since the load is delayed after the first
+ * test.
+ */
+ MoveCodeEntry (S, I, I+4);
- /* Replace the ldx/cpx by lda/cmp */
+ /* We will replace the ldx/cpx by lda/cmp */
ReplaceOPC (L[0], OPC_LDA);
ReplaceOPC (L[1], OPC_CMP);
- /* The high byte is zero, remove the CMP */
- if ((Val & 0xFF00) == 0) {
- DelCodeEntry (S, I+1);
- }
+ }
+
+ ++Changes;
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+static unsigned OptCmp4 (CodeSeg* S)
+/* Optimize compares of local variables:
+ *
+ * ldy #o
+ * lda (sp),y
+ * tax
+ * dey
+ * lda (sp),y
+ * cpx #a
+ * bne L1
+ * cmp #b
+ * jne/jeq L2
+ */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < GetCodeEntryCount (S)) {
+
+ CodeEntry* L[9];
+
+ /* Check for the sequence */
+ if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
+
+ if (L[5]->Num == 0 && L[7]->Num == 0) {
+
+ /* The value is zero, we may use the simple code version:
+ * ldy #o
+ * lda (sp),y
+ * dey
+ * ora (sp),y
+ * jne/jeq ...
+ */
+ ReplaceOPC (L[4], OPC_ORA);
+ DelCodeEntries (S, I+5, 3); /* cpx/bne/cmp */
+ DelCodeEntry (S, I+2); /* tax */
+
+ } else {
+
+ /* 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
+ * dey
+ * lda (sp),y
+ * cmp #b
+ * jne/jeq ...
+ */
+ DelCodeEntry (S, I+2); /* tax */
+ ReplaceOPC (L[5], OPC_CMP); /* cpx -> cmp */
+ MoveCodeEntry (S, I+4, I+2); /* cmp */
+ MoveCodeEntry (S, I+5, I+3); /* bne */
+
}
++Changes;
{ OptCmp2, "OptCmp2", 0 },
/* Optimize compares */
{ OptCmp3, "OptCmp3", 0 },
+ /* Optimize compares */
+ { OptCmp4, "OptCmp4", 0 },
/* Remove unused loads */
{ OptUnusedLoads, "OptUnusedLoads", 0 },
/* Optimize branch distance */
-struct CodeEntry* RetrieveCodeEntry (CodeSeg* S, unsigned Index)
-/* Retrieve a code entry. This means, the code entry is removed from the
- * entry collection, but not deleted and returned instead. The entry may
- * then be inserted again at another position.
+void MoveCodeEntry (CodeSeg* S, unsigned OldPos, unsigned NewPos)
+/* Move an entry from one position to another. OldPos is the current position
+ * of the entry, NewPos is the new position of the entry.
*/
{
- /* Get the code entry, remove it from the collection and return it */
- CodeEntry* E = GetCodeEntry (S, Index);
- CollDelete (&S->Entries, Index);
- return E;
+ /* Get the code entry and remove it from the collection */
+ CodeEntry* E = GetCodeEntry (S, OldPos);
+ CollDelete (&S->Entries, OldPos);
+
+ /* Correct NewPos if needed */
+ if (NewPos >= OldPos) {
+ /* Position has changed with removal */
+ --NewPos;
+ }
+
+ /* Now insert it at the new position */
+ CollInsert (&S->Entries, E, NewPos);
}