-/*-------------------------------------------------------------------------\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
+/*-------------------------------------------------------------------------
+ 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() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0)
+
+/*#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;
+}
+