#include <stdarg.h>
+#include <stdio.h>
#include <string.h>
#include <ctype.h>
+/* common */
+#include "attrib.h"
+#include "xmalloc.h"
+
+/* cc65 */
#include "asmlabel.h"
#include "asmline.h"
#include "check.h"
+#include "cpu.h"
#include "error.h"
#include "global.h"
-#include "io.h"
-#include "mem.h"
#include "optimize.h"
{ "\tcmp\t", 0, REG_A, REG_NONE },
{ "\tcpx\t", 0, REG_X, REG_NONE },
{ "\tcpy\t", 0, REG_Y, REG_NONE },
+ { "\tdea", 1, REG_A, REG_NONE },
+ { "\tdec\ta", 1, REG_A, REG_NONE },
{ "\tdec\t", 0, REG_NONE, REG_NONE },
{ "\tdex", 1, REG_X, REG_NONE },
{ "\tdey", 1, REG_Y, REG_NONE },
{ "\teor\t", 0, REG_A, REG_NONE },
+ { "\tina", 1, REG_A, REG_NONE },
+ { "\tinc\ta", 1, REG_A, REG_NONE },
{ "\tinc\t", 0, REG_NONE, REG_NONE },
{ "\tinx", 1, REG_X, REG_NONE },
{ "\tiny", 1, REG_Y, REG_NONE },
{ "\tsta\t", 0, REG_A, REG_NONE },
{ "\tstx\t", 0, REG_X, REG_NONE },
{ "\tsty\t", 0, REG_Y, REG_NONE },
+ { "\tstz\t", 0, REG_NONE, REG_NONE },
{ "\ttax", 1, REG_A, REG_X },
{ "\ttay", 1, REG_A, REG_Y },
{ "\ttsx", 1, REG_NONE, REG_X },
/*****************************************************************************/
-/* Forwards */
+/* Forwards */
/*****************************************************************************/
static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
/* Subfunction for RegValUsed. Will be called recursively in case of branches. */
+static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) attribute ((format(printf,2,3)));
+/* Create a new line, insert it after L and return it. The new line is marked
+ * as code line.
+ */
+
+static Line* ReplaceLine (Line* L, const char* Format, ...)
+ attribute ((format(printf,2,3)));
+/* Replace one line by another */
+
/*****************************************************************************/
-/* List stuff */
+/* List stuff */
/*****************************************************************************/
-static int IsXIndAddrMode (Line* L)
+static int IsXAddrMode (Line* L)
/* Return true if the given line does use the X register */
{
unsigned Len = strlen (L->Line);
-static int NoXIndAddrMode (Line* L)
+static int NoXAddrMode (Line* L)
/* Return true if the given line does use the X register */
{
- return !IsXIndAddrMode (L);
+ return !IsXAddrMode (L);
}
-static int IsYIndAddrMode (Line* L)
+static int IsYAddrMode (Line* L)
/* Return true if the given line does use the Y register */
{
unsigned Len = strlen (L->Line);
static unsigned EstimateSize (Line* L)
/* Estimate the size of an instruction */
{
- static const char* Transfers [] = {
+ static const char* OneByteCmds [] = {
+ "\tdea",
+ "\tdex",
+ "\tdey",
+ "\tina",
+ "\tinx",
+ "\tiny"
"\ttax",
"\ttay",
"\ttsx",
if (LineMatchX (L, LongBranches) >= 0) {
return 5;
}
- if (LineMatchX (L, Transfers) >= 0) {
+ if (LineMatchX (L, OneByteCmds) >= 0) {
return 1;
}
return 3;
do {
/* Handle jumps to local labels (continue there) */
- if (LineMatch (L, "\tjmp\tL")) {
+ if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
/* Get the target of the jump */
L = GetTargetLine (L->Line+5);
}
/* Bail out if we're done */
if (L == 0 || IsLabel (L)) {
- /* Something is wrong */
- return REG_ALL;
+ /* Something is wrong */
+ return REG_ALL;
}
/* Check if we had this line already. If so, bail out, if not,
* add it to the list of known lines.
*/
if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
- goto ExitPoint;
+ goto ExitPoint;
}
- } while (LineMatch (L, "\tjmp\tL"));
+ } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
/* Special handling for branches */
if (LineMatchX (L, ShortBranches) >= 0 ||
LineMatchX (L, LongBranches) >= 0) {
const char* Target = L->Line+5;
if (Target[0] == 'L') {
- /* Jump to local label. Check the register usage starting at
- * the branch target and at the code following the branch.
- * All registers that are unused in both execution flows are
- * returned as unused.
- */
- unsigned U1, U2;
+ /* Jump to local label. Check the register usage starting at
+ * the branch target and at the code following the branch.
+ * All registers that are unused in both execution flows are
+ * returned as unused.
+ */
+ unsigned U1, U2;
U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
- U1 = RVUInt1 (L, LC, Used, Unused);
- return U1 | U2; /* Used in any of the branches */
+ U1 = RVUInt1 (L, LC, Used, Unused);
+ return U1 | U2; /* Used in any of the branches */
}
}
/* Evaluate the use flags, check for addressing modes */
R = CmdDesc[I].Use;
- if (IsXIndAddrMode (L)) {
+ if (IsXAddrMode (L)) {
R |= REG_X;
- } else if (IsYIndAddrMode (L)) {
+ } else if (IsYAddrMode (L)) {
R |= REG_Y;
}
if (R) {
(Cond = TosCmpFunc (L2[4])) >= 0) {
/* Replace it */
- if (IsXIndAddrMode (L2[0])) {
+ if (IsXAddrMode (L2[0])) {
/* The load is X indirect, so we may not remove the load
* of the X register.
*/
Offs = GetHexNum (L2[2]->Line+7) - 2;
/* Replace it */
- if (IsXIndAddrMode (L2[0])) {
+ if (IsXAddrMode (L2[0])) {
/* The load is X indirect, so we may not remove the load
* of the X register.
*/
LineFullMatch (L2 [1], "\tjsr\tpushax")) {
/* Be sure, X is not used in the load */
- if (NoXIndAddrMode (L2 [0])) {
+ if (NoXAddrMode (L2 [0])) {
/* Replace the subroutine call */
L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
LineMatch (L2 [1], "\tcmp\t#$")) {
/* Be sure, X is not used in the load */
- if (NoXIndAddrMode (L2 [0])) {
+ if (NoXAddrMode (L2 [0])) {
/* Remove the unnecessary load */
FreeLine (L);
LineFullMatch (L2 [1], "\tjsr\tbnega")) {
/* Be sure, X is not used in the load */
- if (NoXIndAddrMode (L2 [0])) {
+ if (NoXAddrMode (L2 [0])) {
/* Remove the unnecessary load */
FreeLine (L);
/* Search for a load of Y and check if the value is used later */
else if (LineMatch (L, "\tldy\t") &&
- !RegYUsed (L) &&
+ !RegYUsed (L) &&
!IsCondJump (NextInstruction (L))) {
/* Remember to delete this line */
/* Cannot get lines */
return 0;
}
- if (LineFullMatch (L2[3], "\tclc") &&
- LineMatch (L2[4], "\tadc\t#$") &&
+ if (LineFullMatch (L2[3], "\tclc") &&
+ LineMatch (L2[4], "\tadc\t#$") &&
LineFullMatch (L2[5], "\tbcc\t*+3") &&
LineFullMatch (L2[6], "\tinx")) {
/* Inlined increment */
}
/* Check for the remainder */
- if (!LineMatch (L3[0], "\tsta\t") ||
+ if (!LineMatch (L3[0], "\tsta\t") ||
strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
- !LineMatch (L3[1], "\tstx\t") ||
+ !LineMatch (L3[1], "\tstx\t") ||
strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
!LineFullMatch (L3[2], "\tlda\tregsave") ||
!LineFullMatch (L3[3], "\tldx\tregsave+1")) {
}
/* Check if AX is actually used following the code above. If not,
- * we don't need to load A/X from regsave. Since X will never by
+ * we don't need to load A/X from regsave. Since X will never be
* used without A, check just for A.
*/
- NeedLoad = 1;
- if (!RegAUsed (L3[3])) {
- /* We don't need to load regsave */
- NeedLoad = 0;
- }
+ NeedLoad = RegAUsed (L3[3]);
/* Special code for register variables */
Done = 0;
- if (LineMatch (L, "\tlda\tregbank+") &&
+ if (LineMatch (L, "\tlda\tregbank+") &&
GetNextCodeLines (L3[3], &L3[4], 1) &&
Inc == 1) {
/* If we need to load a/x, add the code */
if (NeedLoad) {
- L = NewLineAfter (L, "\ttax");
L = NewLineAfter (L, "\tlda\tptr1");
+ L = NewLineAfter (L, "\tldx\tptr1+1");
}
}
* we don't need to load A/X from regsave. Since X will never by
* used without A, check just for A.
*/
- NeedLoad = 1;
- if (!RegAUsed (L3[0])) {
- /* We don't need to load regsave */
- NeedLoad = 0;
- }
+ NeedLoad = RegAUsed (L3[0]);
/* Replace the ldy instruction, offset must point to the low byte */
sprintf (L->Line+7, "%02X", Offs);
/* If we need to load a/x, add the code */
if (NeedLoad) {
- L = NewLineAfter (L, "\ttax");
L = NewLineAfter (L, "\tlda\tptr1");
+ L = NewLineAfter (L, "\tldx\tptr1+1");
}
/* Remove the code that is no longer needed */
}
}
} else if (LineMatch (L, "\tadc\t")) {
+ if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
+ L->Line[strlen(L->Line)-2] = '\0';
+ }
A = -1;
} else if (LineMatch (L, "\tand\t")) {
A = -1;
if (A != -1) {
A = (A << 1) & 0xFF;
}
+ } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
+ L->Line[strlen(L->Line)-2] = '\0';
+ } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
+ LineFullMatch (L, "\tdec\ta"))) {
+ DEC (A, 1);
} else if (LineFullMatch (L, "\tdex")) {
DEC (X, 1);
} else if (LineFullMatch (L, "\tdey")) {
DEC (Y, 1);
} else if (LineMatch (L, "\teor")) {
A = -1;
+ } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
+ LineFullMatch (L, "\tinc\ta"))) {
+ INC (A, 1);
} else if (LineFullMatch (L, "\tinx")) {
INC (X, 1);
} else if (LineFullMatch (L, "\tiny")) {
} else if (LineFullMatch (L, "\tjsr\tpushax")) {
/* We know about this function */
Y = 1;
+ } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
+ /* We know about this function */
+ A = -1;
+ Y = 0;
} else if (LineFullMatch (L, "\tjsr\tpushc0")) {
/* We know about this function */
A = 0;
/* The value loaded is not used later, remove it */
Delete = 1;
} else if (LineMatch (L, "\tlda\t(")) {
- if (IsXIndAddrMode (L)) {
+ if (IsXAddrMode (L)) {
/* lda (zp,x) - if Y and X are both zero, replace by
* load indirect y and save one cycle in some cases.
*/
} else if (NewVal == Y) {
/* Requested value is already in Y */
L = ReplaceLine (L, "\ttya");
+ } else if (CPU == CPU_65C02 && A != -1) {
+ /* Try ina/dea operators of 65C02 */
+ if (NewVal == ((A - 1) & 0xFF)) {
+ L = ReplaceLine (L, "\tdea");
+ } else if (NewVal == ((A + 1) & 0xFF)) {
+ L = ReplaceLine (L, "\tina");
+ }
}
/* Anyway, the new value is now in A */
A = NewVal;
} else if (LineFullMatch (L, "\trti")) {
A = X = Y = -1;
} else if (LineMatch (L, "\tsbc\t")) {
+ if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
+ L->Line[strlen(L->Line)-2] = '\0';
+ }
A = -1;
+ } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
+ /* Try to replace by stz if possible */
+ if (A == 0 && LineMatch (L, "\tsta\t")) {
+ /* Not indirect and not Y allowed */
+ if (L->Line[5] != '(' && !IsYAddrMode (L)) {
+ L->Line[3] = 'z';
+ }
+ } else if (X == 0 && LineMatch (L, "\tstx\t")) {
+ /* absolute,y not allowed */
+ if (!IsYAddrMode (L)) {
+ L->Line[3] = 'z';
+ }
+ } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
+ /* sty and stz share all addressing modes */
+ L->Line[3] = 'z';
+ }
} else if (LineFullMatch (L, "\ttax")) {
if (A != -1 && X == A) {
/* Load has no effect */
}
L = NextCodeLine (L);
}
+
+ /* Special treatment for jumps on the 65C02 */
+ if (CPU == CPU_65C02) {
+
+ Line* L = FirstCode;
+ while (L) {
+ if (LineMatch (L, "\tjmp\tL")) {
+ Line* Target = GetTargetLine (L->Line+5);
+ unsigned Distance = GetJumpDistance (L, Target);
+ if (Distance < 123) { /* Safety */
+ L->Line [1] = 'b'; /* Make a short branch */
+ L->Line [2] = 'r';
+ L->Line [3] = 'a';
+ L->Size = 2; /* Set new size */
+ }
+ }
+ L = NextCodeLine (L);
+ }
+
+ }
}