case OP65_ASL:
if (E->AM == AM65_ACC && In->RegA >= 0) {
Out->RegA = (In->RegA << 1) & 0xFF;
+ } else if (E->AM == AM65_ZP) {
+ if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) {
+ Out->SRegLo = (In->SRegLo << 1) & 0xFF;
+ } else if ((E->Chg & REG_SREG_HI) != 0 && In->SRegHi >= 0) {
+ Out->SRegHi = (In->SRegHi << 1) & 0xFF;
+ }
}
break;
case OP65_DEA:
if (In->RegA >= 0) {
- Out->RegA = In->RegA - 1;
+ Out->RegA = (In->RegA - 1) & 0xFF;
}
break;
case OP65_DEC:
if (E->AM == AM65_ACC && In->RegA >= 0) {
- Out->RegA = In->RegA - 1;
+ Out->RegA = (In->RegA - 1) & 0xFF;
+ } else if (E->AM == AM65_ZP) {
+ if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) {
+ Out->SRegLo = (In->SRegLo - 1) & 0xFF;
+ } else if ((E->Chg & REG_SREG_HI) != 0 && In->SRegHi >= 0) {
+ Out->SRegHi = (In->SRegHi - 1) & 0xFF;
+ }
}
break;
case OP65_DEX:
if (In->RegX >= 0) {
- Out->RegX = In->RegX - 1;
+ Out->RegX = (In->RegX - 1) & 0xFF;
}
break;
case OP65_DEY:
if (In->RegY >= 0) {
- Out->RegY = In->RegY - 1;
+ Out->RegY = (In->RegY - 1) & 0xFF;
}
break;
case OP65_INA:
if (In->RegA >= 0) {
- Out->RegA = In->RegA + 1;
+ Out->RegA = (In->RegA + 1) & 0xFF;
}
break;
case OP65_INC:
if (E->AM == AM65_ACC && In->RegA >= 0) {
- Out->RegA = In->RegA + 1;
+ Out->RegA = (In->RegA + 1) & 0xFF;
+ } else if (E->AM == AM65_ZP) {
+ if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) {
+ Out->SRegLo = (In->SRegLo + 1) & 0xFF;
+ } else if ((E->Chg & REG_SREG_HI) != 0 && In->SRegHi >= 0) {
+ Out->SRegHi = (In->SRegHi + 1) & 0xFF;
+ }
}
break;
case OP65_INX:
if (In->RegX >= 0) {
- Out->RegX = In->RegX + 1;
+ Out->RegX = (In->RegX + 1) & 0xFF;
}
break;
case OP65_INY:
if (In->RegY >= 0) {
- Out->RegY = In->RegY + 1;
+ Out->RegY = (In->RegY + 1) & 0xFF;
}
break;
if (Chg & REG_Y) {
Out->RegY = -1;
}
+ if (Chg & REG_SREG_LO) {
+ Out->SRegLo = -1;
+ }
+ if (Chg & REG_SREG_HI) {
+ Out->SRegHi = -1;
+ }
break;
case OP65_JVC:
case OP65_LDA:
if (CE_KnownImm (E)) {
- Out->RegA = (unsigned char) E->Num;
+ Out->RegA = (unsigned char) E->Num;
+ } else if (E->AM == AM65_ZP) {
+ if (E->Use & REG_SREG_LO) {
+ Out->RegA = In->SRegLo;
+ } else if (E->Use & REG_SREG_HI) {
+ Out->RegA = In->SRegHi;
+ } else {
+ Out->RegA = -1;
+ }
} else {
- /* A is now unknown */
- Out->RegA = -1;
+ /* A is now unknown */
+ Out->RegA = -1;
}
break;
case OP65_LDX:
if (CE_KnownImm (E)) {
- Out->RegX = (unsigned char) E->Num;
+ Out->RegX = (unsigned char) E->Num;
+ } else if (E->AM == AM65_ZP) {
+ if (E->Use & REG_SREG_LO) {
+ Out->RegX = In->SRegLo;
+ } else if (E->Use & REG_SREG_HI) {
+ Out->RegX = In->SRegHi;
+ } else {
+ Out->RegX = -1;
+ }
} else {
- /* X is now unknown */
- Out->RegX = -1;
+ /* X is now unknown */
+ Out->RegX = -1;
}
break;
case OP65_LDY:
if (CE_KnownImm (E)) {
- Out->RegY = (unsigned char) E->Num;
+ Out->RegY = (unsigned char) E->Num;
+ } else if (E->AM == AM65_ZP) {
+ if (E->Use & REG_SREG_LO) {
+ Out->RegY = In->SRegLo;
+ } else if (E->Use & REG_SREG_HI) {
+ Out->RegY = In->SRegHi;
+ } else {
+ Out->RegY = -1;
+ }
} else {
/* Y is now unknown */
Out->RegY = -1;
case OP65_LSR:
if (E->AM == AM65_ACC && In->RegA >= 0) {
Out->RegA = (In->RegA >> 1) & 0xFF;
+ } else if (E->AM == AM65_ZP) {
+ if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) {
+ Out->SRegLo = (In->SRegLo >> 1) & 0xFF;
+ } else if (E->Chg & REG_SREG_HI) {
+ Out->SRegHi = (In->SRegHi >> 1) & 0xFF;
+ }
}
break;
case OP65_PLP:
break;
- case OP65_PLX:
- Out->RegX = -1;
- break;
+ case OP65_PLX:
+ Out->RegX = -1;
+ break;
- case OP65_PLY:
- Out->RegY = -1;
- break;
+ case OP65_PLY:
+ Out->RegY = -1;
+ break;
- case OP65_ROL:
- Out->RegA = -1;
- break;
+ case OP65_ROL:
+ if (E->AM == AM65_ACC) {
+ Out->RegA = -1;
+ } else if (E->AM == AM65_ZP) {
+ if (E->Chg & REG_SREG_LO) {
+ Out->SRegLo = -1;
+ } else if (E->Chg & REG_SREG_HI) {
+ Out->SRegHi = -1;
+ }
+ }
+ break;
- case OP65_ROR:
- Out->RegA = -1;
- break;
+ case OP65_ROR:
+ if (E->AM == AM65_ACC) {
+ Out->RegA = -1;
+ } else if (E->AM == AM65_ZP) {
+ if (E->Chg & REG_SREG_LO) {
+ Out->SRegLo = -1;
+ } else if (E->Chg & REG_SREG_HI) {
+ Out->SRegHi = -1;
+ }
+ }
+ break;
- case OP65_RTI:
- break;
+ case OP65_RTI:
+ break;
- case OP65_RTS:
- break;
+ case OP65_RTS:
+ break;
- case OP65_SBC:
- /* We don't know the value of the carry bit */
- Out->RegA = -1;
- break;
+ case OP65_SBC:
+ /* We don't know the value of the carry bit */
+ Out->RegA = -1;
+ break;
- case OP65_SEC:
- break;
+ case OP65_SEC:
+ break;
- case OP65_SED:
- break;
+ case OP65_SED:
+ break;
- case OP65_SEI:
- break;
+ case OP65_SEI:
+ break;
- case OP65_STA:
+ case OP65_STA:
+ if (E->AM == AM65_ZP) {
+ if (E->Chg & REG_SREG_LO) {
+ Out->SRegLo = In->RegA;
+ } else if (E->Chg & REG_SREG_HI) {
+ Out->SRegHi = In->RegA;
+ }
+ }
break;
case OP65_STX:
+ if (E->AM == AM65_ZP) {
+ if (E->Chg & REG_SREG_LO) {
+ Out->SRegLo = In->RegX;
+ } else if (E->Chg & REG_SREG_HI) {
+ Out->SRegHi = In->RegX;
+ }
+ }
break;
case OP65_STY:
+ if (E->AM == AM65_ZP) {
+ if (E->Chg & REG_SREG_LO) {
+ Out->SRegLo = In->RegY;
+ } else if (E->Chg & REG_SREG_HI) {
+ Out->SRegHi = In->RegY;
+ }
+ }
break;
case OP65_TAX:
Out->RegX = In->RegA;
break;
- case OP65_TAY:
+ case OP65_TAY:
Out->RegY = In->RegA;
break;
{
Buf[0] = '\0';
- strcat (Buf, U & REG_SREG? "E" : "_");
- strcat (Buf, U & REG_A? "A" : "_");
- strcat (Buf, U & REG_X? "X" : "_");
- strcat (Buf, U & REG_Y? "Y" : "_");
- strcat (Buf, U & REG_SP? "S" : "_");
- strcat (Buf, U & REG_TMP1? "T1" : "__");
- strcat (Buf, U & REG_TMP2? "T2" : "__");
- strcat (Buf, U & REG_TMP3? "T3" : "__");
- strcat (Buf, U & REG_TMP4? "T4" : "__");
- strcat (Buf, U & REG_PTR1? "1" : "_");
- strcat (Buf, U & REG_PTR2? "2" : "_");
- strcat (Buf, U & REG_PTR3? "3" : "_");
- strcat (Buf, U & REG_PTR4? "4" : "_");
- strcat (Buf, U & REG_SAVE? "V" : "_");
- strcat (Buf, U & REG_BANK? "B" : "_");
+ strcat (Buf, U & REG_SREG_HI? "H" : "_");
+ strcat (Buf, U & REG_SREG_LO? "L" : "_");
+ strcat (Buf, U & REG_A? "A" : "_");
+ strcat (Buf, U & REG_X? "X" : "_");
+ strcat (Buf, U & REG_Y? "Y" : "_");
+ strcat (Buf, U & REG_SP? "S" : "_");
+ strcat (Buf, U & REG_TMP1? "T1" : "__");
+ strcat (Buf, U & REG_TMP2? "T2" : "__");
+ strcat (Buf, U & REG_TMP3? "T3" : "__");
+ strcat (Buf, U & REG_TMP4? "T4" : "__");
+ strcat (Buf, U & REG_PTR1? "1" : "_");
+ strcat (Buf, U & REG_PTR2? "2" : "_");
+ strcat (Buf, U & REG_PTR3? "3" : "_");
+ strcat (Buf, U & REG_PTR4? "4" : "_");
+ strcat (Buf, U & REG_SAVE? "V" : "_");
+ strcat (Buf, U & REG_BANK? "B" : "_");
return Buf;
}
char Use [128];
char Chg [128];
fprintf (F,
- "%*s; USE: %-19s CHG: %-19s SIZE: %u\n",
+ "%*s; USE: %-20s CHG: %-20s SIZE: %u\n",
30-Chars, "",
RegInfoDesc (E->Use, Use),
RegInfoDesc (E->Chg, Chg),
}
} else {
if ((Flags & CF_NOKEEP) == 0 || CodeSizeFactor < 160) {
- if (Offs) {
- ldyconst (Offs);
- AddCodeLine ("jsr staxysp");
- } else {
- AddCodeLine ("jsr stax0sp");
- }
+ ldyconst (Offs);
+ AddCodeLine ("jsr staxysp");
} else {
if (CPU == CPU_65C02 && Offs == 0) {
AddCodeLine ("sta (sp)");
{ 7, "regbank", REG_BANK },
{ 7, "regsave", REG_SAVE },
{ 2, "sp", REG_SP },
- { 4, "sreg", REG_SREG },
+ { 0, "sreg", REG_SREG_LO },
+ { 0, "sreg+1", REG_SREG_HI },
{ 4, "tmp1", REG_TMP1 },
{ 4, "tmp2", REG_TMP2 },
{ 4, "tmp3", REG_TMP3 },
} else {
- /* Search for the function in the list of builtin functions */
+ /* Search for the function in the list of builtin functions */
const FuncInfo* Info = bsearch (Name, FuncInfoTable, FuncInfoCount,
sizeof(FuncInfo), CompareFuncInfo);
/* Do the compare. Be careful because of the length (Info may contain
* more than just the zeropage name).
*/
- int Res = strncmp (N, E->Name, E->Len);
- if (Res == 0 && (N[E->Len] != '\0' && N[E->Len] != '+')) {
- /* Name is actually longer than Info->Name */
- Res = -1;
+ if (E->Len == 0) {
+ /* Do a full compare */
+ return strcmp (N, E->Name);
+ } else {
+ /* Only compare the first part */
+ int Res = strncmp (N, E->Name, E->Len);
+ if (Res == 0 && (N[E->Len] != '\0' && N[E->Len] != '+')) {
+ /* Name is actually longer than Info->Name */
+ Res = -1;
+ }
+ return Res;
}
- return Res;
}
-static unsigned GetRegInfo1 (CodeSeg* S,
- CodeEntry* E,
- int Index,
- Collection* Visited,
- unsigned Used,
- unsigned Unused);
-/* Recursively called subfunction for GetRegInfo. */
-
-
-
static unsigned GetRegInfo2 (CodeSeg* S,
CodeEntry* E,
int Index,
Collection* Visited,
unsigned Used,
- unsigned Unused)
+ unsigned Unused,
+ unsigned Wanted)
/* Recursively called subfunction for GetRegInfo. */
{
/* Follow the instruction flow recording register usage. */
}
/* If we know about all registers now, bail out */
- if ((Used | Unused) == REG_AXY) {
+ if (((Used | Unused) & Wanted) == Wanted) {
break;
}
/* Unconditional jump */
E = E->JumpTo->Owner;
- Index = -1; /* Invalidate */
+ Index = -1; /* Invalidate */
} else {
/* Jump outside means we're done */
unsigned U1;
unsigned U2;
- U1 = GetRegInfo1 (S, E->JumpTo->Owner, -1, Visited, Used, Unused);
+ U1 = GetRegInfo2 (S, E->JumpTo->Owner, -1, Visited, Used, Unused, Wanted);
if (U1 == REG_AXY) {
/* All registers used, no need for second call */
return REG_AXY;
if ((E = CS_GetEntry (S, ++Index)) == 0) {
Internal ("GetRegInfo2: No next entry!");
}
- U2 = GetRegInfo1 (S, E, Index, Visited, Used, Unused);
+ U2 = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted);
return U1 | U2; /* Used in any of the branches */
} else {
int Index,
Collection* Visited,
unsigned Used,
- unsigned Unused)
+ unsigned Unused,
+ unsigned Wanted)
/* Recursively called subfunction for GetRegInfo. */
{
/* Remember the current count of the line collection */
unsigned Count = CollCount (Visited);
/* Call the worker routine */
- unsigned R = GetRegInfo2 (S, E, Index, Visited, Used, Unused);
+ unsigned R = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted);
/* Restore the old count, unmarking all new entries */
unsigned NewCount = CollCount (Visited);
-unsigned GetRegInfo (struct CodeSeg* S, unsigned Index)
+unsigned GetRegInfo (struct CodeSeg* S, unsigned Index, unsigned Wanted)
/* Determine register usage information for the instructions starting at the
* given index.
*/
InitCollection (&Visited);
/* Call the recursive subfunction */
- R = GetRegInfo1 (S, E, Index, &Visited, REG_NONE, REG_NONE);
+ R = GetRegInfo1 (S, E, Index, &Visited, REG_NONE, REG_NONE, Wanted);
/* Delete the line collection */
DoneCollection (&Visited);
int RegAUsed (struct CodeSeg* S, unsigned Index)
/* Check if the value in A is used. */
{
- return (GetRegInfo (S, Index) & REG_A) != 0;
+ return (GetRegInfo (S, Index, REG_A) & REG_A) != 0;
}
int RegXUsed (struct CodeSeg* S, unsigned Index)
/* Check if the value in X is used. */
{
- return (GetRegInfo (S, Index) & REG_X) != 0;
+ return (GetRegInfo (S, Index, REG_X) & REG_X) != 0;
}
int RegYUsed (struct CodeSeg* S, unsigned Index)
/* Check if the value in Y is used. */
{
- return (GetRegInfo (S, Index) & REG_Y) != 0;
+ return (GetRegInfo (S, Index, REG_Y) & REG_Y) != 0;
}
#define REG_A 0x0001U
#define REG_X 0x0002U
#define REG_Y 0x0004U
-#define REG_TMP1 0x0010U
-#define REG_TMP2 0x0020U
-#define REG_TMP3 0x0040U
-#define REG_TMP4 0x0080U
-#define REG_PTR1 0x0100U
-#define REG_PTR2 0x0200U
-#define REG_PTR3 0x0400U
-#define REG_PTR4 0x0800U
-#define REG_SREG 0x1000U
+#define REG_TMP1 0x0008U
+#define REG_TMP2 0x0010U
+#define REG_TMP3 0x0020U
+#define REG_TMP4 0x0040U
+#define REG_PTR1 0x0080U
+#define REG_PTR2 0x0100U
+#define REG_PTR3 0x0200U
+#define REG_PTR4 0x0400U
+#define REG_SREG_LO 0x0800U
+#define REG_SREG_HI 0x1000U
#define REG_SP 0x2000U
#define REG_SAVE 0x4000U
#define REG_BANK 0x8000U
/* Combined register defines */
+#define REG_SREG (REG_SREG_LO | REG_SREG_HI)
#define REG_AX (REG_A | REG_X)
#define REG_AY (REG_A | REG_Y)
#define REG_XY (REG_X | REG_Y)
* zero page location found.
*/
-unsigned GetRegInfo (struct CodeSeg* S, unsigned Index);
+unsigned GetRegInfo (struct CodeSeg* S, unsigned Index, unsigned Wanted);
/* Determine register usage information for the instructions starting at the
* given index.
*/
L[6]->OPC == OP65_JSR &&
strcmp (L[6]->Arg, "addeqysp") == 0 &&
!CE_HasLabel (L[6]) &&
- (GetRegInfo (S, I+7) & REG_AX) == 0) {
+ (GetRegInfo (S, I+7, REG_AX) & REG_AX) == 0) {
char Buf [20];
CodeEntry* X;
OptEntry (OptTest1, optMain),
/* Remove unused loads */
OptEntry (OptUnusedLoads, optMain),
+ OptEntry (OptUnusedStores, optMain),
OptEntry (OptDuplicateLoads, optMain),
OptEntry (OptStoreLoad, optMain),
OptEntry (OptTransfers, optMain),
/* If we have a function given, get the return type of the function.
* Assume ANY return type besides void will use the A and X registers.
*/
- RetType = GetFuncReturn (Func->Type);
- if (S->Func && !IsTypeVoid (RetType)) {
+ if (S->Func && !IsTypeVoid ((RetType = GetFuncReturn (Func->Type)))) {
if (SizeOf (RetType) == SizeOf (type_long)) {
S->ExitRegs = REG_EAX;
} else {
RC_Invalidate (&Regs);
CurrentRegs = &Regs;
- /* First pass. Walk over all insns an note just the changes from one
+ /* First pass. Walk over all insns and note just the changes from one
* insn to the next one.
*/
WasJump = 0;
if (J->RI->Out2.RegY != Regs.RegY) {
Regs.RegY = -1;
}
+ if (J->RI->Out2.SRegLo != Regs.SRegLo) {
+ Regs.SRegLo = -1;
+ }
+ if (J->RI->Out2.SRegHi != Regs.SRegHi) {
+ Regs.SRegHi = -1;
+ }
++Entry;
}
/*****************************************************************************/
-/* Remove unused loads */
+/* Remove unused loads and stores */
/*****************************************************************************/
}
/* Get register usage and check if the register value is used later */
- if ((GetRegInfo (S, I+1) & R) == 0) {
+ if ((GetRegInfo (S, I+1, R) & R) == 0) {
/* Register value is not used, remove the load */
CS_DelEntry (S, I);
+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 OptDuplicateLoads (CodeSeg* S)
/* Remove loads of registers where the value loaded is already in the register. */
{
/* 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 (E->RI->In.RegA >= 0 && /* Value of A is known */
+ if (In->RegA >= 0 && /* Value of A is known */
CE_KnownImm (E) && /* Value to be loaded is known */
- E->RI->In.RegA == (long) E->Num && /* Both are equal */
+ In->RegA == (long) E->Num && /* Both are equal */
(N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */
(N->Info & OF_FBRA) == 0) { /* Which is not a cond branch */
Delete = 1;
break;
case OP65_LDX:
- if (E->RI->In.RegX >= 0 && /* Value of X is known */
+ if (In->RegX >= 0 && /* Value of X is known */
CE_KnownImm (E) && /* Value to be loaded is known */
- E->RI->In.RegX == (long) E->Num && /* Both are equal */
+ In->RegX == (long) E->Num && /* Both are equal */
(N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */
(N->Info & OF_FBRA) == 0) { /* Which is not a cond branch */
Delete = 1;
break;
case OP65_LDY:
- if (E->RI->In.RegY >= 0 && /* Value of Y is known */
+ if (In->RegY >= 0 && /* Value of Y is known */
CE_KnownImm (E) && /* Value to be loaded is known */
- E->RI->In.RegY == (long) E->Num && /* Both are equal */
+ In->RegY == (long) E->Num && /* Both are equal */
(N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */
(N->Info & OF_FBRA) == 0) { /* Which is not a cond branch */
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 (In->RegA >= 0 && /* Value of A is known */
+ E->AM == AM65_ZP && /* Store into zp */
+ (((E->Chg & REG_SREG_LO) != 0 && /* Store into sreg */
+ In->RegA == In->SRegLo) || /* Value identical */
+ ((E->Chg & REG_SREG_HI) != 0 && /* Store into sreg+1 */
+ In->RegA == In->SRegHi))) { /* Value identical */
+ Delete = 1;
+ }
+ break;
+
case OP65_STX:
- /* If the value in the X register is known and the same as
+ /* If we store into a known zero page location, and this
+ * location does already contain the value to be stored,
+ * remove the store.
+ */
+ if (In->RegX >= 0 && /* Value of A is known */
+ E->AM == AM65_ZP && /* Store into zp */
+ (((E->Chg & REG_SREG_LO) != 0 && /* Store into sreg */
+ In->RegX == In->SRegLo) || /* Value identical */
+ ((E->Chg & REG_SREG_HI) != 0 && /* Store into sreg+1 */
+ In->RegX == In->SRegHi))) { /* 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.
*/
- if (E->RI->In.RegX >= 0 &&
- E->RI->In.RegX == E->RI->In.RegA &&
- E->AM != AM65_ABSY &&
- E->AM != AM65_ZPY) {
+ } else if (In->RegX >= 0 &&
+ 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 (In->RegX >= 0 && /* Value of A is known */
+ E->AM == AM65_ZP && /* Store into zp */
+ (((E->Chg & REG_SREG_LO) != 0 && /* Store into sreg */
+ In->RegX == In->SRegLo) || /* Value identical */
+ ((E->Chg & REG_SREG_HI) != 0 && /* Store into sreg+1 */
+ In->RegX == In->SRegHi))) { /* 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
* replacement by X, but check for invalid addressing modes
* in this case.
*/
- if (E->RI->In.RegY >= 0) {
- if (E->RI->In.RegY == E->RI->In.RegA) {
+ } else if (In->RegY >= 0) {
+ if (In->RegY == In->RegA) {
CE_ReplaceOPC (E, OP65_STA);
- } else if (E->RI->In.RegY == E->RI->In.RegX &&
- E->AM != AM65_ABSX &&
+ } else if (In->RegY == In->RegX &&
+ E->AM != AM65_ABSX &&
E->AM != AM65_ZPX) {
CE_ReplaceOPC (E, OP65_STX);
}
break;
case OP65_TAX:
- if (E->RI->In.RegA >= 0 &&
- E->RI->In.RegA == E->RI->In.RegX &&
- (N = CS_GetNextEntry (S, I)) != 0 &&
+ if (In->RegA >= 0 &&
+ In->RegA == In->RegX &&
+ (N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_FBRA) == 0) {
/* Value is identical and not followed by a branch */
Delete = 1;
break;
case OP65_TAY:
- if (E->RI->In.RegA >= 0 &&
- E->RI->In.RegA == E->RI->In.RegY &&
+ if (In->RegA >= 0 &&
+ In->RegA == In->RegY &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_FBRA) == 0) {
/* Value is identical and not followed by a branch */
break;
case OP65_TXA:
- if (E->RI->In.RegX >= 0 &&
- E->RI->In.RegX == E->RI->In.RegA &&
+ if (In->RegX >= 0 &&
+ In->RegX == In->RegA &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_FBRA) == 0) {
/* Value is identical and not followed by a branch */
break;
case OP65_TYA:
- if (E->RI->In.RegY >= 0 &&
- E->RI->In.RegY == E->RI->In.RegA &&
+ if (In->RegY >= 0 &&
+ In->RegY == In->RegA &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_FBRA) == 0) {
/* Value is identical and not followed by a branch */
unsigned OptUnusedLoads (CodeSeg* S);
/* Remove loads of registers where the value loaded is not used later. */
+unsigned OptUnusedStores (CodeSeg* S);
+/* Remove stores into zero page registers that aren't used later */
+
unsigned OptDuplicateLoads (CodeSeg* S);
/* Remove loads of registers where the value loaded is already in the register. */
int IsFastCallFunc (const type* T)
-/* Return true if this is a function type with __fastcall__ calling conventions */
+/* Return true if this is a function type or pointer to function with
+ * __fastcall__ calling conventions
+ */
{
- FuncDesc* F;
- CHECK (IsTypeFunc (T));
- F = (FuncDesc*) DecodePtr (T+1);
+ FuncDesc* F = GetFuncDesc (T);
return (F->Flags & FD_FASTCALL) != 0;
}
int IsVariadicFunc (const type* T)
-/* Return true if this is a function type with variable parameter list */
+/* Return true if this is a function type or pointer to function type with
+ * variable parameter list
+ */
{
- FuncDesc* F;
- CHECK (IsTypeFunc (T));
- F = (FuncDesc*) DecodePtr (T+1);
+ FuncDesc* F = GetFuncDesc (T);
return (F->Flags & FD_VARIADIC) != 0;
}
/* Return true if the given type has a volatile type qualifier */
int IsFastCallFunc (const type* T) attribute ((const));
-/* Return true if this is a function type with __fastcall__ calling conventions */
+/* Return true if this is a function type or pointer to function with
+ * __fastcall__ calling conventions
+ */
int IsVariadicFunc (const type* T) attribute ((const));
-/* Return true if this is a function type with variable parameter list */
+/* Return true if this is a function type or pointer to function type with
+ * variable parameter list
+ */
int IsTypeFuncPtr (const type* T) attribute ((const));
/* Return true if this is a function pointer */
/* Parse the function */
Decl (D, Mode);
/* Set the fastcall flag */
- if (!IsTypeFunc (T)) {
+ if (!IsTypeFunc (T) && !IsTypeFuncPtr (T)) {
Error ("__fastcall__ modifier applied to non function");
} else if (IsVariadicFunc (T)) {
Error ("Cannot apply __fastcall__ to functions with variable parameter list");
} else {
- FuncDesc* F = (FuncDesc*) DecodePtr (T+1);
+ FuncDesc* F = GetFuncDesc (T);
F->Flags |= FD_FASTCALL;
}
return;
-static void FunctionCall (ExprDesc* lval)
-/* Perform a function call. Called from hie11, this routine will
- * either call the named function, or the function pointer in a/x.
- */
+static void FunctionCall (int k, ExprDesc* lval)
+/* Perform a function call. */
{
- FuncDesc* Func; /* Function descriptor */
- unsigned ParamSize; /* Number of parameter bytes */
- CodeMark Mark;
-
+ FuncDesc* Func; /* Function descriptor */
+ int IsFuncPtr; /* Flag */
+ unsigned ParamSize; /* Number of parameter bytes */
+ CodeMark Mark = 0; /* Initialize to keep gcc silent */
+ int PtrOffs = 0; /* Offset of function pointer on stack */
+ int IsFastCall = 0; /* True if it's a fast call function */
+ int PtrOnStack = 0; /* True if a pointer copy is on stack */
/* Get a pointer to the function descriptor from the type string */
Func = GetFuncDesc (lval->Type);
- /* Initialize vars to keep gcc silent */
- Mark = 0;
+ /* Handle function pointers transparently */
+ IsFuncPtr = IsTypeFuncPtr (lval->Type);
+ if (IsFuncPtr) {
- /* Check if this is a function pointer. If so, save it. If not, check for
- * special known library functions that may be inlined.
- */
- if (lval->Flags & E_MEXPR) {
- /* Function pointer is in primary register, save it */
- Mark = GetCodePos ();
- g_save (CF_PTR);
+ /* Check wether it's a fastcall function */
+ IsFastCall = IsFastCallFunc (lval->Type + 1);
+
+ /* Things may be difficult, depending on where the function pointer
+ * resides. If the function pointer is an expression of some sort
+ * (not a local or global variable), we have to evaluate this
+ * expression now and save the result for later. Since calls to
+ * function pointers may be nested, we must save it onto the stack.
+ * For fastcall functions we do also need to place a copy of the
+ * pointer on stack, since we cannot use a/x.
+ */
+ PtrOnStack = IsFastCall || ((lval->Flags & (E_MGLOBAL | E_MLOCAL)) == 0);
+ if (PtrOnStack) {
+
+ /* Not a global or local variable, or a fastcall function. Load
+ * the pointer into the primary and mark it as an expression.
+ */
+ exprhs (CF_NONE, k, lval);
+ lval->Flags |= E_MEXPR;
+
+ /* Remember the code position */
+ Mark = GetCodePos ();
+
+ /* Push the pointer onto the stack and remember the offset */
+ g_push (CF_PTR, 0);
+ PtrOffs = oursp;
+ }
+
+ /* Check for known standard functions and inline them if requested */
} else if (InlineStdFuncs && IsStdFunc ((const char*) lval->Name)) {
- /* Inline this function */
+
+ /* Inline this function */
HandleStdFunc (lval);
return;
+
}
/* Parse the parameter list */
ParamSize = FunctionParamList (Func);
- /* We need the closing bracket here */
+ /* We need the closing paren here */
ConsumeRParen ();
- /* */
- if (lval->Flags & E_MEXPR) {
- /* Function called via pointer: Restore it and call function */
- if (ParamSize != 0) {
- g_restore (CF_PTR);
- } else {
- /* We had no parameters - remove save code */
- RemoveCode (Mark);
+ /* Special handling for function pointers */
+ if (IsFuncPtr) {
+
+ /* If the function is not a fastcall function, load the pointer to
+ * the function into the primary.
+ */
+ if (!IsFastCallFunc (lval->Type)) {
+
+ /* Not a fastcall function - we may use the primary */
+ if (PtrOnStack) {
+ /* If we have no parameters, the pointer is still in the
+ * primary. Remove the code to push it and correct the
+ * stack pointer.
+ */
+ if (ParamSize == 0) {
+ RemoveCode (Mark);
+ pop (CF_PTR);
+ PtrOnStack = 0;
+ } else {
+ /* Load from the saved copy */
+ g_getlocal (CF_PTR, PtrOffs);
+ }
+ } else {
+ /* Load from original location */
+ exprhs (CF_NONE, k, lval);
+ }
+
+ /* Call the function */
+ g_callind (TypeOf (lval->Type), ParamSize);
+
+ /* If we have a pointer on stack, remove it */
+ if (PtrOnStack) {
+ g_space (- (int) sizeofarg (CF_PTR));
+ pop (CF_PTR);
+ }
+
+ } else {
+
+ /* Fastcall function. We cannot use the primary for the function
+ * pointer and must therefore use an offset to the stack location.
+ * Since fastcall functions may never be variadic, we can use the
+ * index register for this purpose.
+ */
+ Error ("Not implemented");
+ pop (CF_PTR);
}
- g_callind (TypeOf (lval->Type), ParamSize);
+
+ /* Skip T_PTR */
+ ++lval->Type;
+
} else {
+
+ /* Normal function */
g_call (TypeOf (lval->Type), (const char*) lval->Name, ParamSize);
+
}
}
/* Check for legal symbol types */
if ((Sym->Flags & SC_CONST) == SC_CONST) {
- /* Enum or some other numeric constant */
+ /* Enum or some other numeric constant */
lval->Flags = E_MCONST;
lval->ConstVal = Sym->V.ConstVal;
return 0;
lval->ConstVal = 0;
} else if ((Sym->Flags & SC_AUTO) == SC_AUTO) {
/* Local variable. If this is a parameter for a variadic
- * function, we have to add some address calculations, and the
- * address is not const.
- */
+ * function, we have to add some address calculations, and the
+ * address is not const.
+ */
if ((Sym->Flags & SC_PARAM) == SC_PARAM && IsVariadic (CurrentFunc)) {
- /* Variadic parameter */
- g_leavariadic (Sym->V.Offs - GetParamSize (CurrentFunc));
- lval->Flags = E_MEXPR;
- lval->ConstVal = 0;
- } else {
- /* Normal parameter */
- lval->Flags = E_MLOCAL | E_TLOFFS;
- lval->ConstVal = Sym->V.Offs;
- }
+ /* Variadic parameter */
+ g_leavariadic (Sym->V.Offs - GetParamSize (CurrentFunc));
+ lval->Flags = E_MEXPR;
+ lval->ConstVal = 0;
+ } else {
+ /* Normal parameter */
+ lval->Flags = E_MLOCAL | E_TLOFFS;
+ lval->ConstVal = Sym->V.Offs;
+ }
} else if ((Sym->Flags & SC_STATIC) == SC_STATIC) {
/* Static variable */
if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) {
/* Function call. Skip the opening parenthesis */
NextToken ();
tptr = lval->Type;
- if (IsTypeFunc (tptr) || IsTypeFuncPtr (tptr)) {
- if (IsTypeFuncPtr (tptr)) {
- /* Pointer to function. Handle transparently */
- exprhs (CF_NONE, k, lval); /* Function pointer to A/X */
- ++lval->Type; /* Skip T_PTR */
- lval->Flags |= E_MEXPR;
- }
- FunctionCall (lval);
+ if (IsTypeFunc (lval->Type) || IsTypeFuncPtr (lval->Type)) {
+
+ /* Call the function */
+ FunctionCall (k, lval);
+
+ /* Result is in the primary register */
lval->Flags = E_MEXPR;
- lval->Type += DECODE_SIZE + 1; /* Set to result */
+
+ /* Set to result */
+ lval->Type = GetFuncReturn (lval->Type);
+
} else {
Error ("Illegal function call");
}