Files
cc65/src/cc65/optimize.c
cuz 8f1688015b Removed a duplicate line
git-svn-id: svn://svn.cc65.org/cc65/trunk@60 b7a2c559-68d2-44c3-8de9-860c34a00d81
2000-06-12 09:29:13 +00:00

4272 lines
110 KiB
C

/*****************************************************************************/
/* */
/* optimize.c */
/* */
/* An optimizer for the cc65 C compiler */
/* */
/* */
/* */
/* (C) 1998 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "asmlabel.h"
#include "asmline.h"
#include "check.h"
#include "error.h"
#include "global.h"
#include "io.h"
#include "mem.h"
#include "optimize.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Bitset of flags that switch the different optimizer passes */
unsigned long OptDisable = 0;
/* Bitmapped flags for the Flags field in the Line struct */
#define OF_CODE 0x0001 /* This line is in a code segment */
/* Pointer to first code line */
static Line* FirstCode;
/* Label list */
static Line** Labels = 0; /* Pointers to label lines */
static unsigned LabelCount = 0; /* Count of local labels found */
/* A collection of lines */
typedef struct LineColl_ LineColl;
struct LineColl_ {
unsigned Count; /* Count of lines in the collection */
unsigned Max; /* Maximum count of lines */
Line* Lines[1]; /* Lines, dynamically allocated */
};
/* Calculate the element count of a table */
#define COUNT(T) (sizeof (T) / sizeof (T [0]))
/* Macro to increment and decrement register contents if they're valid */
#define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
#define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
/* Defines for the conditions in a compare */
#define CMP_EQ 0
#define CMP_NE 1
#define CMP_GT 2
#define CMP_GE 3
#define CMP_LT 4
#define CMP_LE 5
#define CMP_UGT 6
#define CMP_UGE 7
#define CMP_ULT 8
#define CMP_ULE 9
/* Defines for registers */
#define REG_NONE 0x00
#define REG_A 0x01
#define REG_X 0x02
#define REG_Y 0x04
#define REG_AX (REG_A | REG_X)
#define REG_ALL (REG_A | REG_X | REG_Y)
/* Description of the commands */
static const struct {
const char* Insn; /* Instruction */
unsigned char FullMatch; /* Match full instuction? */
unsigned char Use; /* Registers used */
unsigned char Load; /* Registers loaded */
} CmdDesc [] = {
{ "\tadc\t", 0, REG_A, REG_NONE },
{ "\tand\t", 0, REG_A, REG_NONE },
{ "\tasl\ta", 1, REG_A, REG_NONE },
{ "\tasl\t", 0, REG_NONE, REG_NONE },
{ "\tclc", 1, REG_NONE, REG_NONE },
{ "\tcld", 1, REG_NONE, REG_NONE },
{ "\tcli", 1, REG_NONE, REG_NONE },
{ "\tcmp\t", 0, REG_A, REG_NONE },
{ "\tcpx\t", 0, REG_X, REG_NONE },
{ "\tcpy\t", 0, REG_Y, 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 },
{ "\tinc\t", 0, REG_NONE, REG_NONE },
{ "\tinx", 1, REG_X, REG_NONE },
{ "\tiny", 1, REG_Y, REG_NONE },
{ "\tjsr\tbool", 0, REG_NONE, REG_AX },
{ "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
{ "\tjsr\tdecax", 0, REG_AX, REG_AX },
{ "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
{ "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
{ "\tjsr\tpusha", 1, REG_A, REG_Y },
{ "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
{ "\tjsr\tpushax", 1, REG_AX, REG_Y },
{ "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
{ "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
{ "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
{ "\tlda\t", 0, REG_NONE, REG_A },
{ "\tldax\t", 0, REG_NONE, REG_AX },
{ "\tldx\t", 0, REG_NONE, REG_X },
{ "\tldy\t", 0, REG_NONE, REG_Y },
{ "\tlsr\ta", 1, REG_A, REG_NONE },
{ "\tlsr\t", 0, REG_NONE, REG_NONE },
{ "\tnop", 1, REG_NONE, REG_NONE },
{ "\tora\t", 0, REG_A, REG_NONE },
{ "\tpha", 1, REG_A, REG_NONE },
{ "\tphp", 1, REG_NONE, REG_NONE },
{ "\tpla", 1, REG_NONE, REG_A },
{ "\tplp", 1, REG_NONE, REG_NONE },
{ "\trol\ta", 1, REG_A, REG_A },
{ "\trol\t", 0, REG_NONE, REG_NONE },
{ "\tror\ta", 1, REG_A, REG_A },
{ "\tror\t", 0, REG_NONE, REG_NONE },
{ "\tsbc\t", 0, REG_A, REG_NONE },
{ "\tsec", 1, REG_NONE, REG_NONE },
{ "\tsed", 1, REG_NONE, REG_NONE },
{ "\tsei", 1, REG_NONE, REG_NONE },
{ "\tsta\t", 0, REG_A, REG_NONE },
{ "\tstx\t", 0, REG_X, REG_NONE },
{ "\tsty\t", 0, REG_Y, REG_NONE },
{ "\ttax", 1, REG_A, REG_X },
{ "\ttay", 1, REG_A, REG_Y },
{ "\ttsx", 1, REG_NONE, REG_X },
{ "\ttxa", 1, REG_X, REG_A },
{ "\ttya", 1, REG_Y, REG_A },
};
/* Table with the compare suffixes */
static const char CmpSuffixTab [][4] = {
"eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
};
/* Table used to invert a condition, indexed by condition */
static const unsigned char CmpInvertTab [] = {
CMP_NE, CMP_EQ,
CMP_LE, CMP_LT, CMP_GE, CMP_GT,
CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
};
/* Table to show which compares are signed (use the N flag) */
static const char CmpSignedTab [] = {
0, 0, 1, 1, 1, 1, 0, 0, 0, 0
};
/* Lists of branches */
static const char* ShortBranches [] = {
"\tbeq\t",
"\tbne\t",
"\tbpl\t",
"\tbmi\t",
"\tbcc\t",
"\tbcs\t",
"\tbvc\t",
"\tbvs\t",
0
};
static const char* LongBranches [] = {
"\tjeq\t",
"\tjne\t",
"\tjpl\t",
"\tjmi\t",
"\tjcc\t",
"\tjcs\t",
"\tjvc\t",
"\tjvs\t",
0
};
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
static unsigned EstimateSize (Line* L);
/* Estimate the size of an instruction */
static int IsLocalLabel (const Line* L);
/* Return true if the line is a local label line */
static unsigned GetLabelNum (const char* L);
/* Return the label number of a label line */
static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
/* Subfunction for RegValUsed. Will be called recursively in case of branches. */
/*****************************************************************************/
/* List stuff */
/*****************************************************************************/
static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
/* Create a new line, insert it after L and return it. The new line is marked
* as code line.
*/
{
Line* L;
/* Format the new line and add it */
va_list ap;
va_start (ap, Format);
L = NewCodeLineAfter (LineBefore, Format, ap);
va_end (ap);
/* Make the line a code line */
L->Flags = OF_CODE;
/* Estimate the code size */
L->Size = EstimateSize (L);
/* Return the new line */
return L;
}
static Line* NewLabelAfter (Line* L, unsigned Label)
/* Add a new line with a definition of a local label after the line L */
{
char Buf [32];
/* Create the label */
sprintf (Buf, "L%04X:", Label);
/* Create a new line */
L = NewLineAfter (L, Buf);
/* Insert this label into the label list */
Labels [Label] = L;
/* Return the new line */
return L;
}
static void FreeLine (Line* L)
/* Remove a line from the list and free it */
{
/* If this is a label line, remove it from the label list */
if (IsLocalLabel (L)) {
Labels [GetLabelNum (L->Line)] = 0;
}
/* Unlink the line */
FreeCodeLine (L);
}
static Line* ReplaceLine (Line* L, const char* Format, ...)
/* Replace one line by another */
{
unsigned Len;
char S [256];
/* Format the new line */
va_list ap;
va_start (ap, Format);
vsprintf (S, Format, ap);
va_end (ap);
/* Get the length of the new line */
Len = strlen (S);
/* We can copy the line if the old line has space enough */
if (Len <= L->Len) {
/* Just copy the new line, but don't update the length */
memcpy (L->Line, S, Len);
L->Line [Len] = '\0';
} else {
/* We must allocate new space */
Line* NewLine = xmalloc (sizeof (Line) + Len);
/* Set the values in the new struct */
NewLine->Flags = L->Flags;
NewLine->Index = L->Index;
NewLine->Size = L->Size; /* Hmm ... */
NewLine->Len = Len;
memcpy (NewLine->Line, S, Len + 1);
/* Replace the old struct in the list */
NewLine->Next = L->Next;
if (NewLine->Next) {
NewLine->Next->Prev = NewLine;
} else {
/* Last line */
LastLine = NewLine;
}
NewLine->Prev = L->Prev;
if (NewLine->Prev) {
NewLine->Prev->Next = NewLine;
} else {
/* First line */
FirstLine = NewLine;
}
/* Free the old struct */
xfree (L);
L = NewLine;
}
/* Estimate the new size */
if (L->Flags & OF_CODE) {
L->Size = EstimateSize (L);
}
/* Return the line */
return L;
}
static Line* PrevCodeLine (Line* L)
/* Return the previous line containing code */
{
L = L->Prev;
while (L) {
if (L->Flags & OF_CODE && L->Line [0] != '+') {
break;
}
L = L->Prev;
}
return L;
}
static Line* NextCodeSegLine (Line* L)
/* Return the next line in the code segment */
{
L = L->Next;
while (L) {
if (L->Flags & OF_CODE) {
break;
}
L = L->Next;
}
return L;
}
static Line* NextCodeLine (Line* L)
/* Return the next line containing code */
{
L = L->Next;
while (L) {
if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
break;
}
L = L->Next;
}
return L;
}
static Line* NextInstruction (Line* L)
/* Return the next line containing code, ignoring labels. */
{
do {
L = NextCodeLine (L);
} while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
return L;
}
static void FreeLines (Line* Start, Line* End)
/* Delete all lines from Start to End, both inclusive */
{
Line* L;
do {
L = Start;
Start = NextCodeSegLine (Start);
FreeLine (L);
} while (L != End);
}
/*****************************************************************************/
/* Line Collections */
/*****************************************************************************/
static LineColl* NewLineColl (unsigned Size)
/* Create a new line collection and return it */
{
/* Allocate memory */
LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
/* Initialize members */
LC->Count = 0;
LC->Max = Size;
/* Return the new collection */
return LC;
}
static void FreeLineColl (LineColl* LC)
/* Delete a line collection */
{
xfree (LC);
}
static int LCAddLine (LineColl* LC, Line* L)
/* Add a line. Return 0 if no space available, return 1 otherwise */
{
/* Check if there is enough space available */
if (LC->Count >= LC->Max) {
/* No room available */
return 0;
}
/* Add the line */
LC->Lines [LC->Count++] = L;
/* Done */
return 1;
}
static int LCHasLine (LineColl* LC, Line* L)
/* Check if the given line is in the collection */
{
unsigned I;
for (I = 0; I < LC->Count; ++I) {
if (LC->Lines[I] == L) {
return 1;
}
}
return 0;
}
/*****************************************************************************/
/* Test a line for several things */
/*****************************************************************************/
static int IsLocalLabel (const Line* L)
/* Return true if the line is a local label line */
{
return (L->Line [0] == 'L' && isxdigit (L->Line [1]));
}
static int IsLabel (const Line* L)
/* Return true if the line is a label line */
{
return (L->Line [0] == 'L' && isxdigit (L->Line [1])) ||
(L->Line [0] == '_');;
}
static int IsHintLine (const Line* L)
/* Return true if the line contains an optimizer hint */
{
return L->Line [0] == '+';
}
static int IsSegHint (const Line* L)
/* Return true if the given line contains a segment hint */
{
return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
}
static int IsHint (const Line* L, const char* Hint)
/* Check if the line contains a given hint */
{
return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
}
static int IsCondJump (Line* L)
/* Return true if the line contains a conditional jump */
{
return (L->Line [0] == '\t' &&
(strncmp (L->Line + 1, "beq\t", 4) == 0 ||
strncmp (L->Line + 1, "bne\t", 4) == 0 ||
strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
strncmp (L->Line + 1, "jne\t", 4) == 0));
}
static int IsXIndAddrMode (Line* L)
/* Return true if the given line does use the X register */
{
unsigned Len = strlen (L->Line);
return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
strcmp (L->Line + Len - 2, ",x") == 0);
}
static int NoXIndAddrMode (Line* L)
/* Return true if the given line does use the X register */
{
return !IsXIndAddrMode (L);
}
static int IsYIndAddrMode (Line* L)
/* Return true if the given line does use the Y register */
{
unsigned Len = strlen (L->Line);
return (strcmp (L->Line + Len - 2, ",y") == 0);
}
static Line* FindHint (Line* L, const char* Hint)
/* Search for a line with the given hint */
{
while (L) {
if (IsHint (L, Hint)) {
break;
}
L = L->Next;
}
return L;
}
static unsigned GetHexNum (const char* S)
/* Get a hex number from a string */
{
unsigned I = 0;
unsigned Val = 0;
while (isxdigit (S [I])) {
int C = (unsigned char) (S [I++]);
if (C >= 'A') {
C -= 'A' - 10;
} else {
C -= '0';
}
Val = (Val << 4) + C;
}
return Val;
}
static unsigned GetLabelNum (const char* L)
/* Return the label number of a label line */
{
CHECK (*L == 'L');
return GetHexNum (L+1);
}
static Line* GetTargetLine (const char* L)
/* Get the line with the target label of a jump. L must be a pointer to the
* string containing the label number.
*/
{
Line* Target;
/* Get the label number of the target */
unsigned Label = GetLabelNum (L);
CHECK (Label < LabelCount);
/* Get the line with this label */
Target = Labels [Label];
CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
/* And return it */
return Target;
}
static unsigned GetJumpDistance (Line* L, Line* Target)
/* Get the distance between both lines */
{
unsigned Distance = 0;
if (L != Target) {
if (Target->Index > L->Index) {
/* This is a forward jump. */
do {
L = NextCodeLine (L);
Distance += L->Size;
} while (L != Target);
} else {
/* This is a backward jump */
do {
L = PrevCodeLine (L);
Distance += L->Size;
} while (L != Target);
}
}
/* Return the calculated distance */
return Distance;
}
static int LineMatch (const Line* L, const char* Start)
/* Check if the start of the line matches Start */
{
return strncmp (L->Line, Start, strlen (Start)) == 0;
}
static int LineFullMatch (const Line* L, const char* Start)
/* Check if the matches Start */
{
return strcmp (L->Line, Start) == 0;
}
static int LineMatchX (const Line* L, const char** Start)
/* Check the start of the line against a list of patterns. Return the
* number of the pattern that matched, or -1 in case of no match.
*/
{
unsigned I = 0;
while (*Start) {
if (LineMatch (L, *Start)) {
/* Found */
return I;
}
++Start;
++I;
}
/* Not found */
return -1;
}
static int LineFullMatchX (const Line* L, const char** Start)
/* Check the the line against a list of patterns. Return the
* number of the pattern that matched, or -1 in case of no match.
*/
{
unsigned I = 0;
while (*Start) {
if (LineFullMatch (L, *Start)) {
/* Found */
return I;
}
++Start;
++I;
}
/* Not found */
return -1;
}
static int IsLoadAX (Line* L1, Line* L2)
/* Check if the both lines load a static variable into ax. That is, both lines
* look like
* lda x+0
* ldx x+0+1
*/
{
return LineMatch (L1, "\tlda\t") &&
LineMatch (L2, "\tldx\t") &&
strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
}
/*****************************************************************************/
/* Initial optimizer setup */
/*****************************************************************************/
static void FindCodeStart (void)
/* Find and remember the first line of actual code */
{
Line* L = FindHint (FirstLine, "end_of_preamble");
FirstCode = L? L->Next : 0;
}
static unsigned EstimateDataSize (Line* L, unsigned Chunk)
/* Estimate the size of a .byte, .word or .dword command */
{
unsigned Size = Chunk;
char* S = L->Line;
while ((S = strchr (S, ',')) != 0) {
Size += Chunk;
++S;
}
return Size;
}
static unsigned EstimateSize (Line* L)
/* Estimate the size of an instruction */
{
static const char* Transfers [] = {
"\ttax",
"\ttay",
"\ttsx",
"\ttxa",
"\ttya",
0
};
char OpStart;
if (L->Line [0] != '\t') {
return 0;
}
if (LineMatch (L, "\tldax\t")) {
/* Immidiate load of both, A and X */
return 4;
}
if (LineMatch (L, "\tld")) {
OpStart = L->Line [5];
return (OpStart == '#' || OpStart == '(')? 2 : 3;
}
if (LineMatch (L, "\tst")) {
OpStart = L->Line [5];
return (OpStart == '(')? 2 : 3;
}
if (LineMatch (L, "\t.byte\t")) {
return EstimateDataSize (L, 1);
}
if (LineMatch (L, "\t.word\t")) {
return EstimateDataSize (L, 2);
}
if (LineMatch (L, "\t.dword\t")) {
return EstimateDataSize (L, 4);
}
if (LineMatchX (L, ShortBranches) >= 0) {
return 2;
}
if (LineMatchX (L, LongBranches) >= 0) {
return 5;
}
if (LineMatchX (L, Transfers) >= 0) {
return 1;
}
return 3;
}
static void MarkCodeLines (void)
/* Mark all lines that are inside a code segment */
{
int InCode = 1;
Line* L = FirstCode;
while (L) {
if (IsSegHint (L)) {
InCode = IsHint (L, "seg:code");
} else if (InCode && L->Line[0] != '\0') {
L->Flags |= OF_CODE;
L->Size = EstimateSize (L);
}
L = L->Next;
}
}
static void CreateLabelList (void)
/* Create a list with pointers to local labels */
{
unsigned I;
Line* L;
/* Get the next label number. This is also the current label count.
* Make some room for more labels when optimizing code.
*/
LabelCount = GetLabel () + 100;
/* Allocate memory for the array and clear it */
Labels = xmalloc (LabelCount * sizeof (Line*));
for (I = 0; I < LabelCount; ++I) {
Labels [I] = 0;
}
/* Walk through the code and insert all label lines */
L = FirstLine;
while (L) {
if (IsLocalLabel (L)) {
unsigned LabelNum = GetLabelNum (L->Line);
CHECK (LabelNum < LabelCount);
Labels [LabelNum] = L;
}
L = L->Next;
}
}
static unsigned AllocLabel (void)
/* Get a new label. The current code does not realloc the label list, so there
* must be room enough in the current list.
*/
{
unsigned I;
/* Search for a free slot, start at 1, since 0 is "no label" */
for (I = 1; I < LabelCount; ++I) {
if (Labels[I] == 0) {
/* Found a free slot */
return I;
}
}
/* No label space available */
Internal ("Out of label space in the optimizer");
/* Not reached */
return 0;
}
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
/* Get a number of code lines ignoring hints and other stuff. The function
* returns 1 if we got the lines and 0 if we are at the end of the code
* segment or if we hit a label.
*/
{
while (Count--) {
/* Get the next valid line */
do {
L = NextCodeLine (L);
} while (L && IsHintLine (L));
/* Did we get one? */
if (L == 0 || IsLabel (L)) {
/* Error */
return 0;
}
/* Remember the line */
*Lines++ = L;
}
/* Success */
return 1;
}
static int FindCond (const char* Suffix)
/* Map a condition suffix to a code. Return the code or -1 on failure */
{
int I;
/* Linear search */
for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
/* Found */
return I;
}
}
/* Not found */
return -1;
}
static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
/* Helper function to check for a compare subroutine call followed by a
* conditional branch. Will return the condition found, or -1 if no
* or invalid condition.
*/
{
char Cond[5];
const char* Tail;
int C;
/* Extract the condition from the function name. */
if ((Cond [0] = JSR->Line [8]) == 'u') {
Cond [1] = JSR->Line [9];
Cond [2] = JSR->Line [10];
Cond [3] = '\0';
Tail = JSR->Line + 11;
} else {
Cond [1] = JSR->Line [9];
Cond [2] = '\0';
Tail = JSR->Line + 10;
}
/* Check if this is indeed an integer function */
if (strcmp (Tail, "ax") != 0) {
/* No! */
return -1;
}
/* Get the condition code */
C = FindCond (Cond);
if (C < 0) {
/* OOPS! */
return -1;
}
/* Invert the code if we jump on condition not met. */
if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
/* Jumps if condition false, invert condition */
C = CmpInvertTab [C];
}
/* Return the condition code */
return C;
}
static int TosCmpFunc (Line* L)
/* Check if this is a call to one of the TOS compare functions (tosgtax).
* Return the condition code or -1 if not found.
*/
{
if (LineMatch (L, "\tjsr\ttos") &&
strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
/* Ok, found. Get the condition. */
return FindCond (L->Line+8);
} else {
/* Not found */
return -1;
}
}
static int IsUnsignedCmp (int Code)
/* Check if this is an unsigned compare */
{
CHECK (Code >= 0);
return CmpSignedTab [Code] == 0;
}
static void InvertZJump (Line* L)
/* Invert a jeq/jne jump */
{
if (L->Line [2] == 'n' && L->Line [3] == 'e') {
/* This was a bne/jne */
L->Line [2] = 'e';
L->Line [3] = 'q';
} else {
/* This was (hopefully) a beq/jeq */
L->Line [2] = 'n';
L->Line [3] = 'e';
}
}
static int FindCmd (Line* L)
{
int I;
/* Search for the known patterns */
for (I = 0; I < COUNT(CmdDesc); ++I) {
if (CmdDesc[I].FullMatch) {
if (LineFullMatch (L, CmdDesc[I].Insn)) {
/* found */
return I;
}
} else {
if (LineMatch (L, CmdDesc[I].Insn)) {
/* found */
return I;
}
}
}
/* Not found */
return -1;
}
static unsigned RVUInt2 (Line* L,
LineColl* LC, /* To remember visited lines */
unsigned Used, /* Definitely used registers */
unsigned Unused) /* Definitely unused registers */
/* Subfunction for RegValUsed. Will be called recursively in case of branches. */
{
int I;
/* Check the following instructions. We classifiy them into primary
* loads (register value not used), neutral (check next instruction),
* and unknown (assume register was used).
*/
while (1) {
unsigned R;
/* Get the next line and follow jumps */
do {
/* Handle jumps to local labels (continue there) */
if (LineMatch (L, "\tjmp\tL")) {
/* Get the target of the jump */
L = GetTargetLine (L->Line+5);
}
/* Get the next instruction line */
L = NextInstruction (L);
/* Bail out if we're done */
if (L == 0 || IsLabel (L)) {
/* 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;
}
} while (LineMatch (L, "\tjmp\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;
U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
U1 = RVUInt1 (L, LC, Used, Unused);
return U1 | U2; /* Used in any of the branches */
}
}
/* Search for the instruction in this line */
I = FindCmd (L);
/* If we don't find it, assume all other registers are */
if (I < 0) {
break;
}
/* Evaluate the use flags, check for addressing modes */
R = CmdDesc[I].Use;
if (IsXIndAddrMode (L)) {
R |= REG_X;
} else if (IsYIndAddrMode (L)) {
R |= REG_Y;
}
if (R) {
/* Remove registers that were already new loaded */
R &= ~Unused;
/* Remember the remaining registers */
Used |= R;
}
/* Evaluate the load flags */
R = CmdDesc[I].Load;
if (R) {
/* Remove registers that were already used */
R &= ~Used;
/* Remember the remaining registers */
Unused |= R;
}
/* If we know about all registers, bail out */
if ((Used | Unused) == REG_ALL) {
break;
}
}
ExitPoint:
/* Return to the caller the complement of all unused registers */
return ~Unused & REG_ALL;
}
static unsigned RVUInt1 (Line* L,
LineColl* LC, /* To remember visited lines */
unsigned Used, /* Definitely used registers */
unsigned Unused) /* Definitely unused registers */
/* Subfunction for RegValUsed. Will be called recursively in case of branches. */
{
/* Remember the current count of the line collection */
unsigned Count = LC->Count;
/* Call the worker routine */
unsigned R = RVUInt2 (L, LC, Used, Unused);
/* Restore the old count */
LC->Count = Count;
/* Return the result */
return R;
}
static unsigned RegValUsed (Line* Start)
/* Check the next instructions after the one in L for register usage. If
* a register is used as an index, or in a store or other instruction, it
* is assumed to be used. If a register is loaded with a value, before it
* was used by one of the actions described above, it is assumed unused.
* If the end of the lookahead is reached, all registers that are uncertain
* are marked as used.
* The result of the search is returned.
*/
{
unsigned R;
/* Create a new line collection and enter the start line */
LineColl* LC = NewLineColl (256);
LCAddLine (LC, Start);
/* Call the recursive subfunction */
R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
/* Delete the line collection */
FreeLineColl (LC);
/* Return the registers used */
return R;
}
static int RegAUsed (Line* Start)
/* Check if the value in A is used. */
{
return (RegValUsed (Start) & REG_A) != 0;
}
static int RegXUsed (Line* Start)
/* Check if the value in X is used. */
{
return (RegValUsed (Start) & REG_X) != 0;
}
static int RegYUsed (Line* Start)
/* Check if the value in Y is used. */
{
return (RegValUsed (Start) & REG_Y) != 0;
}
/*****************************************************************************/
/* Real optimizer routines */
/*****************************************************************************/
static void OptCompares1 (void)
/* Try to optimize the integer compare subroutines. */
{
Line* L2[10]; /* Line lookahead */
int Cond; /* Condition to evaluate */
unsigned Label; /* Local label number */
unsigned Offs; /* Stack offset */
Line* DelStart; /* First line to delete */
Line* L = FirstCode;
while (L) {
/* Search for compares of local byte sized variables. This looks
* like:
*
* ldy #$xx
* ldx #$00
* lda (sp),y
* jsr pushax
* ldy #$yy
* ldx #$00
* lda (sp),y
* jsr tosugtax
*
* Replace it by a direct compare:
*
* ldy #$xx
* lda (sp),y
* ldy #$yy
* cmp (sp),y
* jsr boolugt
*/
if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 7) &&
LineFullMatch (L2[0], "\tldx\t#$00") &&
LineFullMatch (L2[1], "\tlda\t(sp),y") &&
LineFullMatch (L2[2], "\tjsr\tpushax") &&
LineMatch (L2[3], "\tldy\t#$") &&
LineFullMatch (L2[4], "\tldx\t#$00") &&
LineFullMatch (L2[5], "\tlda\t(sp),y") &&
(Cond = TosCmpFunc (L2[6])) >= 0) {
/* Get the stack offset and correct it, since we will remove
* the pushax.
*/
Offs = GetHexNum (L2[3]->Line+7) - 2;
/* Replace it */
L = NewLineAfter (L, "\tlda\t(sp),y");
L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
L = NewLineAfter (L, "\tcmp\t(sp),y");
L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
/* Remove the old cruft */
FreeLines (L2[0], L2[6]);
}
/* Compares of byte sized global variables */
else if (LineFullMatch (L, "\tldx\t#$00") &&
GetNextCodeLines (L, L2, 5) &&
LineMatch (L2[0], "\tlda\t") &&
LineFullMatch (L2[1], "\tjsr\tpushax") &&
LineFullMatch (L2[2], "\tldx\t#$00") &&
LineMatch (L2[3], "\tlda\t") &&
(Cond = TosCmpFunc (L2[4])) >= 0) {
/* Replace it */
if (IsXIndAddrMode (L2[0])) {
/* The load is X indirect, so we may not remove the load
* of the X register.
*/
L = L2[0];
DelStart = L2[1];
} else {
L = ReplaceLine (L, L2[0]->Line);
DelStart = L2[0];
}
L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
/* Remove the old cruft */
FreeLines (DelStart, L2[4]);
}
/* Byte sized local to global */
else if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 6) &&
LineFullMatch (L2[0], "\tldx\t#$00") &&
LineFullMatch (L2[1], "\tlda\t(sp),y") &&
LineFullMatch (L2[2], "\tjsr\tpushax") &&
LineFullMatch (L2[3], "\tldx\t#$00") &&
LineMatch (L2[4], "\tlda\t") &&
(Cond = TosCmpFunc (L2[5])) >= 0) {
/* Replace it */
L = NewLineAfter (L, L2[1]->Line);
L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
/* Remove the old cruft */
FreeLines (L2[0], L2[5]);
}
/* Byte sized global to local */
else if (LineFullMatch (L, "\tldx\t#$00") &&
GetNextCodeLines (L, L2, 6) &&
LineMatch (L2[0], "\tlda\t") &&
LineFullMatch (L2[1], "\tjsr\tpushax") &&
LineMatch (L2[2], "\tldy\t#$") &&
LineFullMatch (L2[3], "\tldx\t#$00") &&
LineFullMatch (L2[4], "\tlda\t(sp),y") &&
(Cond = TosCmpFunc (L2[5])) >= 0) {
/* Get the stack offset and correct it, since we will remove
* the pushax.
*/
Offs = GetHexNum (L2[2]->Line+7) - 2;
/* Replace it */
if (IsXIndAddrMode (L2[0])) {
/* The load is X indirect, so we may not remove the load
* of the X register.
*/
L = L2[0];
DelStart = L2[1];
} else {
L = ReplaceLine (L, L2[0]->Line);
DelStart = L2[0];
}
L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
L = NewLineAfter (L, "\tcmp\t(sp),y");
L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
/* Remove the old cruft */
FreeLines (DelStart, L2[5]);
}
/* Search for unsigned compares against global variables. This looks
* like:
*
* jsr pushax
* lda _b+0
* ldx _b+0+1
* jsr tosugtax
*
* Replace that by a direct compare:
*
* cpx _b+0+1
* bne L
* cmp _b+0
* L:
* jsr boolugt
*/
else if (LineFullMatch (L, "\tjsr\tpushax") &&
GetNextCodeLines (L, L2, 3) &&
IsLoadAX (L2[0], L2[1]) &&
(Cond = TosCmpFunc (L2[2])) >= 0 &&
IsUnsignedCmp (Cond)) {
/* Get a free label number */
Label = AllocLabel ();
/* Replace the code */
L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
L = NewLineAfter (L, "\tbne\tL%04X", Label);
L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
L = NewLabelAfter(L, Label);
L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
/* Remove the old code */
FreeLines (L2[0], L2[2]);
}
L = NextCodeLine (L);
}
}
static void OptDeadJumps (void)
/* Remove jumps to the following instruction */
{
static const char* Jumps [] = {
"\tbeq\tL",
"\tbne\tL",
"\tjeq\tL",
"\tjne\tL",
"\tjmp\tL",
0
};
Line* L = FirstCode;
while (L) {
/* Get a pointer to the next instruction line */
Line* NextLine = NextInstruction (L);
/* Is this line a jump? */
int I = LineMatchX (L, Jumps);
if (I >= 0) {
/* Yes. Get the target label, skip labels */
Line* Target = NextInstruction (GetTargetLine (L->Line+5));
/* If the target label is the next line, remove the jump */
if (Target == NextLine) {
FreeLine (L);
}
}
/* Go to the next line */
L = NextLine;
}
}
static void OptLoads (void)
/* Remove unnecessary loads of values */
{
Line* L2 [10];
Line* L = FirstCode;
while (L) {
/* Check for
*
* ldy #$..
* lda (sp),y
* tax
* dey
* lda (sp),y
* jsr pushax
*
* and replace it by
*
* ldy #$..
* jsr pushwysp
*
* or even
*
* jsr pushw0sp
*
* This change will cost 3 cycles (one additional jump inside the
* subroutine), but it saves a lot of code (6 bytes per occurrence),
* so we will accept the overhead. It may even be possible to rewrite
* the library routine to get rid of the additional overhead.
*/
if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 5) &&
LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
LineFullMatch (L2 [1], "\ttax") &&
LineFullMatch (L2 [2], "\tdey") &&
LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
LineFullMatch (L2 [4], "\tjsr\tpushax")) {
/* Found - replace it */
if (LineFullMatch (L, "\tldy\t#$01")) {
/* Word at offset zero */
FreeLine (L);
L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
} else {
ReplaceLine (L2 [4], "\tjsr\tpushwysp");
}
/* Delete the remaining lines */
FreeLines (L2 [0], L2 [3]);
}
/* Check for
*
* ldy #$xx
* lda (sp),y
* tax
* dey
* lda (sp),y
* ldy #$yy
* jsr ldauidx
*
* and replace it by
*
* ldy #$xx
* ldx #$yy
* jsr ldauiysp
*
* or even
*
* jsr ldaui0sp
*
* This change will cost 2 cycles, but it saves a lot of code (6 bytes
* per occurrence), so we will accept the overhead. It may even be
* possible to rewrite the library routine to get rid of the additional
* overhead.
*/
if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 6) &&
LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
LineFullMatch (L2 [1], "\ttax") &&
LineFullMatch (L2 [2], "\tdey") &&
LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
LineMatch (L2 [4], "\tldy\t#$") &&
LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
/* Found - replace it */
L2 [4]->Line [3] = 'x'; /* Change to ldx */
if (LineFullMatch (L, "\tldy\t#$01")) {
/* Word at offset zero */
FreeLine (L);
L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
} else {
ReplaceLine (L2 [5], "\tjsr\tldauiysp");
}
/* Delete the remaining lines */
FreeLines (L2 [0], L2 [3]);
}
/* Search for:
*
* lda (sp),y
* jsr pusha
*
* And replace by
*
* jsr pushaysp
*/
if (LineFullMatch (L, "\tlda\t(sp),y") &&
GetNextCodeLines (L, L2, 1) &&
LineFullMatch (L2 [0], "\tjsr\tpusha")) {
/* Found, replace it */
L = ReplaceLine (L, "\tjsr\tpushaysp");
FreeLine (L2 [0]);
}
/* All other patterns start with this one: */
if (!LineFullMatch (L, "\tldx\t#$00")) {
/* Next line */
goto NextLine;
}
/* Search for:
*
* ldx #$00
* jsr pushax
*
* and replace it by:
*
* jsr pusha0
*
*/
if (GetNextCodeLines (L, L2, 1) &&
LineFullMatch (L2 [0], "\tjsr\tpushax")) {
/* Replace the subroutine call */
L = ReplaceLine (L, "\tjsr\tpusha0");
/* Remove the unnecessary line */
FreeLine (L2[0]);
}
/* Search for:
*
* ldx #$00
* lda ...
* jsr pushax
*
* and replace it by:
*
* lda ...
* jsr pusha0
*
*/
else if (GetNextCodeLines (L, L2, 2) &&
LineMatch (L2 [0], "\tlda\t") &&
LineFullMatch (L2 [1], "\tjsr\tpushax")) {
/* Be sure, X is not used in the load */
if (NoXIndAddrMode (L2 [0])) {
/* Replace the subroutine call */
L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
/* Remove the unnecessary load */
FreeLine (L);
/* L must be valid */
L = L2 [0];
}
}
/* Search for:
*
* ldx #$00
* lda ...
* cmp #$..
*
* and replace it by:
*
* lda ...
* cmp #$..
*/
else if (GetNextCodeLines (L, L2, 2) &&
LineMatch (L2 [0], "\tlda\t") &&
LineMatch (L2 [1], "\tcmp\t#$")) {
/* Be sure, X is not used in the load */
if (NoXIndAddrMode (L2 [0])) {
/* Remove the unnecessary load */
FreeLine (L);
/* L must be valid */
L = L2 [0];
}
}
/* Search for:
*
* ldx #$00
* lda ...
* jsr bnega
*
* and replace it by:
*
* lda ...
* jsr bnega
*/
else if (GetNextCodeLines (L, L2, 2) &&
LineMatch (L2 [0], "\tlda\t") &&
LineFullMatch (L2 [1], "\tjsr\tbnega")) {
/* Be sure, X is not used in the load */
if (NoXIndAddrMode (L2 [0])) {
/* Remove the unnecessary load */
FreeLine (L);
/* L must be valid */
L = L2 [0];
}
}
NextLine:
/* Go to the next line */
L = NextCodeLine (L);
}
}
static void OptRegLoads (void)
/* Remove unnecessary loads of registers */
{
unsigned Deletions;
Line* L;
Line* Lx;
/* Repeat this until there is nothing more to delete */
do {
Deletions = 0;
L = FirstCode;
while (L) {
int Delete = 0;
/* Search for a load of X and check if the value is used later */
if (LineMatch (L, "\tldx\t") &&
!RegXUsed (L) &&
!IsCondJump (NextInstruction (L))) {
/* Remember to delete this line */
Delete = 1;
}
/* Search for a load of A and check if the value is used later */
else if (LineMatch (L, "\tlda\t") &&
!RegAUsed (L) &&
!IsCondJump (NextInstruction (L))) {
/* Remember to delete this line */
Delete = 1;
}
/* Search for a load of Y and check if the value is used later */
else if (LineMatch (L, "\tldy\t") &&
!RegYUsed (L) &&
!IsCondJump (NextInstruction (L))) {
/* Remember to delete this line */
Delete = 1;
}
/* Go to the next line, delete the current if requested */
Lx = L;
L = NextCodeLine (L);
if (Delete) {
FreeLine (Lx);
++Deletions;
}
}
} while (Deletions > 0);
}
static int OptPtrOps1 (Line** Start)
/* Optimize several pointer and array constructs - subfunction 1 */
{
Line* L2[15];
Line** L3;
unsigned NeedLoad;
unsigned LinesToRemove;
unsigned Inc;
unsigned Done;
unsigned Offs;
/* Use a local variable for the working line */
Line* L = *Start;
/* Search for (23B/XXT)
*
* lda _b+0
* ldx _b+0+1
* sta regsave
* stx regsave+1
* jsr incax1
* sta _b+0
* stx _b+0+1
* lda regsave
* ldx regsave+1
*
* and replace it by something like (24B/26T)
*
* lda _b+0
* sta regsave
* clc
* adc #$01
* sta _b+0
* lda _b+0+1
* sta regsave+1
* adc #$00
* sta _b+0+1
* tax
* lda regsave
*/
if (!LineMatch (L, "\tlda\t") ||
!GetNextCodeLines (L, L2, 4) ||
!IsLoadAX (L, L2 [0]) ||
!LineFullMatch (L2[1], "\tsta\tregsave") ||
!LineFullMatch (L2[2], "\tstx\tregsave+1")) {
/* Not found */
return 0;
}
/* */
if (LineMatch (L2[3], "\tjsr\tincax")) {
/* Get next code lines */
if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
/* Cannot get lines */
return 0;
}
Inc = GetHexNum (L2[3]->Line+10);
L3 = &L2[4];
LinesToRemove = 8;
} else {
/* Get next code lines */
if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
/* Cannot get lines */
return 0;
}
if (LineFullMatch (L2[3], "\tclc") &&
LineMatch (L2[4], "\tadc\t#$") &&
LineFullMatch (L2[5], "\tbcc\t*+3") &&
LineFullMatch (L2[6], "\tinx")) {
/* Inlined increment */
Inc = GetHexNum (L2[4]->Line+7);
L3 = &L2[7];
LinesToRemove = 11;
} else {
/* Not found */
return 0;
}
}
/* Check for the remainder */
if (!LineMatch (L3[0], "\tsta\t") ||
strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
!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")) {
/* Not found */
return 0;
}
/* 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
* used without A, check just for A.
*/
NeedLoad = 1;
if (!RegAUsed (L3[3])) {
/* We don't need to load regsave */
NeedLoad = 0;
}
/* Special code for register variables */
Done = 0;
if (LineMatch (L, "\tlda\tregbank+") &&
GetNextCodeLines (L3[3], &L3[4], 1) &&
Inc == 1) {
/* Remember the offset into the register bank */
char Reg[20];
strcpy (Reg, L->Line+5);
/* Check for several special sequences */
if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
/* Load char indirect */
L = ReplaceLine (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
L = NewLineAfter (L, "\tinc\t%s", Reg);
L = NewLineAfter (L, "\tbne\t*+4");
L = NewLineAfter (L, "\tinc\t%s+1", Reg);
Done = 1;
++LinesToRemove;
} else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
GetNextCodeLines (L3[4], &L3[5], 3) &&
LineFullMatch (L3[5], "\tstx\tptr1+1") &&
LineFullMatch (L3[6], "\tldx\t#$00") &&
LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
/* Load char indirect, inlined */
L = ReplaceLine (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
L = NewLineAfter (L, "\tinc\t%s", Reg);
L = NewLineAfter (L, "\tbne\t*+4");
L = NewLineAfter (L, "\tinc\t%s+1", Reg);
Done = 1;
LinesToRemove += 4;
} else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
if (GetNextCodeLines (L3[4], &L3[5], 2) &&
LineMatch (L3[5], "\tlda\t") &&
LineFullMatch (L3[6], "\tjsr\tstaspp")) {
/* Store to pointer */
L = ReplaceLine (L, L3[5]->Line);
L = NewLineAfter (L, "\tldy\t#$00");
L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
L = NewLineAfter (L, "\tinc\t%s", Reg);
L = NewLineAfter (L, "\tbne\t*+4");
L = NewLineAfter (L, "\tinc\t%s+1", Reg);
Done = 1;
LinesToRemove += 3;
} else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
LineMatch (L3[5], "\tldy\t#$") &&
LineFullMatch (L3[6], "\tlda\t(sp),y") &&
LineFullMatch (L3[7], "\tjsr\tstaspp")) {
/* Beware: We have to correct the stack offset, since we will
* remove the pushax instruction!
*/
Offs = GetHexNum (L3[5]->Line+7) - 2;
/* Store to pointer */
L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
L = NewLineAfter (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(sp),y");
L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
L = NewLineAfter (L, "\tinc\t%s", Reg);
L = NewLineAfter (L, "\tbne\t*+4");
L = NewLineAfter (L, "\tinc\t%s+1", Reg);
Done = 1;
LinesToRemove += 4;
}
}
}
if (Done == 0) {
/* No register variable - insert the first part of the code */
if (NeedLoad) {
L = NewLineAfter (L, "\tsta\tptr1");
}
L = NewLineAfter (L, "\tclc");
L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
if (NeedLoad) {
L = NewLineAfter (L, "\tsta\tptr1+1");
}
L = NewLineAfter (L, "\tadc\t#$00");
L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
/* Check if we must really load the old value into a/x or if the
* code may be replaced by something else.
*/
if (GetNextCodeLines (L3[3], &L3[4], 1)) {
if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
/* Load char indirect */
L = NewLineAfter (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(ptr1,x)");
NeedLoad = 0;
++LinesToRemove;
} else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
GetNextCodeLines (L3[4], &L3[5], 3) &&
LineFullMatch (L3[5], "\tstx\tptr1+1") &&
LineFullMatch (L3[6], "\tldx\t#$00") &&
LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
/* Load char indirect, inlined */
L = NewLineAfter (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(ptr1,x)");
NeedLoad = 0;
LinesToRemove += 4;
} else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
/* Load word indirect */
L = NewLineAfter (L, "\tldy\t#$01");
L = NewLineAfter (L, "\tlda\t(ptr1),y");
L = NewLineAfter (L, "\ttax");
L = NewLineAfter (L, "\tdey");
L = NewLineAfter (L, "\tlda\t(ptr1),y");
NeedLoad = 0;
++LinesToRemove;
} else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
if (GetNextCodeLines (L3[4], &L3[5], 2) &&
LineMatch (L3[5], "\tlda\t") &&
LineFullMatch (L3[6], "\tjsr\tstaspp")) {
/* Store to pointer */
L = NewLineAfter (L, L3[5]->Line);
L = NewLineAfter (L, "\tldy\t#$00");
L = NewLineAfter (L, "\tsta\t(ptr1),y");
NeedLoad = 0;
LinesToRemove += 3;
} else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
LineMatch (L3[5], "\tldy\t#$") &&
LineFullMatch (L3[6], "\tlda\t(sp),y") &&
LineFullMatch (L3[7], "\tjsr\tstaspp")) {
/* Beware: We have to correct the stack offset, since we will
* remove the pushax instruction!
*/
sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
/* Store to pointer */
L = NewLineAfter (L, L3[5]->Line);
L = NewLineAfter (L, L3[6]->Line);
L = NewLineAfter (L, "\tldy\t#$00");
L = NewLineAfter (L, "\tsta\t(ptr1),y");
NeedLoad = 0;
LinesToRemove += 4;
}
}
}
/* If we need to load a/x, add the code */
if (NeedLoad) {
L = NewLineAfter (L, "\ttax");
L = NewLineAfter (L, "\tlda\tptr1");
}
}
/* Remove the code that is no longer needed */
FreeLines (L2[0], L2[LinesToRemove-1]);
/* Return the new line and success */
*Start = NextCodeLine (L);
return 1;
}
static int OptPtrOps2 (Line** Start)
/* Optimize several pointer and array constructs - subfunction 2 */
{
Line* L2[25];
Line** L3;
unsigned NeedLoad;
unsigned LinesToRemove;
unsigned Inc;
unsigned Offs;
/* Use a local variable for the working line */
Line* L = *Start;
/* Same as subfunction 1 but for local variables. */
if (LineMatch (L, "\tldy\t#$") == 0) {
return 0;
}
/* Get the stack offset. The offset points to the high byte, correct that. */
Offs = GetHexNum (L->Line+7) - 1;
/* Check for the actual sequences */
if (GetNextCodeLines (L, L2, 7) &&
LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
LineFullMatch (L2[1], "\tsta\tregsave") &&
LineFullMatch (L2[2], "\tstx\tregsave+1") &&
LineMatch (L2[3], "\tjsr\tincax")) {
/* Non inlined version */
Inc = GetHexNum (L2[3]->Line+10);
/* Check for stack offset zero */
if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
LineFullMatch (L2[5], "\tlda\tregsave") &&
LineFullMatch (L2[6], "\tldx\tregsave+1")) {
LinesToRemove = 7;
} else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
LineMatch (L2[4], "\tldy\t#$") &&
GetHexNum (L2[4]->Line+7) == Offs &&
LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
LineFullMatch (L2[6], "\tlda\tregsave") &&
LineFullMatch (L2[7], "\tldx\tregsave+1")) {
LinesToRemove = 8;
} else {
/* Not found */
return 0;
}
} else if (GetNextCodeLines (L, L2, 13) &&
LineFullMatch (L2[0], "\tlda\t(sp),y") &&
LineFullMatch (L2[1], "\ttax") &&
LineFullMatch (L2[2], "\tdey") &&
LineFullMatch (L2[3], "\tlda\t(sp),y") &&
LineFullMatch (L2[4], "\tsta\tregsave") &&
LineFullMatch (L2[5], "\tstx\tregsave+1") &&
LineFullMatch (L2[6], "\tclc") &&
LineMatch (L2[7], "\tadc\t#$") &&
LineFullMatch (L2[8], "\tbcc\t*+3") &&
LineFullMatch (L2[9], "\tinx")) {
/* Inlined version */
Inc = GetHexNum (L2[7]->Line+7);
/* Check for stack offset zero */
if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
LineFullMatch (L2[11], "\tlda\tregsave") &&
LineFullMatch (L2[12], "\tldx\tregsave+1")) {
LinesToRemove = 13;
} else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
LineMatch (L2[10], "\tldy\t#$") &&
GetHexNum (L2[10]->Line+7) == Offs &&
LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
LineFullMatch (L2[12], "\tlda\tregsave") &&
LineFullMatch (L2[13], "\tldx\tregsave+1")) {
LinesToRemove = 14;
} else {
/* Not found */
return 0;
}
} else {
/* Not found */
return 0;
}
/* Get a pointer to the last line of the preceding sequence */
L3 = &L2[LinesToRemove-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
* used without A, check just for A.
*/
NeedLoad = 1;
if (!RegAUsed (L3[0])) {
/* We don't need to load regsave */
NeedLoad = 0;
}
/* Replace the ldy instruction, offset must point to the low byte */
sprintf (L->Line+7, "%02X", Offs);
/* Insert the first part of the code */
L = NewLineAfter (L, "\tlda\t(sp),y");
if (NeedLoad) {
L = NewLineAfter (L, "\tsta\tptr1");
}
L = NewLineAfter (L, "\tclc");
L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
L = NewLineAfter (L, "\tsta\t(sp),y");
L = NewLineAfter (L, "\tiny");
L = NewLineAfter (L, "\tlda\t(sp),y");
if (NeedLoad) {
L = NewLineAfter (L, "\tsta\tptr1+1");
}
L = NewLineAfter (L, "\tadc\t#$00");
L = NewLineAfter (L, "\tsta\t(sp),y");
/* Check if we must really load the old value into a/x or if the
* code may be replaced by something else.
*/
if (GetNextCodeLines (L3[0], &L3[1], 1)) {
if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
/* Load char indirect */
L = NewLineAfter (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(ptr1,x)");
NeedLoad = 0;
++LinesToRemove;
} else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
GetNextCodeLines (L3[1], &L3[2], 3) &&
LineFullMatch (L3[2], "\tstx\tptr1+1") &&
LineFullMatch (L3[3], "\tldx\t#$00") &&
LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
/* Load char indirect, inlined */
L = NewLineAfter (L, "\tldx\t#$00");
L = NewLineAfter (L, "\tlda\t(ptr1,x)");
NeedLoad = 0;
LinesToRemove += 4;
} else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
/* Load word indirect */
L = NewLineAfter (L, "\tldy\t#$01");
L = NewLineAfter (L, "\tlda\t(ptr1),y");
L = NewLineAfter (L, "\ttax");
L = NewLineAfter (L, "\tdey");
L = NewLineAfter (L, "\tlda\t(ptr1),y");
NeedLoad = 0;
++LinesToRemove;
} else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
if (GetNextCodeLines (L3[1], &L3[2], 2) &&
LineMatch (L3[2], "\tlda\t") &&
LineFullMatch (L3[3], "\tjsr\tstaspp")) {
/* Store to pointer */
L = NewLineAfter (L, L3[2]->Line);
L = NewLineAfter (L, "\tldy\t#$00");
L = NewLineAfter (L, "\tsta\t(ptr1),y");
NeedLoad = 0;
LinesToRemove += 3;
} else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
LineMatch (L3[2], "\tldy\t#$") &&
LineFullMatch (L3[3], "\tlda\t(sp),y") &&
LineFullMatch (L3[4], "\tjsr\tstaspp")) {
/* Beware: We have to correct the stack offset, since we will
* remove the pushax instruction!
*/
sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
/* Store to pointer */
L = NewLineAfter (L, L3[2]->Line);
L = NewLineAfter (L, L3[3]->Line);
L = NewLineAfter (L, "\tldy\t#$00");
L = NewLineAfter (L, "\tsta\t(ptr1),y");
NeedLoad = 0;
LinesToRemove += 4;
}
}
}
/* If we need to load a/x, add the code */
if (NeedLoad) {
L = NewLineAfter (L, "\ttax");
L = NewLineAfter (L, "\tlda\tptr1");
}
/* Remove the code that is no longer needed */
FreeLines (L2[0], L2[LinesToRemove-1]);
/* Return the new line and success */
*Start = NextCodeLine (L);
return 1;
}
static void OptPtrOps (void)
/* Optimize several pointer and array constructs */
{
Line* L2 [10];
Line* L = FirstCode;
while (L) {
if (OptPtrOps1 (&L)) {
continue;
} else if (OptPtrOps2 (&L)) {
continue;
}
/* Search for the following sequence:
*
* lda regsave
* ldx regsave+1
* jsr pushax
* lda #$..
* jsr staspp
*
* and replace it by:
*
* lda #$..
* ldy #$00
* sta (regsave),y
*
*/
else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
LineFullMatch (L2 [1], "\tjsr\tpushax") &&
LineMatch (L2 [2], "\tlda\t#$") &&
LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
/* Found the sequence, replace it */
L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
/* Free the remaining lines */
FreeLines (L2 [2], L2 [3]);
}
/* Search for the following sequence:
*
* lda regsave
* ldx regsave+1
* jsr ldaui
*
* and replace it by:
*
* ldx #$00
* lda (regsave,x)
*
*/
else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
LineFullMatch (L2 [1], "\tjsr\tldaui")) {
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tldx\t#$00");
L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
/* Free the remaining lines */
FreeLine (L2 [1]);
}
/*
* Search for the following sequence:
*
* lda regsave
* ldx regsave+1
* jsr pushax
* ldx #$high
* lda #$low
* jsr staxspp
*
* and replace it by:
*
* ldy #$01
* lda #$high
* sta (regsave),y
* tax
* dey
* lda #$low
* sta (regsave),y
*
*/
else if (LineFullMatch (L, "\tlda\tregsave") &&
GetNextCodeLines (L, L2, 5) &&
LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
LineFullMatch (L2 [1], "\tjsr\tpushax") &&
LineMatch (L2 [2], "\tldx\t#$") &&
LineMatch (L2 [3], "\tlda\t#$") &&
LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tldy\t#$01");
L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
L2 [0]->Line [3] = 'a';
L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
L2 [2] = ReplaceLine (L2 [2], "\ttax");
L2 [3] = ReplaceLine (L2 [3], "\tdey");
L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
}
/*
* Search for the following sequence:
*
* lda regsave
* ldx regsave+1
* sta ptr1
* stx ptr1+1
* ldx #$00
* lda (ptr1,x)
*
* and replace it by:
*
* ldx #$00
* lda (regsave,x)
*
*/
else if (LineFullMatch (L, "\tlda\tregsave") &&
GetNextCodeLines (L, L2, 5) &&
LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
LineFullMatch (L2 [1], "\tsta\tptr1") &&
LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
LineFullMatch (L2 [3], "\tldx\t#$00") &&
LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tldx\t#$00");
L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
/* Remove the remaining lines */
FreeLines (L2 [1], L2 [4]);
}
/* Search for the following sequence:
*
* jsr pushax
* lda ...
* jsr staspp
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* lda ...
* ldy #$00
* sta (ptr1),y
*
*/
else if (LineFullMatch (L, "\tjsr\tpushax") &&
GetNextCodeLines (L, L2, 2) &&
LineMatch (L2 [0], "\tlda\t") &&
LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tsta\tptr1");
L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
}
/* Search for the following sequence:
*
* jsr pushax
* lda ...
* ldy #$nn
* jsr staspidx
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* lda ...
* ldy #$nn
* sta (ptr1),y
*
*/
else if (LineFullMatch (L, "\tjsr\tpushax") &&
GetNextCodeLines (L, L2, 3) &&
LineMatch (L2 [0], "\tlda\t") &&
LineMatch (L2 [1], "\tldy\t#$") &&
LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tsta\tptr1");
L = NewLineAfter (L, "\tstx\tptr1+1");
L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
}
/* Search for the following sequence:
*
* jsr pushax
* ldy #$..
* lda (sp),y
* jsr staspp
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* ldy #$..
* lda (sp),y
* ldy #$00
* sta (ptr1),y
*
* Beware: Since we remove a call to a function that changes the stack
* pointer, we have to adjust the stack address for the lda.
*
*/
else if (LineFullMatch (L, "\tjsr\tpushax") &&
GetNextCodeLines (L, L2, 3) &&
LineMatch (L2 [0], "\tldy\t#$") &&
LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
/* Found the sequence, replace it. First create a new load
* instruction for the changed stack offset.
*/
char Buf [30];
sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
L = ReplaceLine (L, "\tsta\tptr1");
L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
}
/* Search for the following sequence:
*
* jsr pushax
* ldy #$nn
* lda (sp),y
* ldy #$mm
* jsr staspidx
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* ldy #$nn
* lda (sp),y
* ldy #$mm
* sta (ptr1),y
*
* Beware: Since we remove a call to a function that changes the stack
* pointer, we have to adjust the stack address for the lda.
*
*/
else if (LineFullMatch (L, "\tjsr\tpushax") &&
GetNextCodeLines (L, L2, 4) &&
LineMatch (L2 [0], "\tldy\t#$") &&
LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
LineMatch (L2 [2], "\tldy\t#$") &&
LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
/* Found the sequence, replace it. First create a new load
* instruction for the changed stack offset.
*/
char Buf [30];
sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
L = ReplaceLine (L, "\tsta\tptr1");
L = NewLineAfter (L, "\tstx\tptr1+1");
L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
}
/* Search for the following sequence:
*
* ldax _label+0
* ldy #$..
* clc
* adc (sp),y
* bcc *+3
* inx
* sta ptr1
* stx ptr1+1
* ldx #$00
* lda (ptr1,x)
*
* and replace it by:
*
* ldy #$..
* lda (sp),y
* tay
* ldx #$00
* lda _label+0,y
*
* The load of X may be omitted if X is not used below.
*/
else if (LineMatch (L, "\tldax\t_") &&
GetNextCodeLines (L, L2, 9) &&
LineMatch (L2 [0], "\tldy\t#$") &&
LineFullMatch (L2 [1], "\tclc") &&
LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
LineFullMatch (L2 [3], "\tbcc\t*+3") &&
LineFullMatch (L2 [4], "\tinx") &&
LineFullMatch (L2 [5], "\tsta\tptr1") &&
LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
LineFullMatch (L2 [7], "\tldx\t#$00") &&
LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
/* Found the sequence, replace it */
char Label [256];
strcpy (Label, L->Line + 6); /* Remember the label */
L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
L = NewLineAfter (L, "\tlda\t(sp),y");
L = NewLineAfter (L, "\ttay");
if (RegXUsed (L2[8])) {
L = NewLineAfter (L, "\tldx\t#$00");
}
L = NewLineAfter (L, "\tlda\t%s,y", Label);
/* Remove the remaining stuff. There may be hints between the
* instructions, remove them too
*/
FreeLines (L2[0], L2 [8]);
}
/* Check for
*
* ldy #$xx
* lda (sp),y
* tax
* dey
* lda (sp),y
* ldy #$yy
* jsr ldauidx
*
* and replace it by
*
* ldy #$xx
* ldx #$yy
* jsr ldauiysp
*
* or even
*
* jsr ldaui0sp
*
* This change will cost 2 cycles, but it saves a lot of code (6 bytes
* per occurrence), so we will accept the overhead. It may even be
* possible to rewrite the library routine to get rid of the additional
* overhead.
*/
else if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 6) &&
LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
LineFullMatch (L2 [1], "\ttax") &&
LineFullMatch (L2 [2], "\tdey") &&
LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
LineMatch (L2 [4], "\tldy\t#$") &&
LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
/* Found - replace it */
L2 [4]->Line [3] = 'x'; /* Change to ldx */
if (LineFullMatch (L, "\tldy\t#$01")) {
/* Word at offset zero */
FreeLine (L);
L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
} else {
ReplaceLine (L2 [5], "\tjsr\tldauiysp");
}
/* Delete the remaining lines */
FreeLines (L2 [0], L2 [3]);
}
/* Check for
*
* ldy #$xx
* lda (sp),y
* tax
* dey
* lda (sp),y
* sta ptr1
* stx ptr1+1
* ldx #$00
* lda (ptr1,x)
*
* and replace it by
*
* ldy #$xx
* jsr ldau0ysp
*
* or even
*
* jsr ldau00sp
*
* This change will has an overhead of 10 cycles, but it saves 11(!)
* bytes per invocation. Maybe we should apply only if FavourSize is
* true?
*/
else if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 8) &&
LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
LineFullMatch (L2 [1], "\ttax") &&
LineFullMatch (L2 [2], "\tdey") &&
LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
LineFullMatch (L2 [4], "\tsta\tptr1") &&
LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
LineFullMatch (L2 [6], "\tldx\t#$00") &&
LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
/* Found - replace it */
if (LineFullMatch (L, "\tldy\t#$01")) {
/* Word at offset zero */
FreeLine (L);
L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
} else {
ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
}
/* Delete the remaining lines */
FreeLines (L2 [1], L2 [7]);
}
/* Next Line */
L = NextCodeLine (L);
}
}
static void OptRegVars (void)
/* Optimize register variable uses */
{
Line* L2 [10];
Line* L = FirstCode;
while (L) {
/* Search for the following sequence:
*
* lda regbank+n
* ldx regbank+n+1
* jsr ldaui
*
* and replace it by:
*
* ldx #$00
* lda (regbank+n,x)
*
*/
if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
LineFullMatch (L2 [1], "\tjsr\tldaui") &&
L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
strcmp (L2 [0]->Line + 14, "+1") == 0) {
char Buf [100];
sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tldx\t#$00");
L2 [0] = ReplaceLine (L2 [0], Buf);
/* Free the remaining lines */
FreeLine (L2 [1]);
}
/* Search for the following sequence:
*
* lda regbank+n
* ldx regbank+n+1
* sta ptr1
* stx ptr1+1
* ldx #$00
* lda (ptr1,x)
*
* and replace it by:
*
* ldx #$00
* lda (regbank+n,x)
*
*/
else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
strcmp (L2 [0]->Line + 14, "+1") == 0 &&
LineFullMatch (L2 [1], "\tsta\tptr1") &&
LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
LineFullMatch (L2 [3], "\tldx\t#$00") &&
LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
char Buf [100];
sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
/* Found the sequence, replace it */
L = ReplaceLine (L, "\tldx\t#$00");
L2 [0] = ReplaceLine (L2 [0], Buf);
/* Free the remaining lines */
FreeLines (L2 [1], L2 [4]);
}
/* Search for the following sequence:
*
* lda regbank+n
* ldx regbank+n+1
* ldy #$..
* jsr ldauidx
*
* and replace it by:
*
* ldy #$..
* ldx #$00
* lda (regbank+n),y
*
*/
else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
strcmp (L2 [0]->Line + 14, "+1") == 0 &&
LineMatch (L2 [1], "\tldy\t#$") &&
LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
char Buf [100];
sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
/* Found the sequence, replace it */
L = ReplaceLine (L, L2 [1]->Line);
L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
L2 [1] = ReplaceLine (L2 [1], Buf);
/* Free the remaining lines */
FreeLine (L2 [2]);
}
/* Search for the following sequence:
*
* lda regbank+n
* ldx regbank+n+1
* sta ptr1
* stx ptr1+1
* lda ...
* ldy #$mm
* sta (ptr1),y
*
* and replace it by:
*
* lda ...
* ldy #$mm
* sta (regbank+n),y
*
* The source form is not generated by the parser but by the optimizer.
*/
else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
strcmp (L2 [0]->Line + 14, "+1") == 0 &&
LineFullMatch (L2 [1], "\tsta\tptr1") &&
LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
LineMatch (L2 [3], "\tlda\t") &&
LineMatch (L2 [4], "\tldy\t#$") &&
LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
char Buf [100];
sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
/* Found the sequence, replace it */
L2 [5] = ReplaceLine (L2 [5], Buf);
/* Free the remaining lines */
FreeLines (L, L2 [2]);
/* Make the line pointer valid again */
L = L2 [5];
}
/* Search for the following sequence:
*
* lda regbank+n
* ldx regbank+n+1
* sta ptr1
* stx ptr1+1
* ldy #$mm
* lda (sp),y
* ldy #$ll
* sta (ptr1),y
*
* and replace it by:
*
* ldy #$mm
* lda (sp),y
* ldy #$ll
* sta (regbank+n),y
*
* The source form is not generated by the parser but by the optimizer.
*/
else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
strcmp (L2 [0]->Line + 14, "+1") == 0 &&
LineFullMatch (L2 [1], "\tsta\tptr1") &&
LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
LineMatch (L2 [3], "\tldy\t#$") &&
LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
LineMatch (L2 [5], "\tldy\t#$") &&
LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
char Buf [100];
sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
/* Found the sequence, replace it */
L2 [6] = ReplaceLine (L2 [6], Buf);
/* Free the remaining lines */
FreeLines (L, L2 [2]);
/* Make the line pointer valid again */
L = L2 [6];
}
/* Next Line */
L = NextCodeLine (L);
}
}
static void OptDoubleJumps (void)
/* Remove/rearrange jumps that jump to other jumps */
{
static const char* Jumps [] = {
"\tjeq\tL",
"\tjne\tL",
"\tbeq\tL",
"\tbne\tL",
"\tjmp\tL",
0
};
unsigned D;
Line* L = FirstCode;
while (L) {
int I;
/* Is this a jump? */
while ((I = LineMatchX (L, Jumps)) >= 0) {
/* Yes. Get the target label */
Line* Target = GetTargetLine (L->Line + 5);
/* Target points to the label itself. Skip lines until we reach
* one that is not a label.
*/
Target = NextInstruction (Target);
/* Be sure, this line is not the same as the one the jump is
* in (this happens if there is an empty loop).
*/
if (Target == L) {
break;
}
D = 0;
if (LineMatch (Target, "\tjmp\t")) {
/* The target is itself a jump. If this is a short branch, get
* the final target and check if it is in reach. Bail out if
* not.
*/
if (L->Line[1] == 'b') {
Line* FinalTarget = GetTargetLine (Target->Line+5);
FinalTarget = NextInstruction (FinalTarget);
if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
break;
}
}
/* Make sure the jump does indeed point to another label.
* It may happen that this is not the case for some endless
* loop (while(1) and similar).
*/
if (strcmp (L->Line+5, Target->Line+5) == 0) {
/* Same label, bail out */
break;
}
/* Use the label in the original jump instead */
L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
} else if (I < 2 && LineMatch (Target, Jumps [I])) {
/* Conditional jump. Use final label */
strcpy (L->Line+5, Target->Line+5);
} else {
break;
}
}
/* Next line */
L = NextCodeLine (L);
}
}
static void OptJumpRTS (void)
/* Replace jumps to an RTS by an RTS */
{
Line* L = FirstCode;
while (L) {
/* Is this a jump to a numbered label? */
if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
/* Yes. Get the target label */
Line* Target = GetTargetLine (L->Line+5);
/* Target points to the label itself. Get the next line */
Target = NextCodeLine (Target);
if (LineFullMatch (Target, "\trts")) {
/* Replace the jump by an RTS */
L = ReplaceLine (L, "\trts");
}
}
L = NextCodeLine (L);
}
}
static void OptBoolTransforms (void)
/* Try to remove the boolean transformation subroutines where they aren't
* necessary.
*/
{
Line* L2 [2];
unsigned Label;
const char* BranchTarget;
Line* L = FirstCode;
while (L) {
/* Search for a boolean transformer followed by a conditional jump. */
if (LineMatch (L, "\tjsr\tbool") &&
GetNextCodeLines (L, L2, 1) &&
IsCondJump (L2 [0])) {
/* Make the boolean transformer unnecessary by changing the
* the conditional jump to evaluate the condition flags that
* are set after the compare directly. Note: jeq jumps if
* the condition is not met, jne jumps if the condition is met.
*/
/* Get the condition code */
int Cond = FindCond (L->Line + 9);
if (Cond < 0) {
/* OOPS! */
goto NextLine;
}
/* Invert the code if we jump on condition not met. */
if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
/* Jumps if condition false, invert condition */
Cond = CmpInvertTab [Cond];
}
/* For easier reading, get a pointer to the jump target */
BranchTarget = L2[0]->Line+5;
/* Check if we can replace the jump (sometimes we would need two
* conditional jumps, we will not handle that for now since it
* has some complications - both jumps may be far jumps for
* example making the jumps more costly than the bool transformer
* subroutine). If we cannot replace the jump, bail out.
*/
switch (Cond) {
case CMP_EQ:
L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
break;
case CMP_NE:
L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
break;
case CMP_GT:
Label = AllocLabel ();
L = ReplaceLine (L, "\tbeq\tL%04X", Label);
L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
L = NewLabelAfter(L, Label);
break;
case CMP_GE:
L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
break;
case CMP_LT:
L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
break;
case CMP_LE:
L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
break;
case CMP_UGT:
Label = AllocLabel ();
L = ReplaceLine (L, "\tbeq\tL%04X", Label);
L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
L = NewLabelAfter(L, Label);
break;
case CMP_UGE:
L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
break;
case CMP_ULT:
L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
break;
case CMP_ULE:
L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
break;
default:
Internal ("Unknown jump condition: %u", Cond);
}
/* Remove the old stuff */
FreeLine (L2[0]);
}
NextLine:
L = NextCodeLine (L);
}
}
static void OptCompares2 (void)
/* Try to optimize the integer compare subroutines. */
{
Line* L2[10];
unsigned Label;
const char* BranchTarget;
int C;
Line* L = FirstCode;
while (L) {
/* Search for
*
* lda x
* ldx x+1
* cpx #$00
* bne *+4
* cmp #$00
* jne/jeq ...
*
* and replace it by
*
* lda x
* ora x+1
* jne/jeq ...
*/
if (LineMatch (L, "\tlda\t") &&
GetNextCodeLines (L, L2, 5) &&
IsLoadAX (L, L2[0]) &&
LineFullMatch (L2[1], "\tcpx\t#$00") &&
LineFullMatch (L2[2], "\tbne\t*+4") &&
LineFullMatch (L2[3], "\tcmp\t#$00") &&
IsCondJump (L2[4])) {
/* Replace the load of X by an ora */
L2[0]->Line[1] = 'o';
L2[0]->Line[2] = 'r';
L2[0]->Line[3] = 'a';
/* Remove unneeded stuff */
FreeLines (L2[1], L2[3]);
}
/* Same for local variables: Replace
*
* ldy #$xx
* lda (sp),y
* tax
* dey
* lda (sp),y
* cpx #$00
* bne *+4 cmp #$00
* cmp #$00
* jne/jeq ...
*
* by
*
* ldy #$xx
* lda (sp),y
* dey
* ora (sp),y
* jne/jeq ...
*/
else if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 8) &&
LineFullMatch (L2[0], "\tlda\t(sp),y") &&
LineFullMatch (L2[1], "\ttax") &&
LineFullMatch (L2[2], "\tdey") &&
LineFullMatch (L2[3], "\tlda\t(sp),y") &&
LineFullMatch (L2[4], "\tcpx\t#$00") &&
LineFullMatch (L2[5], "\tbne\t*+4") &&
LineFullMatch (L2[6], "\tcmp\t#$00") &&
IsCondJump (L2[7])) {
/* Replace the second load by an ora */
L2[3]->Line[1] = 'o';
L2[3]->Line[2] = 'r';
L2[3]->Line[3] = 'a';
/* Remove unneeded stuff */
FreeLine (L2[1]);
FreeLines (L2[4], L2[6]);
}
/* Search for the call to a compare subroutine followed by a
* conditional jump.
*/
else if (LineMatch (L, "\tjsr\ttos") &&
(L2[0] = NextCodeLine (L)) != 0 &&
IsCondJump (L2[0])) {
/* Extract the condition from the function name and branch */
C = CheckAndGetIntCmp (L, L2[0]);
if (C < 0) {
/* Something is wrong */
goto NextLine;
}
/* Replace the subroutine call by a cheaper one */
L = ReplaceLine (L, "\tjsr\ttosicmp");
/* For easier reading, get a pointer to the jump target */
BranchTarget = L2[0]->Line+5;
/* Check if we can replace the jump (sometimes we would need two
* conditional jumps, we will not handle that for now since it
* has some complications - both jumps may be far jumps for
* example making the jumps more costly than the bool transformer
* subroutine). If we cannot replace the jump, bail out.
*/
switch (C) {
case CMP_EQ:
L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
break;
case CMP_NE:
L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
break;
case CMP_GT:
Label = AllocLabel ();
L = NewLineAfter (L, "\tbeq\tL%04X", Label);
L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
L = NewLabelAfter(L, Label);
break;
case CMP_GE:
L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
break;
case CMP_LT:
L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
break;
case CMP_LE:
L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
break;
case CMP_UGT:
Label = AllocLabel ();
L = NewLineAfter (L, "\tbeq\tL%04X", Label);
L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
L = NewLabelAfter(L, Label);
break;
case CMP_UGE:
L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
break;
case CMP_ULT:
L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
break;
case CMP_ULE:
L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
break;
default:
Internal ("Unknown jump condition: %u", C);
}
/* Remove the old stuff */
FreeLine (L2[0]);
}
NextLine:
L = NextCodeLine (L);
}
}
static void OptTests (void)
/* Remove unnecessary tests */
{
Line* L2 [2];
const char* BitOps [] = {
"\tand\t",
"\tora\t",
"\teor\t",
0
};
/* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
* Search for
* lda ...
* cmp #$00
* jne/jeq
* Remove the cmp.
*/
Line* L = FirstCode;
while (L) {
/* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
* Search for
* lda/and/ora/eor
* cmp #$00
* jne/jeq ...
* Remove the cmp.
*/
if ((LineMatch (L, "\tlda\t") ||
LineMatch (L, "\tand\t") ||
LineMatch (L, "\tora\t") ||
LineMatch (L, "\teor\t")) &&
GetNextCodeLines (L, L2, 2) &&
(LineFullMatch (L2 [0], "\ttay") ||
LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
IsCondJump (L2 [1])) {
/* We can remove the tay */
FreeLine (L2 [0]);
}
/* Search for
*
* and ...
* tax
* jeq/jne
*
* and remove the tax.
*/
else if (LineMatchX (L, BitOps) >= 0 &&
GetNextCodeLines (L, L2, 2) &&
LineFullMatch (L2[0], "\ttax") &&
IsCondJump (L2[1])) {
/* Remove the tax including a hint line of there is one */
if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
FreeLine (L2[0]->Prev);
}
FreeLine (L2[0]);
/* If the line before L loads X, this is useless and may be removed */
L2[0] = PrevCodeLine (L);
if (LineFullMatch (L2[0], "\tldx\t#$00")) {
FreeLine (L2[0]);
}
}
/* Search for the sequence
*
* stx xx
* stx tmp1
* ora tmp1
*
* and replace it by
*
* stx xx
* ora xx
*/
else if (LineMatch (L, "\tstx\t") &&
GetNextCodeLines (L, L2, 2) &&
LineFullMatch (L2[0], "\tstx\ttmp1") &&
LineFullMatch (L2[1], "\tora\ttmp1")) {
/* Found, replace it */
L = NewLineAfter (L, "\tora\t%s", L->Line+5);
/* Remove remaining stuff */
FreeLines (L2[0], L2[1]);
}
/* Next line */
L = NextCodeLine (L);
}
}
static void OptNeg (void)
/* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
{
Line* L2 [10];
Line* L = FirstCode;
while (L) {
/* Search for the sequence:
*
* lda ...
* jsr bnega
* jeq/jne ...
*
* and replace it by:
*
* lda ...
* jne/jeq ...
*/
if (LineMatch (L, "\tlda\t") && /* Match on start */
GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
LineFullMatch (L2 [0], "\tjsr\tbnega") &&
IsCondJump (L2 [1])) {
/* Found the sequence, replace it */
FreeLine (L2 [0]);
InvertZJump (L2 [1]);
}
/* Search for the sequence:
*
* ldy #$xx
* lda (sp),y
* tax
* dey
* lda (sp),y
* jsr bnegax
* jne/jeq ...
*
* and replace it by
*
* ldy #$xx
* lda (sp),y
* dey
* ora (sp),y
* jeq/jne ...
*/
else if (LineMatch (L, "\tldy\t#$") &&
GetNextCodeLines (L, L2, 6) &&
LineFullMatch (L2[0], "\tlda\t(sp),y") &&
LineFullMatch (L2[1], "\ttax") &&
LineFullMatch (L2[2], "\tdey") &&
LineFullMatch (L2[3], "\tlda\t(sp),y") &&
LineFullMatch (L2[4], "\tjsr\tbnegax") &&
IsCondJump (L2[5])) {
L2[1] = ReplaceLine (L2[1], "\tdey");
L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
FreeLines (L2[3], L2[4]);
InvertZJump (L2[5]);
}
/* Search for the sequence:
*
* lda xx
* ldx xx+1
* jsr bnegax
* jne/jeq ...
*
* and replace it by
*
* lda xx
* ora xx+1
* jeq/jne ...
*/
else if (LineMatch (L, "\tlda\t") &&
GetNextCodeLines (L, L2, 3) &&
IsLoadAX (L, L2[0]) &&
LineFullMatch (L2[1], "\tjsr\tbnegax") &&
IsCondJump (L2[2])) {
/* Replace the load of X by ora */
L2[0]->Line[1] = 'o';
L2[0]->Line[2] = 'r';
L2[0]->Line[3] = 'a';
FreeLine (L2[1]);
InvertZJump (L2[2]);
}
/* Search for the sequence:
*
* jsr _xxx
* jsr bnega(x)
* jeq/jne ...
*
* and replace it by:
*
* jsr _xxx
* <boolean test>
* jne/jeq ...
*/
else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
GetNextCodeLines (L, L2, 2) &&
LineMatch (L2 [0], "\tjsr\tbnega") &&
IsCondJump (L2 [1])) {
if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
/* Byte sized */
L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
} else {
/* Word sized */
L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
NewLineAfter (L2 [0], "\tora\ttmp1");
}
/* Invert the jump */
InvertZJump (L2 [1]);
}
/* Next line */
L = NextCodeLine (L);
}
}
static void OptTriples (void)
/* Replace code triples */
{
static const char* Pat1 [] = {
"\tjsr\tldaxysp",
"\tjsr\tldax0sp",
"\tjsr\tldaysp",
"\tjsr\tleaasp",
"\tjsr\tldaxi",
0
};
static const char* Pat2 [] = {
"\tjsr\tpushax",
"\tjsr\tpushax",
"\tjsr\tpushax",
"\tjsr\tpushax",
"\tjsr\tpushax",
0
};
static const char* Replace [] = {
"\tjsr\tpushwysp",
"\tjsr\tpushw0sp",
"\tjsr\tpushbysp",
"\tjsr\tpleaasp",
"\tjsr\tpushw",
};
Line* L = FirstCode;
while (L) {
int I = LineFullMatchX (L, Pat1);
if (I >= 0) {
/* We found the first match, get the next line */
Line* L2 = NextCodeLine (L);
if (L2 && LineFullMatch (L2, Pat2 [I])) {
/* Found. Replace by the short call */
FreeLine (L2);
L = ReplaceLine (L, Replace [I]);
}
}
/* Next line */
L = NextCodeLine (L);
}
}
static Line* OptOneBlock (Line* L)
/* Optimize the register contents inside one basic block */
{
static const char* Compares [] = {
"\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
"\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
"\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
"\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
"\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
"\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
"\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
"\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
"\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
"\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
"\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
"\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
"\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
"\tjsr\ttosugeeax",
0
};
static const char* MakeBool [] = {
"\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
"\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
"\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
"\tjsr\tbooluge",
0
};
int A = -1; /* Contents of A register */
int X = -1; /* Contents of X register */
int Y = -1; /* Contents of Y register */
Line* L2;
unsigned NewVal;
int Delete;
while (L && !IsLabel (L)) {
/* Handle all instructions. All instructions not tested here have
* no effects on the register contents.
*/
Delete = 0;
if (L->Line [0] == '+') {
/* This is a hint */
if (LineMatch (L, "+a:")) {
/* Information about a */
switch (L->Line [3]) {
case '!': A = -1; break;
case '=': A = GetHexNum (L->Line + 4); break;
}
} else if (LineMatch (L, "+x:")) {
/* The code generator tells something about the x register */
switch (L->Line [3]) {
case '!': X = -1; break;
case '=': X = GetHexNum (L->Line + 4); break;
}
} else if (LineMatch (L, "+y:")) {
/* Information about the y register */
switch (L->Line [3]) {
case '!': Y = -1; break;
case '=': Y = GetHexNum (L->Line + 4); break;
}
}
} else if (LineMatch (L, "\tadc\t")) {
A = -1;
} else if (LineMatch (L, "\tand\t")) {
A = -1;
} else if (LineFullMatch (L, "\tasl\ta")) {
if (A != -1) {
A = (A << 1) & 0xFF;
}
} 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 (LineFullMatch (L, "\tinx")) {
INC (X, 1);
} else if (LineFullMatch (L, "\tiny")) {
INC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
/* We know about this function */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
/* We know about this function */
A = X = -1;
INC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\taxulong")) {
/* We know about this function and we're trying to replace it by
* inline code if we have already a register that contains zero.
*/
char C;
if (A == 0) {
C = 'a';
} else if (X == 0) {
C = 'x';
} else if (Y == 0) {
C = 'y';
} else {
C = '\0';
}
if (C == '\0') {
/* We cannot replace the code, but we know about the results */
Y = 0;
} else {
L = ReplaceLine (L, "\tst%c\tsreg", C);
NewLineAfter (L, "\tst%c\tsreg+1", C);
}
} else if (LineFullMatch (L, "\tjsr\tbnega")) {
/* We know about this function */
A = -1;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tbnegax")) {
/* We know about this function */
A = -1;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
/* We know about this function */
A = -1;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tcomplax")) {
/* We know about this function */
if (A != -1) {
A ^= 0xFF;
}
if (X != -1) {
X ^= 0xFF;
}
} else if (LineFullMatch (L, "\tjsr\tdecax1")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tdecax2")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tincax1")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tincax2")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tladdeq")) {
/* We know about this function */
A = X = -1;
Y = 3;
} else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
/* We know about this function */
A = X = -1;
Y = 3;
} else if (LineFullMatch (L, "\tjsr\tlbneg")) {
/* We know about this function */
A = -1;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tldai")) {
/* We know about this function */
A = X = -1;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tldaidx")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
/* We know about this function */
A = -1;
X = 0;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
/* We know about this function */
A = -1;
X = 0;
DEC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\tldaui")) {
/* We know about this function */
A = -1;
X = 0;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
A = -1;
Y = X;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tldauidx")) {
/* We know about this function */
A = -1;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
/* We know about this function */
A = -1;
Y = X;
X = 0;
} else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
/* We know about this function */
A = X = -1;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tldaxi")) {
/* We know about this function */
A = X = -1;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
/* We know about this function */
A = X = -1;
DEC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
/* We know about this function */
A = X = -1;
DEC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
/* We know about this function */
A = X = -1;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
/* We know about this function */
A = X = -1;
DEC (Y, 3);
} else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
/* We know about this function */
A = X = -1;
Y = 3;
} else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
/* We know about this function */
A = X = -1;
Y = 3;
} else if (LineFullMatch (L, "\tjsr\tnegax")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tnegeax")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tpush0")) {
/* We know about this function */
A = 0;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush1")) {
/* We know about this function */
A = 1;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush2")) {
/* We know about this function */
A = 2;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush3")) {
/* We know about this function */
A = 3;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush4")) {
/* We know about this function */
A = 4;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush5")) {
/* We know about this function */
A = 5;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush6")) {
/* We know about this function */
A = 6;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpush7")) {
/* We know about this function */
A = 7;
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpusha")) {
/* We know about this function */
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tpusha0")) {
/* We know about this function
* If X is already zero, we may call pushax instead and save two
* cycles.
*/
if (X == 0) {
L = ReplaceLine (L, "\tjsr\tpushax");
}
X = 0;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpushax")) {
/* We know about this function */
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpushc0")) {
/* We know about this function */
A = 0;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tpushc1")) {
/* We know about this function */
A = 1;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tpushc2")) {
/* We know about this function */
A = 2;
Y = 0;
} else if (LineFullMatch (L, "\tjsr\tpushw")) {
/* We know about this function (calls pushax) */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
/* We know about this function(calls pushax) */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
/* We know about this function (calls pushax) */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
/* We know about this function (calls pushax) */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tresteax")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
/* We know about this function */
/* Changes nothing */
} else if (LineFullMatch (L, "\tjsr\tshrax1")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tshrax2")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tshrax3")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tshreax1")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tshreax2")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tshreax3")) {
/* We know about this function */
A = X = -1;
} else if (LineFullMatch (L, "\tjsr\tstaspp")) {
/* We know about this function */
Y = -1;
} else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
/* We know about this function */
Y = -1;
} else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
/* We know about this function */
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
/* We know about this function */
INC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
/* We know about this function */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
/* We know about this function */
A = X = -1;
INC (Y, 1);
} else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
/* We know about this function */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
/* We know about this function */
A = X = -1;
Y = 1;
} else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
/* We know about this function */
A = X = -1;
Y = 0;
} else if (LineFullMatchX (L, Compares) >= 0) {
A = Y = -1;
X = 0;
} else if (LineFullMatchX (L, MakeBool) >= 0) {
A = -1;
X = 0;
} else if (LineMatch (L, "\tjsr\t")) {
/* Subroutine call, forget all register information */
A = X = Y = -1;
} else if (LineMatch (L, "\tlda\t")) {
if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
/* The value loaded is not used later, remove it */
Delete = 1;
} else if (LineMatch (L, "\tlda\t(")) {
if (IsXIndAddrMode (L)) {
/* lda (zp,x) - if Y and X are both zero, replace by
* load indirect y and save one cycle in some cases.
*/
if (X == 0 && Y == 0) {
char Buf [256];
const char* S = L->Line + 6;
char* T = Buf + 6;
strcpy (Buf, "\tlda\t(");
while (*S != ',') {
*T++ = *S++;
}
*T++ = ')';
*T++ = ',';
*T++ = 'y';
*T = '\0';
L = ReplaceLine (L, Buf);
}
}
/* In any case invalidate A */
A = -1;
} else if (LineMatch (L, "\tlda\t#$")) {
/* Immidiate load into A */
NewVal = GetHexNum (L->Line + 7);
if (NewVal == A) {
/* Load has no effect */
Delete = 1;
} else if (NewVal == X) {
/* Requested value is already in X */
L = ReplaceLine (L, "\ttxa");
} else if (NewVal == Y) {
/* Requested value is already in Y */
L = ReplaceLine (L, "\ttya");
}
/* Anyway, the new value is now in A */
A = NewVal;
} else {
/* Memory load into A */
A = -1;
}
} else if (LineMatch (L, "\tldax\t")) {
/* Memory load into A and X */
A = X = -1;
} else if (LineMatch (L, "\tldx\t")) {
if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
/* The value loaded is not used later, remove it */
Delete = 1;
} else if (LineMatch (L, "\tldx\t#$")) {
/* Immidiate load into X */
NewVal = GetHexNum (L->Line + 7);
if (NewVal == X) {
/* Load has no effect */
Delete = 1;
} else if (NewVal == A) {
/* Requested value is already in A */
L = ReplaceLine (L, "\ttax");
} else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
/* Requested value is one more than current contents */
L = ReplaceLine (L, "\tinx");
} else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
/* Requested value is one less than current contents */
L = ReplaceLine (L, "\tdex");
}
/* Anyway, the new value is now in X */
X = NewVal;
} else {
/* Memory load into X */
X = -1;
}
} else if (LineMatch (L, "\tldy\t")) {
if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
/* The value loaded is not used later, remove it */
Delete = 1;
} else if (LineMatch (L, "\tldy\t#$")) {
/* Immidiate load into Y */
NewVal = GetHexNum (L->Line + 7);
if (NewVal == Y) {
/* Load has no effect */
Delete = 1;
} else if (NewVal == A) {
/* Requested value is already in A */
L = ReplaceLine (L, "\ttay");
} else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
/* Requested value is one more than current contents */
L = ReplaceLine (L, "\tiny");
} else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
/* Requested value is one less than current contents */
L = ReplaceLine (L, "\tdey");
}
/* Anyway, the new value is now in Y */
Y = NewVal;
} else {
/* Memory load into Y */
Y = -1;
}
} else if (LineFullMatch (L, "\tlsr\ta")) {
if (A != -1) {
A >>= 1;
}
} else if (LineMatch (L, "\tora\t#$")) {
if (A != -1) {
A |= GetHexNum (L->Line + 7);
}
} else if (LineMatch (L, "\tora\t")) {
A = -1;
} else if (LineFullMatch (L, "\tpla")) {
A = -1;
} else if (LineFullMatch (L, "\trol\ta")) {
A = -1;
} else if (LineFullMatch (L, "\tror\ta")) {
A = -1;
} else if (LineFullMatch (L, "\trts")) {
A = X = Y = -1;
} else if (LineFullMatch (L, "\trti")) {
A = X = Y = -1;
} else if (LineMatch (L, "\tsbc\t")) {
A = -1;
} else if (LineFullMatch (L, "\ttax")) {
if (A != -1 && X == A) {
/* Load has no effect */
Delete = 1;
} else {
X = A;
}
} else if (LineFullMatch (L, "\ttay")) {
if (A != -1 && Y == A) {
/* Load has no effect */
Delete = 1;
} else {
Y = A;
}
} else if (LineFullMatch (L, "\ttsx")) {
X = -1;
} else if (LineFullMatch (L, "\ttxa")) {
if (X != -1 && A == X) {
/* Load has no effect */
Delete = 1;
} else {
A = X;
}
} else if (LineFullMatch (L, "\ttya")) {
if (Y != -1 && A == Y) {
/* Load has no effect */
Delete = 1;
} else {
A = Y;
}
}
/* Set to next line, handle deletions */
L2 = NextCodeSegLine (L);
if (Delete) {
FreeLine (L);
}
L = L2;
}
if (L) {
/* Skip the label */
L = NextCodeSegLine (L);
}
return L;
}
static void OptBlocks (void)
/* Optimize the register contents inside basic blocks */
{
Line* L = FirstCode;
while (L) {
L = OptOneBlock (L);
}
}
static void OptJumps (void)
/* Optimize jumps */
{
static const char* Jumps [] = {
"\tjeq\tL",
"\tjne\tL",
"\tjmi\tL",
"\tjpl\tL",
"\tjcs\tL",
"\tjcc\tL",
0
};
Line* L = FirstCode;
while (L) {
int I = LineMatchX (L, Jumps);
if (I >= 0) {
Line* Target = GetTargetLine (L->Line+5);
if (Target->Index > L->Index) {
/* This is a forward jump. Backward jumps are handled
* automagically by the assembler.
*/
unsigned Distance = GetJumpDistance (L, Target);
if (Distance < 123) { /* Safety */
L->Line [1] = 'b'; /* Make a short branch */
L->Size = 2; /* Set new size */
}
}
}
L = NextCodeLine (L);
}
}
static void OptRTS (void)
/* Change sequences of jsr XXX/rts to jmp XXX */
{
Line* L = FirstCode;
while (L) {
if (LineMatch (L, "\tjsr\t")) {
/* This is a jsr, get the next instruction */
Line* L2 = NextCodeLine (L);
if (L2 && LineFullMatch (L2, "\trts")) {
/* We found a sequence */
FreeLine (L2);
L->Line [2] = 'm';
L->Line [3] = 'p';
}
}
/* Try the next line */
L = NextCodeLine (L);
}
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static void OptOnePass (unsigned long Flag, void (*F) (void))
/* Call one optimizer pass if enabled */
{
if ((OptDisable & Flag) == 0) {
F ();
} else if (Verbose || Debug) {
printf ("Optimizer pass %04lX skipped\n", Flag);
}
}
void OptDoOpt (void)
/* Run the optimizer over the collected stuff */
{
/* Find and remember the first line of code */
FindCodeStart ();
/* Mark all lines inside the code segment */
MarkCodeLines ();
/* Create a list of all local labels for fast access */
CreateLabelList ();
/* Ok, now start the real optimizations */
/* Optimize compares - first step */
OptOnePass (0x0001, OptCompares1);
/* Remove dead jumps */
OptOnePass (0x0002, OptDeadJumps);
/* Remove unnecessary loads */
OptOnePass (0x0004, OptLoads);
/* Remove unnecessary register loads */
OptOnePass (0x0008, OptRegLoads);
/* Optimize stores through pointers */
OptOnePass (0x0010, OptPtrOps);
/* Optimize use of register variables */
OptOnePass (0x0020, OptRegVars);
/* Remove jump cascades - must be used before OptNeg */
OptOnePass (0x0040, OptDoubleJumps);
/* Remove unnecessary boolean negates */
OptOnePass (0x0080, OptNeg);
/* Replace jumps to an RTS by an RTS */
OptOnePass (0x0100, OptJumpRTS);
/* Optimize boolean transforms */
OptOnePass (0x0200, OptBoolTransforms);
/* Optimize compares */
OptOnePass (0x0400, OptCompares2);
/* Remove unnecessary tests */
OptOnePass (0x0800, OptTests);
/* Optimize several triples */
OptOnePass (0x1000, OptTriples);
/* Optimize basic blocks */
OptOnePass (0x2000, OptBlocks);
/* Remove unnecessary register loads (another pass) */
OptOnePass (0x0008, OptRegLoads);
/* Optimize jumps */
OptOnePass (0x4000, OptJumps);
/* Optimize jsr/rts sequences */
OptOnePass (0x8000, OptRTS);
}