-/*-------------------------------------------------------------------------
- peep.c - source file for peephole optimizer helper functions
-
- Written By - Philipp Klaus Krause
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding!
--------------------------------------------------------------------------*/
-
-#include "common.h"
-#include "SDCCicode.h"
-#include "z80.h"
-#include "SDCCglobl.h"
-#include "SDCCpeeph.h"
-#include "gen.h"
-
-#define NOTUSEDERROR {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");}
-
-/*#define D(_s) { printf _s; fflush(stdout); }*/
-#define D(_s)
-
-typedef enum
-{
- S4O_CONDJMP,
- S4O_WR_OP,
- S4O_RD_OP,
- S4O_TERM,
- S4O_VISITED,
- S4O_ABORT,
- S4O_CONTINUE
-} S4O_RET;
-
-static struct
-{
- lineNode *head;
-} _G;
-
-/*-----------------------------------------------------------------*/
-/* univisitLines - clear "visited" flag in all lines */
-/*-----------------------------------------------------------------*/
-static void
-unvisitLines (lineNode *pl)
-{
- for (; pl; pl = pl->next)
- pl->visited = FALSE;
-}
-
-#define AOP(op) op->aop
-#define AOP_SIZE(op) AOP(op)->size
-
-static bool
-isReturned(const char *what)
-{
- symbol *sym;
- sym_link *sym_lnk;
- int size;
- lineNode *l;
-
- if(strncmp(what, "iy", 2) == 0)
- return FALSE;
- if(strlen(what) != 1)
- return TRUE;
-
- l = _G.head;
- do
- {
- l = l->next;
- } while(l->ic->op != FUNCTION);
-
- sym = OP_SYMBOL(IC_LEFT(_G.head->next->next->ic));
-
- if(sym && IS_DECL(sym->type))
- {
- // Find size of return value.
- specifier *spec;
- if(sym->type->select.d.dcl_type != FUNCTION)
- NOTUSEDERROR
- spec = &(sym->etype->select.s);
- if(spec->noun == V_VOID)
- size = 0;
- else if(spec->noun == V_CHAR)
- size = 1;
- else if(spec->noun == V_INT && !(spec->b_long))
- size = 2;
- else
- size = 4;
-
- // Check for returned pointer.
- sym_lnk = sym->type;
- while (sym_lnk && !IS_PTR (sym_lnk))
- sym_lnk = sym_lnk->next;
- if(IS_PTR(sym_lnk))
- size = 2;
- }
- else
- {
- NOTUSEDERROR
- size = 4;
- }
-
- switch(*what)
- {
- case 'd':
- return(size >= 4);
- case 'e':
- return(size >= 3);
- case 'h':
- return(size >= 2);
- case 'l':
- return(size >= 1);
- default:
- return FALSE;
- }
-}
-
-/*-----------------------------------------------------------------*/
-/* incLabelJmpToCount - increment counter "jmpToCount" in entry */
-/* of the list labelHash */
-/*-----------------------------------------------------------------*/
-static bool
-incLabelJmpToCount (const char *label)
-{
- labelHashEntry *entry;
-
- entry = getLabelRef (label, _G.head);
- if (!entry)
- return FALSE;
- entry->jmpToCount++;
- return TRUE;
-}
-
-/*-----------------------------------------------------------------*/
-/* findLabel - */
-/* 1. extracts label in the opcode pl */
-/* 2. increment "label jump-to count" in labelHash */
-/* 3. search lineNode with label definition and return it */
-/*-----------------------------------------------------------------*/
-static lineNode *
-findLabel (const lineNode *pl)
-{
- char *p;
- lineNode *cpl;
-
- /* 1. extract label in opcode */
-
- /* In each mcs51 jumping opcode the label is at the end of the opcode */
- p = strlen (pl->line) - 1 + pl->line;
-
- /* scan backward until ',' or '\t' */
- for (; p > pl->line; p--)
- if (*p == ',' || *p == '\t')
- break;
-
- /* sanity check */
- if (p == pl->line)
- {
- NOTUSEDERROR
- return NULL;
- }
-
- /* skip ',' resp. '\t' */
- ++p;
-
- /* 2. increment "label jump-to count" */
- if (!incLabelJmpToCount (p))
- return NULL;
-
- /* 3. search lineNode with label definition and return it */
- for (cpl = _G.head; cpl; cpl = cpl->next)
- {
- if ( cpl->isLabel
- && strncmp (p, cpl->line, strlen(p)) == 0)
- {
- return cpl;
- }
- }
- return NULL;
-}
-
-static bool
-z80MightRead(const lineNode *pl, const char *what)
-{
- if(strcmp(pl->line, "call\t__initrleblock") == 0)
- return TRUE;
-
- if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0)
- what = "iy";
-
- if(strncmp(pl->line, "call\t", 5) == 0 && strchr(pl->line, ',') == 0)
- return FALSE;
-
- if(strncmp(pl->line, "ret", 3) == 0 && !isReturned(what))
- return FALSE;
-
- if(strcmp(pl->line, "ex\tde,hl") == 0 && strchr(what, 'h') == 0 && strchr(what, 'l') == 0 && strchr(what, 'd') == 0&& strchr(what, 'e') == 0)
- return FALSE;
- if(strncmp(pl->line, "ld\t", 3) == 0)
- {
- if(strstr(strchr(pl->line, ','), what) && strchr(pl->line, ',')[1] != '#')
- return TRUE;
- if(*(strchr(pl->line, ',') - 1) == ')' && strstr(pl->line + 3, what) && (strchr(pl->line, '#') == 0 || strchr(pl->line, '#') > strchr(pl->line, ',')))
- return TRUE;
- return FALSE;
- }
-
- if(strcmp(pl->line, "xor\ta,a") == 0)
- return FALSE;
-
- if(strncmp(pl->line, "adc\t", 4) == 0 ||
- strncmp(pl->line, "add\t", 4) == 0 ||
- strncmp(pl->line, "and\t", 4) == 0 ||
- strncmp(pl->line, "or\t", 3) == 0 ||
- strncmp(pl->line, "sbc\t", 4) == 0 ||
- strncmp(pl->line, "sub\t", 4) == 0 ||
- strncmp(pl->line, "xor\t", 4) == 0)
- {
- if( strstr(pl->line + 3, what) == 0 && strcmp("a", what))
- return FALSE;
- }
-
- if(strncmp(pl->line, "pop\t", 4) == 0)
- return FALSE;
-
- if(strncmp(pl->line, "push\t", 5) == 0)
- return(strstr(pl->line + 5, what) != 0);
-
- if(
- strncmp(pl->line, "dec\t", 4) == 0 ||
- strncmp(pl->line, "inc\t", 4) == 0 ||
- strncmp(pl->line, "rl\t", 3) == 0 ||
- strncmp(pl->line, "rr\t", 3) == 0 ||
- strncmp(pl->line, "sla\t", 4) == 0 ||
- strncmp(pl->line, "srl\t", 4) == 0)
- {
- return (strstr(pl->line + 3, what) != 0);
- }
-
- if(strncmp(pl->line, "jp\t", 3) == 0 ||
- (bool)(strncmp(pl->line, "jr\t", 3)) == 0)
- return FALSE;
-
- if(strncmp(pl->line, "rla", 3) == 0 ||
- strncmp(pl->line, "rlca", 4) == 0)
- return(strcmp(what, "a") == 0);
-
- return TRUE;
-}
-
-static bool
-z80UncondJump(const lineNode *pl)
-{
- if((strncmp(pl->line, "jp\t", 3) == 0 ||
- strncmp(pl->line, "jr\t", 3) == 0) && strchr(pl->line, ',') == 0)
- return TRUE;
- return FALSE;
-}
-
-static bool
-z80CondJump(const lineNode *pl)
-{
- if((strncmp(pl->line, "jp\t", 3) == 0 ||
- strncmp(pl->line, "jr\t", 3) == 0) && strchr(pl->line, ',') != 0)
- return TRUE;
- return FALSE;
-}
-
-static bool
-z80SurelyWrites(const lineNode *pl, const char *what)
-{
- if(strcmp(pl->line, "xor\ta,a") == 0 && strcmp(what, "a") == 0)
- return TRUE;
- if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, "hl", 2) == 0 && (what[0] == 'h' || what[0] == 'l'))
- return TRUE;
- if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, "de", 2) == 0 && (what[0] == 'd' || what[0] == 'e'))
- return TRUE;
- if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, "bc", 2) == 0 && (what[0] == 'b' || what[0] == 'c'))
- return TRUE;
- if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, what, strlen(what)) == 0 && pl->line[3 + strlen(what)] == ',')
- return TRUE;
- if(strncmp(pl->line, "pop\t", 4) == 0 && strstr(pl->line + 4, what))
- return TRUE;
- if(strncmp(pl->line, "call\t", 5) == 0 && strchr(pl->line, ',') == 0)
- return TRUE;
- if(strcmp(pl->line, "ret") == 0)
- return TRUE;
- if(strncmp(pl->line, "ld\tiy", 5) == 0 && strncmp(what, "iy", 2) == 0)
- return TRUE;
- return FALSE;
-}
-
-static bool
-z80SurelyReturns(const lineNode *pl)
-{
- if(strcmp(pl->line, "\tret") == 0)
- return TRUE;
- return FALSE;
-}
-
-/*-----------------------------------------------------------------*/
-/* scan4op - "executes" and examines the assembler opcodes, */
-/* follows conditional and un-conditional jumps. */
-/* Moreover it registers all passed labels. */
-/* */
-/* Parameter: */
-/* lineNode **pl */
-/* scanning starts from pl; */
-/* pl also returns the last scanned line */
-/* const char *pReg */
-/* points to a register (e.g. "ar0"). scan4op() tests for */
-/* read or write operations with this register */
-/* const char *untilOp */
-/* points to NULL or a opcode (e.g. "push"). */
-/* scan4op() returns if it hits this opcode. */
-/* lineNode **plCond */
-/* If a conditional branch is met plCond points to the */
-/* lineNode of the conditional branch */
-/* */
-/* Returns: */
-/* S4O_ABORT */
-/* on error */
-/* S4O_VISITED */
-/* hit lineNode with "visited" flag set: scan4op() already */
-/* scanned this opcode. */
-/* S4O_FOUNDOPCODE */
-/* found opcode and operand, to which untilOp and pReg are */
-/* pointing to. */
-/* S4O_RD_OP, S4O_WR_OP */
-/* hit an opcode reading or writing from pReg */
-/* S4O_CONDJMP */
-/* hit a conditional jump opcode. pl and plCond return the */
-/* two possible branches. */
-/* S4O_TERM */
-/* acall, lcall, ret and reti "terminate" a scan. */
-/*-----------------------------------------------------------------*/
-static S4O_RET
-scan4op (lineNode **pl, const char *what, const char *untilOp,
- lineNode **plCond)
-{
- for (; *pl; *pl = (*pl)->next)
- {
- if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel)
- continue;
- D(("Scanning %s for %s\n", (*pl)->line, what));
- /* don't optimize across inline assembler,
- e.g. isLabel doesn't work there */
- if ((*pl)->isInline)
- return S4O_ABORT;
-
- if ((*pl)->visited)
- return S4O_VISITED;
- (*pl)->visited = TRUE;
-
- if(z80MightRead(*pl, what))
- {
- D(("S4O_RD_OP\n"));
- return S4O_RD_OP;
- }
-
- if(z80UncondJump(*pl))
- {
- *pl = findLabel (*pl);
- if (!*pl)
- {
- D(("S4O_ABORT\n"));
- return S4O_ABORT;
- }
- }
- if(z80CondJump(*pl))
- {
- *plCond = findLabel (*pl);
- if (!*plCond)
- {
- D(("S4O_ABORT\n"));
- return S4O_ABORT;
- }
- D(("S4O_CONDJMP\n"));
- return S4O_CONDJMP;
- }
-
- if(z80SurelyWrites(*pl, what))
- {
- D(("S4O_WR_OP\n"));
- return S4O_WR_OP;
- }
-
- /* Don't need to check for de, hl since z80MightRead() does that */
- if(z80SurelyReturns(*pl))
- {
- D(("S4O_TERM\n"));
- return S4O_TERM;
- }
- }
- D(("S4O_ABORT\n"));
- return S4O_ABORT;
-}
-
-/*-----------------------------------------------------------------*/
-/* doTermScan - scan through area 2. This small wrapper handles: */
-/* - action required on different return values */
-/* - recursion in case of conditional branches */
-/*-----------------------------------------------------------------*/
-static bool
-doTermScan (lineNode **pl, const char *what)
-{
- lineNode *plConditional;
-
- for (;; *pl = (*pl)->next)
- {
- switch (scan4op (pl, what, NULL, &plConditional))
- {
- case S4O_TERM:
- case S4O_VISITED:
- case S4O_WR_OP:
- /* all these are terminating condtions */
- return TRUE;
- case S4O_CONDJMP:
- /* two possible destinations: recurse */
- {
- lineNode *pl2 = plConditional;
- D(("CONDJMP trying other branch first\n"));
- if (!doTermScan (&pl2, what))
- return FALSE;
- D(("Other branch OK.\n"));
- }
- continue;
- case S4O_RD_OP:
- default:
- /* no go */
- return FALSE;
- }
- }
-}
-
-static bool
-isReg(const char *what)
-{
- if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0)
- return TRUE;
- if(strlen(what) != 1)
- return FALSE;
- switch(*what)
- {
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'h':
- case 'l':
- return TRUE;
- }
- return FALSE;
-}
-
-static bool
-isRegPair(const char *what)
-{
- if(strlen(what) != 2)
- return FALSE;
- if(strcmp(what, "bc") == 0)
- return TRUE;
- if(strcmp(what, "de") == 0)
- return TRUE;
- if(strcmp(what, "hl") == 0)
- return TRUE;
- if(strcmp(what, "iy") == 0)
- return TRUE;
- return FALSE;
-}
-
-/* Check that what is never read after endPl. */
-
-bool
-z80notUsed (const char *what, lineNode *endPl, lineNode *head)
-{
- lineNode *pl;
- D(("Checking for %s\n", what));
- if(isRegPair(what))
- {
- char low[2], high[2];
- low[0] = what[1];
- high[0] = what[0];
- low[1] = 0;
- high[1] = 0;
- if(strcmp(what, "iy") == 0)
- return(z80notUsed("iyl", endPl, head) && z80notUsed("iyh", endPl, head));
- return(z80notUsed(low, endPl, head) && z80notUsed(high, endPl, head));
- }
-
- if(!isReg(what))
- return FALSE;
-
- _G.head = head;
-
- unvisitLines (_G.head);
-
- pl = endPl->next;
- if (!doTermScan (&pl, what))
- return FALSE;
-
- return TRUE;
-}
-
+/*-------------------------------------------------------------------------\r
+ peep.c - source file for peephole optimizer helper functions\r
+\r
+ Written By - Philipp Klaus Krause\r
+\r
+ This program is free software; you can redistribute it and/or modify it\r
+ under the terms of the GNU General Public License as published by the\r
+ Free Software Foundation; either version 2, or (at your option) any\r
+ later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program; if not, write to the Free Software\r
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+\r
+ In other words, you are welcome to use, share and improve this program.\r
+ You are forbidden to forbid anyone else to use, share and improve\r
+ what you give them. Help stamp out software-hoarding!\r
+-------------------------------------------------------------------------*/\r
+\r
+#include "common.h"\r
+#include "SDCCicode.h"\r
+#include "z80.h"\r
+#include "SDCCglobl.h"\r
+#include "SDCCpeeph.h"\r
+#include "gen.h"\r
+\r
+#define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0)\r
+\r
+/*#define D(_s) { printf _s; fflush(stdout); }*/\r
+#define D(_s)\r
+\r
+typedef enum\r
+{\r
+ S4O_CONDJMP,\r
+ S4O_WR_OP,\r
+ S4O_RD_OP,\r
+ S4O_TERM,\r
+ S4O_VISITED,\r
+ S4O_ABORT,\r
+ S4O_CONTINUE\r
+} S4O_RET;\r
+\r
+static struct\r
+{\r
+ lineNode *head;\r
+} _G;\r
+\r
+/*-----------------------------------------------------------------*/\r
+/* univisitLines - clear "visited" flag in all lines */\r
+/*-----------------------------------------------------------------*/\r
+static void\r
+unvisitLines (lineNode *pl)\r
+{\r
+ for (; pl; pl = pl->next)\r
+ pl->visited = FALSE;\r
+}\r
+\r
+#define AOP(op) op->aop\r
+#define AOP_SIZE(op) AOP(op)->size\r
+\r
+static bool\r
+isReturned(const char *what)\r
+{\r
+ symbol *sym;\r
+ sym_link *sym_lnk;\r
+ int size;\r
+ lineNode *l;\r
+\r
+ if(strncmp(what, "iy", 2) == 0)\r
+ return FALSE;\r
+ if(strlen(what) != 1)\r
+ return TRUE;\r
+\r
+ l = _G.head;\r
+ do\r
+ {\r
+ l = l->next;\r
+ } while(l->ic->op != FUNCTION);\r
+\r
+ sym = OP_SYMBOL(IC_LEFT(_G.head->next->next->ic));\r
+\r
+ if(sym && IS_DECL(sym->type))\r
+ {\r
+ // Find size of return value.\r
+ specifier *spec;\r
+ if(sym->type->select.d.dcl_type != FUNCTION)\r
+ NOTUSEDERROR();\r
+ spec = &(sym->etype->select.s);\r
+ if(spec->noun == V_VOID)\r
+ size = 0;\r
+ else if(spec->noun == V_CHAR)\r
+ size = 1;\r
+ else if(spec->noun == V_INT && !(spec->b_long))\r
+ size = 2;\r
+ else\r
+ size = 4;\r
+\r
+ // Check for returned pointer.\r
+ sym_lnk = sym->type;\r
+ while (sym_lnk && !IS_PTR (sym_lnk))\r
+ sym_lnk = sym_lnk->next;\r
+ if(IS_PTR(sym_lnk))\r
+ size = 2;\r
+ }\r
+ else\r
+ {\r
+ NOTUSEDERROR();\r
+ size = 4;\r
+ }\r
+\r
+ switch(*what)\r
+ {\r
+ case 'd':\r
+ return(size >= 4);\r
+ case 'e':\r
+ return(size >= 3);\r
+ case 'h':\r
+ return(size >= 2);\r
+ case 'l':\r
+ return(size >= 1);\r
+ default:\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------------*/\r
+/* incLabelJmpToCount - increment counter "jmpToCount" in entry */\r
+/* of the list labelHash */\r
+/*-----------------------------------------------------------------*/\r
+static bool\r
+incLabelJmpToCount (const char *label)\r
+{\r
+ labelHashEntry *entry;\r
+\r
+ entry = getLabelRef (label, _G.head);\r
+ if (!entry)\r
+ return FALSE;\r
+ entry->jmpToCount++;\r
+ return TRUE;\r
+}\r
+\r
+/*-----------------------------------------------------------------*/\r
+/* findLabel - */\r
+/* 1. extracts label in the opcode pl */\r
+/* 2. increment "label jump-to count" in labelHash */\r
+/* 3. search lineNode with label definition and return it */\r
+/*-----------------------------------------------------------------*/\r
+static lineNode *\r
+findLabel (const lineNode *pl)\r
+{\r
+ char *p;\r
+ lineNode *cpl;\r
+\r
+ /* 1. extract label in opcode */\r
+\r
+ /* In each mcs51 jumping opcode the label is at the end of the opcode */\r
+ p = strlen (pl->line) - 1 + pl->line;\r
+\r
+ /* scan backward until ',' or '\t' */\r
+ for (; p > pl->line; p--)\r
+ if (*p == ',' || *p == '\t')\r
+ break;\r
+\r
+ /* sanity check */\r
+ if (p == pl->line)\r
+ {\r
+ NOTUSEDERROR();\r
+ return NULL;\r
+ }\r
+\r
+ /* skip ',' resp. '\t' */\r
+ ++p;\r
+\r
+ /* 2. increment "label jump-to count" */\r
+ if (!incLabelJmpToCount (p))\r
+ return NULL;\r
+\r
+ /* 3. search lineNode with label definition and return it */\r
+ for (cpl = _G.head; cpl; cpl = cpl->next)\r
+ {\r
+ if ( cpl->isLabel\r
+ && strncmp (p, cpl->line, strlen(p)) == 0)\r
+ {\r
+ return cpl;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
+static bool\r
+z80MightRead(const lineNode *pl, const char *what)\r
+{\r
+ if(strcmp(pl->line, "call\t__initrleblock") == 0)\r
+ return TRUE;\r
+\r
+ if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0)\r
+ what = "iy";\r
+\r
+ if(strncmp(pl->line, "call\t", 5) == 0 && strchr(pl->line, ',') == 0)\r
+ return FALSE;\r
+\r
+ if(strncmp(pl->line, "ret", 3) == 0 && !isReturned(what))\r
+ return FALSE;\r
+\r
+ if(strcmp(pl->line, "ex\tde,hl") == 0 && strchr(what, 'h') == 0 && strchr(what, 'l') == 0 && strchr(what, 'd') == 0&& strchr(what, 'e') == 0)\r
+ return FALSE;\r
+ if(strncmp(pl->line, "ld\t", 3) == 0)\r
+ {\r
+ if(strstr(strchr(pl->line, ','), what) && strchr(pl->line, ',')[1] != '#')\r
+ return TRUE;\r
+ if(*(strchr(pl->line, ',') - 1) == ')' && strstr(pl->line + 3, what) && (strchr(pl->line, '#') == 0 || strchr(pl->line, '#') > strchr(pl->line, ',')))\r
+ return TRUE;\r
+ return FALSE;\r
+ }\r
+\r
+ if(strcmp(pl->line, "xor\ta,a") == 0)\r
+ return FALSE;\r
+\r
+ if(strncmp(pl->line, "adc\t", 4) == 0 ||\r
+ strncmp(pl->line, "add\t", 4) == 0 ||\r
+ strncmp(pl->line, "and\t", 4) == 0 ||\r
+ strncmp(pl->line, "or\t", 3) == 0 ||\r
+ strncmp(pl->line, "sbc\t", 4) == 0 ||\r
+ strncmp(pl->line, "sub\t", 4) == 0 ||\r
+ strncmp(pl->line, "xor\t", 4) == 0)\r
+ {\r
+ if( strstr(pl->line + 3, what) == 0 && strcmp("a", what))\r
+ return FALSE;\r
+ }\r
+\r
+ if(strncmp(pl->line, "pop\t", 4) == 0)\r
+ return FALSE;\r
+\r
+ if(strncmp(pl->line, "push\t", 5) == 0)\r
+ return(strstr(pl->line + 5, what) != 0);\r
+\r
+ if(\r
+ strncmp(pl->line, "dec\t", 4) == 0 ||\r
+ strncmp(pl->line, "inc\t", 4) == 0 ||\r
+ strncmp(pl->line, "rl\t", 3) == 0 ||\r
+ strncmp(pl->line, "rr\t", 3) == 0 || \r
+ strncmp(pl->line, "sla\t", 4) == 0 ||\r
+ strncmp(pl->line, "srl\t", 4) == 0)\r
+ {\r
+ return (strstr(pl->line + 3, what) != 0);\r
+ }\r
+\r
+ if(strncmp(pl->line, "jp\t", 3) == 0 ||\r
+ (bool)(strncmp(pl->line, "jr\t", 3)) == 0)\r
+ return FALSE;\r
+\r
+ if(strncmp(pl->line, "rla", 3) == 0 ||\r
+ strncmp(pl->line, "rlca", 4) == 0)\r
+ return(strcmp(what, "a") == 0);\r
+\r
+ return TRUE;\r
+}\r
+\r
+static bool\r
+z80UncondJump(const lineNode *pl)\r
+{\r
+ if((strncmp(pl->line, "jp\t", 3) == 0 ||\r
+ strncmp(pl->line, "jr\t", 3) == 0) && strchr(pl->line, ',') == 0)\r
+ return TRUE;\r
+ return FALSE;\r
+}\r
+\r
+static bool\r
+z80CondJump(const lineNode *pl)\r
+{\r
+ if((strncmp(pl->line, "jp\t", 3) == 0 ||\r
+ strncmp(pl->line, "jr\t", 3) == 0) && strchr(pl->line, ',') != 0)\r
+ return TRUE;\r
+ return FALSE;\r
+}\r
+\r
+static bool\r
+z80SurelyWrites(const lineNode *pl, const char *what)\r
+{\r
+ if(strcmp(pl->line, "xor\ta,a") == 0 && strcmp(what, "a") == 0)\r
+ return TRUE;\r
+ if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, "hl", 2) == 0 && (what[0] == 'h' || what[0] == 'l'))\r
+ return TRUE;\r
+ if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, "de", 2) == 0 && (what[0] == 'd' || what[0] == 'e'))\r
+ return TRUE;\r
+ if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, "bc", 2) == 0 && (what[0] == 'b' || what[0] == 'c'))\r
+ return TRUE;\r
+ if(strncmp(pl->line, "ld\t", 3) == 0 && strncmp(pl->line + 3, what, strlen(what)) == 0 && pl->line[3 + strlen(what)] == ',')\r
+ return TRUE;\r
+ if(strncmp(pl->line, "pop\t", 4) == 0 && strstr(pl->line + 4, what))\r
+ return TRUE;\r
+ if(strncmp(pl->line, "call\t", 5) == 0 && strchr(pl->line, ',') == 0)\r
+ return TRUE;\r
+ if(strcmp(pl->line, "ret") == 0)\r
+ return TRUE;\r
+ if(strncmp(pl->line, "ld\tiy", 5) == 0 && strncmp(what, "iy", 2) == 0)\r
+ return TRUE;\r
+ return FALSE;\r
+}\r
+\r
+static bool\r
+z80SurelyReturns(const lineNode *pl)\r
+{\r
+ if(strcmp(pl->line, "\tret") == 0)\r
+ return TRUE;\r
+ return FALSE;\r
+}\r
+\r
+/*-----------------------------------------------------------------*/\r
+/* scan4op - "executes" and examines the assembler opcodes, */\r
+/* follows conditional and un-conditional jumps. */\r
+/* Moreover it registers all passed labels. */\r
+/* */\r
+/* Parameter: */\r
+/* lineNode **pl */\r
+/* scanning starts from pl; */\r
+/* pl also returns the last scanned line */\r
+/* const char *pReg */\r
+/* points to a register (e.g. "ar0"). scan4op() tests for */\r
+/* read or write operations with this register */\r
+/* const char *untilOp */\r
+/* points to NULL or a opcode (e.g. "push"). */\r
+/* scan4op() returns if it hits this opcode. */\r
+/* lineNode **plCond */\r
+/* If a conditional branch is met plCond points to the */\r
+/* lineNode of the conditional branch */\r
+/* */\r
+/* Returns: */\r
+/* S4O_ABORT */\r
+/* on error */\r
+/* S4O_VISITED */\r
+/* hit lineNode with "visited" flag set: scan4op() already */\r
+/* scanned this opcode. */\r
+/* S4O_FOUNDOPCODE */\r
+/* found opcode and operand, to which untilOp and pReg are */\r
+/* pointing to. */\r
+/* S4O_RD_OP, S4O_WR_OP */\r
+/* hit an opcode reading or writing from pReg */\r
+/* S4O_CONDJMP */\r
+/* hit a conditional jump opcode. pl and plCond return the */\r
+/* two possible branches. */\r
+/* S4O_TERM */\r
+/* acall, lcall, ret and reti "terminate" a scan. */\r
+/*-----------------------------------------------------------------*/\r
+static S4O_RET\r
+scan4op (lineNode **pl, const char *what, const char *untilOp,\r
+ lineNode **plCond)\r
+{\r
+ for (; *pl; *pl = (*pl)->next)\r
+ {\r
+ if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel)\r
+ continue;\r
+ D(("Scanning %s for %s\n", (*pl)->line, what));\r
+ /* don't optimize across inline assembler,\r
+ e.g. isLabel doesn't work there */\r
+ if ((*pl)->isInline)\r
+ return S4O_ABORT;\r
+\r
+ if ((*pl)->visited)\r
+ return S4O_VISITED;\r
+ (*pl)->visited = TRUE;\r
+\r
+ if(z80MightRead(*pl, what))\r
+ {\r
+ D(("S4O_RD_OP\n"));\r
+ return S4O_RD_OP;\r
+ }\r
+\r
+ if(z80UncondJump(*pl))\r
+ {\r
+ *pl = findLabel (*pl);\r
+ if (!*pl)\r
+ {\r
+ D(("S4O_ABORT\n"));\r
+ return S4O_ABORT;\r
+ }\r
+ }\r
+ if(z80CondJump(*pl))\r
+ {\r
+ *plCond = findLabel (*pl);\r
+ if (!*plCond)\r
+ {\r
+ D(("S4O_ABORT\n"));\r
+ return S4O_ABORT;\r
+ }\r
+ D(("S4O_CONDJMP\n"));\r
+ return S4O_CONDJMP;\r
+ }\r
+\r
+ if(z80SurelyWrites(*pl, what))\r
+ {\r
+ D(("S4O_WR_OP\n"));\r
+ return S4O_WR_OP;\r
+ }\r
+\r
+ /* Don't need to check for de, hl since z80MightRead() does that */\r
+ if(z80SurelyReturns(*pl))\r
+ {\r
+ D(("S4O_TERM\n"));\r
+ return S4O_TERM;\r
+ }\r
+ }\r
+ D(("S4O_ABORT\n"));\r
+ return S4O_ABORT;\r
+}\r
+\r
+/*-----------------------------------------------------------------*/\r
+/* doTermScan - scan through area 2. This small wrapper handles: */\r
+/* - action required on different return values */\r
+/* - recursion in case of conditional branches */\r
+/*-----------------------------------------------------------------*/\r
+static bool\r
+doTermScan (lineNode **pl, const char *what)\r
+{\r
+ lineNode *plConditional;\r
+\r
+ for (;; *pl = (*pl)->next)\r
+ {\r
+ switch (scan4op (pl, what, NULL, &plConditional))\r
+ {\r
+ case S4O_TERM:\r
+ case S4O_VISITED:\r
+ case S4O_WR_OP:\r
+ /* all these are terminating condtions */\r
+ return TRUE;\r
+ case S4O_CONDJMP:\r
+ /* two possible destinations: recurse */\r
+ {\r
+ lineNode *pl2 = plConditional;\r
+ D(("CONDJMP trying other branch first\n"));\r
+ if (!doTermScan (&pl2, what))\r
+ return FALSE;\r
+ D(("Other branch OK.\n"));\r
+ }\r
+ continue;\r
+ case S4O_RD_OP:\r
+ default:\r
+ /* no go */\r
+ return FALSE;\r
+ }\r
+ }\r
+}\r
+\r
+static bool\r
+isReg(const char *what)\r
+{\r
+ if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0)\r
+ return TRUE;\r
+ if(strlen(what) != 1)\r
+ return FALSE;\r
+ switch(*what)\r
+ {\r
+ case 'a':\r
+ case 'b':\r
+ case 'c':\r
+ case 'd':\r
+ case 'e':\r
+ case 'h':\r
+ case 'l':\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+static bool\r
+isRegPair(const char *what)\r
+{\r
+ if(strlen(what) != 2)\r
+ return FALSE;\r
+ if(strcmp(what, "bc") == 0)\r
+ return TRUE;\r
+ if(strcmp(what, "de") == 0)\r
+ return TRUE;\r
+ if(strcmp(what, "hl") == 0)\r
+ return TRUE;\r
+ if(strcmp(what, "iy") == 0)\r
+ return TRUE;\r
+ return FALSE;\r
+}\r
+\r
+/* Check that what is never read after endPl. */\r
+\r
+bool\r
+z80notUsed (const char *what, lineNode *endPl, lineNode *head)\r
+{\r
+ lineNode *pl;\r
+ D(("Checking for %s\n", what));\r
+ if(isRegPair(what))\r
+ {\r
+ char low[2], high[2];\r
+ low[0] = what[1];\r
+ high[0] = what[0];\r
+ low[1] = 0;\r
+ high[1] = 0;\r
+ if(strcmp(what, "iy") == 0)\r
+ return(z80notUsed("iyl", endPl, head) && z80notUsed("iyh", endPl, head));\r
+ return(z80notUsed(low, endPl, head) && z80notUsed(high, endPl, head));\r
+ }\r
+\r
+ if(!isReg(what))\r
+ return FALSE;\r
+\r
+ _G.head = head;\r
+\r
+ unvisitLines (_G.head);\r
+\r
+ pl = endPl->next;\r
+ if (!doTermScan (&pl, what))\r
+ return FALSE;\r
+\r
+ return TRUE;\r
+}\r
+\r