From 0bfa13722bb72d8e8f99606f3ecddeedf360149c Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:23:02 +0800 Subject: [PATCH] More funcinfo on register usage fixes. --- src/cc65/codeent.c | 4 + src/cc65/codeinfo.c | 586 ++++++++++++++++++++++------------------- src/cc65/codeinfo.h | 3 + src/cc65/codeoptutil.c | 105 +++++--- 4 files changed, 386 insertions(+), 312 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index dd2000db0..4f257a22e 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -172,6 +172,10 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) if (Info && Info->ByteUse != REG_NONE) { /* These addressing modes will never change the zp loc */ E->Use |= Info->WordUse; + + if ((E->Use & REG_SP) != 0) { + E->Use |= SLV_IND; + } } break; diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 46a7d76c6..88f8a5138 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -82,269 +82,296 @@ struct FuncInfo { unsigned Chg; /* Changed/destroyed registers */ }; -/* Note for the shift functions: Shifts are done modulo 32, so all shift +/* Functions that change the SP are regarded as using the SP as well. +** The callax/jmpvec functions may call a function that uses/changes more +** registers, so we should further check the info of the called function +** or just play it safe. +** Note for the shift functions: Shifts are done modulo 32, so all shift ** routines are marked to use only the A register. The remainder is ignored ** anyway. */ static const FuncInfo FuncInfoTable[] = { - { "addeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, - { "addeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, - { "addysp", REG_Y, PSTATE_ALL | REG_NONE }, - { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "bcasta", REG_A, PSTATE_ALL | REG_AX }, - { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, - { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "bnega", REG_A, PSTATE_ALL | REG_AX }, - { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, - { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, - { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, - { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, - { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, - { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, - { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, - { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, - { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, - { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, - { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, - { "callax", REG_AX, PSTATE_ALL | REG_ALL }, - { "complax", REG_AX, PSTATE_ALL | REG_AX }, - { "decax1", REG_AX, PSTATE_ALL | REG_AX }, - { "decax2", REG_AX, PSTATE_ALL | REG_AX }, - { "decax3", REG_AX, PSTATE_ALL | REG_AX }, - { "decax4", REG_AX, PSTATE_ALL | REG_AX }, - { "decax5", REG_AX, PSTATE_ALL | REG_AX }, - { "decax6", REG_AX, PSTATE_ALL | REG_AX }, - { "decax7", REG_AX, PSTATE_ALL | REG_AX }, - { "decax8", REG_AX, PSTATE_ALL | REG_AX }, - { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, - { "decsp1", REG_NONE, PSTATE_ALL | REG_Y }, - { "decsp2", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp3", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp4", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp5", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp6", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp7", REG_NONE, PSTATE_ALL | REG_A }, - { "decsp8", REG_NONE, PSTATE_ALL | REG_A }, - { "incax1", REG_AX, PSTATE_ALL | REG_AX }, - { "incax2", REG_AX, PSTATE_ALL | REG_AX }, - { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "incsp1", REG_NONE, PSTATE_ALL | REG_NONE }, - { "incsp2", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp3", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp4", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp5", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp6", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp7", REG_NONE, PSTATE_ALL | REG_Y }, - { "incsp8", REG_NONE, PSTATE_ALL | REG_Y }, - { "laddeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "laddeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, - { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "laddeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, - { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "ldax0sp", REG_NONE, PSTATE_ALL | REG_AXY }, - { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "ldaxysp", REG_Y, PSTATE_ALL | REG_AXY }, - { "ldeax0sp", REG_NONE, PSTATE_ALL | REG_EAXY }, - { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, - { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, - { "ldeaxysp", REG_Y, PSTATE_ALL | REG_EAXY }, - { "leaa0sp", REG_A, PSTATE_ALL | REG_AX }, - { "leaaxsp", REG_AX, PSTATE_ALL | REG_AX }, - { "lsubeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "lsubeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, - { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, - { "lsubeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, - { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, - { "negax", REG_AX, PSTATE_ALL | REG_AX }, - { "push0", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push0ax", REG_AX, PSTATE_ALL | REG_Y | REG_SREG }, - { "push1", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push2", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push3", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push4", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push5", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push6", REG_NONE, PSTATE_ALL | REG_AXY }, - { "push7", REG_NONE, PSTATE_ALL | REG_AXY }, - { "pusha", REG_A, PSTATE_ALL | REG_Y }, - { "pusha0", REG_A, PSTATE_ALL | REG_XY }, - { "pusha0sp", REG_NONE, PSTATE_ALL | REG_AY }, - { "pushaFF", REG_A, PSTATE_ALL | REG_Y }, - { "pushax", REG_AX, PSTATE_ALL | REG_Y }, - { "pushaysp", REG_Y, PSTATE_ALL | REG_AY }, - { "pushc0", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, - { "pushc1", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, - { "pushc2", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, - { "pusheax", REG_EAX, PSTATE_ALL | REG_Y }, - { "pushl0", REG_NONE, PSTATE_ALL | REG_AXY }, - { "pushw", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "pushw0sp", REG_NONE, PSTATE_ALL | REG_AXY }, - { "pushwidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "pushwysp", REG_Y, PSTATE_ALL | REG_AXY }, - { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "regswap1", REG_XY, PSTATE_ALL | REG_A }, - { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, - { "return0", REG_NONE, PSTATE_ALL | REG_AX }, - { "return1", REG_NONE, PSTATE_ALL | REG_AX }, - { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, - { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, - { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "staspidx", REG_A | REG_Y, PSTATE_ALL | REG_Y | REG_TMP1 | REG_PTR1 }, - { "stax0sp", REG_AX, PSTATE_ALL | REG_Y }, - { "staxspidx", REG_AXY, PSTATE_ALL | REG_TMP1 | REG_PTR1 }, - { "staxysp", REG_AXY, PSTATE_ALL | REG_Y }, - { "steax0sp", REG_EAX, PSTATE_ALL | REG_Y }, - { "steaxysp", REG_EAXY, PSTATE_ALL | REG_Y }, - { "subeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, - { "subeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, - { "subysp", REG_Y, PSTATE_ALL | REG_AY }, - { "tosadd0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosadda0", REG_A, PSTATE_ALL | REG_AXY }, - { "tosaddax", REG_AX, PSTATE_ALL | REG_AXY }, - { "tosaddeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosand0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosanda0", REG_A, PSTATE_ALL | REG_AXY }, - { "tosandax", REG_AX, PSTATE_ALL | REG_AXY }, - { "tosandeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosaslax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosasleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosasrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosasreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosdiv0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosdiva0", REG_A, PSTATE_ALL | REG_ALL }, - { "tosdivax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosdiveax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "toseq00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toseqa0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toseqax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toseqeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosgt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosgteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosicmp", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosicmp0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslcmp", REG_EAX, PSTATE_ALL | REG_A | REG_Y | REG_PTR1 }, - { "tosle00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "toslt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosltax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "toslteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosmod0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosmodeax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "tosmul0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosmula0", REG_A, PSTATE_ALL | REG_ALL }, - { "tosmulax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosmuleax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "tosne00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosnea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosneax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosneeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosrsub0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosrsuba0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosrsubax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosrsubeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosshlax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosshleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosshrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosshreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tossub0ax", REG_AX, PSTATE_ALL | REG_EAXY }, - { "tossuba0", REG_A, PSTATE_ALL | REG_AXY }, - { "tossubax", REG_AX, PSTATE_ALL | REG_AXY }, - { "tossubeax", REG_EAX, PSTATE_ALL | REG_EAXY }, - { "tosudiv0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosudiva0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudivax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudiveax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosuge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosugt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosugteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosule00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosulea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosuleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosuleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosult00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosulta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosultax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, - { "tosulteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, - { "tosumod0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosumoda0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodeax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, - { "tosumul0ax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosumula0", REG_A, PSTATE_ALL | REG_ALL }, - { "tosumulax", REG_AX, PSTATE_ALL | REG_ALL }, - { "tosumuleax", REG_EAX, PSTATE_ALL | REG_ALL }, - { "tosxor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tosxora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosxorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, - { "tosxoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, - { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, - { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "addeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "addeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "addysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP }, + { "along", REG_A, PSTATE_ALL | REG_X | REG_SREG }, + { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "aulong", REG_NONE, PSTATE_ALL | REG_X | REG_SREG }, + { "axlong", REG_X, PSTATE_ALL | REG_Y | REG_SREG }, + { "axulong", REG_NONE, PSTATE_ALL | REG_Y | REG_SREG }, + { "bcasta", REG_A, PSTATE_ALL | REG_AX }, + { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, + { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "bnega", REG_A, PSTATE_ALL | REG_AX }, + { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, + { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, + { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, + { "callax", REG_AX, PSTATE_ALL | REG_ALL }, /* PSTATE_ZN | REG_PTR1 */ + { "complax", REG_AX, PSTATE_ALL | REG_AX }, + { "decax1", REG_AX, PSTATE_ALL | REG_AX }, + { "decax2", REG_AX, PSTATE_ALL | REG_AX }, + { "decax3", REG_AX, PSTATE_ALL | REG_AX }, + { "decax4", REG_AX, PSTATE_ALL | REG_AX }, + { "decax5", REG_AX, PSTATE_ALL | REG_AX }, + { "decax6", REG_AX, PSTATE_ALL | REG_AX }, + { "decax7", REG_AX, PSTATE_ALL | REG_AX }, + { "decax8", REG_AX, PSTATE_ALL | REG_AX }, + { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, + { "decsp1", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "decsp2", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp3", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp4", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp5", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp6", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp7", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp8", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "enter", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "incax1", REG_AX, PSTATE_ALL | REG_AX }, + { "incax2", REG_AX, PSTATE_ALL | REG_AX }, + { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incsp1", REG_SP, PSTATE_ALL | REG_SP }, + { "incsp2", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp3", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp4", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp5", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp6", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp7", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp8", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "jmpvec", REG_EVERYTHING, PSTATE_ALL | REG_ALL }, /* NONE */ + { "laddeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldax0sp", SLV_TOP, PSTATE_ALL | REG_AXY }, + { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_AXY }, + { "ldeax0sp", SLV_TOP, PSTATE_ALL | REG_EAXY }, + { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_EAXY }, + { "leaa0sp", REG_SP | REG_A, PSTATE_ALL | REG_AX }, + { "leaaxsp", REG_SP | REG_AX, PSTATE_ALL | REG_AX }, + { "leave00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leave0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leave", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "leavey00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leavey0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leavey", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_Y }, + { "lsubeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "negax", REG_AX, PSTATE_ALL | REG_AX }, + { "negeax", REG_EAX, PSTATE_ALL | REG_EAX }, + { "popa", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "popax", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "popeax", SLV_TOP, PSTATE_ALL | REG_SP | REG_EAXY }, + { "push0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push0ax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y | REG_SREG }, + { "push1", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push2", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push3", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push4", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push5", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push6", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push7", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pusha", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pusha0", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_XY }, + { "pusha0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "pushaFF", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pushax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushaysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "pushc0", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc1", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc2", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pusheax", REG_SP | REG_EAX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushl0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushw", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushw0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushwidx", REG_SP | REG_AXY, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushwysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AXY }, + { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "regswap1", REG_XY, PSTATE_ALL | REG_A }, + { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, + { "resteax", REG_SAVE, PSTATE_ZN | REG_EAX }, /* also uses regsave+2/+3 */ + { "return0", REG_NONE, PSTATE_ALL | REG_AX }, + { "return1", REG_NONE, PSTATE_ALL | REG_AX }, + { "saveeax", REG_EAX, PSTATE_ZN | REG_Y | REG_SAVE }, /* also regsave+2/+3 */ + { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "staspidx", SLV_TOP | REG_AY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, + { "stax0sp", REG_SP | REG_AX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "staxspidx", SLV_TOP | REG_AXY, PSTATE_ALL | REG_SP | REG_TMP1 | REG_PTR1 }, + { "staxysp", REG_SP | REG_AXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "steax0sp", REG_SP | REG_EAX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "steaxspidx", SLV_TOP | REG_EAXY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, /* also tmp2, tmp3 */ + { "steaxysp", REG_SP | REG_EAXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "subeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "subeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "subysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "swapstk", SLV_TOP | REG_AX, PSTATE_ALL | SLV_TOP | REG_AXY }, /* also ptr4 */ + { "tosadd0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosadda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosand0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosanda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosaslax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosasrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosdiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiveax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_ALL }, + { "toseq00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqa0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosgt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosicmp", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosicmp0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosint", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslcmp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_A | REG_Y | REG_PTR1 }, + { "tosle00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "toslong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosltax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosmod0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmodeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosmul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosmulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosne00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosnea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshlax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tossub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tossuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tosudiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosudiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudiveax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosuge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosugt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosule00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosulong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "tosult00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosultax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosumod0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumoda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodeax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosumulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosxor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosxora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, }; #define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0])) @@ -481,6 +508,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { FuncDesc* D = GetFuncDesc (E->Type); + *Use = REG_NONE; /* A variadic function will use the Y register (the parameter list ** size is passed there). A fastcall function will use the A or A/X @@ -488,31 +516,40 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** we assume that any function will destroy all registers. */ if ((D->Flags & FD_VARIADIC) != 0) { - *Use = REG_Y; + *Use = REG_Y | REG_SP | SLV_TOP; } else if (D->Flags & FD_CALL_WRAPPER) { /* Wrappers may go to any functions, so mark them as using all ** registers. */ *Use = REG_EAXY; - } else if ((D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) && - IsFastcallFunc (E->Type)) { + } else if (D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) { /* Will use registers depending on the last param. If the last ** param has incomplete type, or if the function has not been ** prototyped yet, just assume __EAX__. */ - if (D->LastParam != 0) { - switch (SizeOf (D->LastParam->Type)) { - case 1u: - *Use = REG_A; - break; - case 2u: - *Use = REG_AX; - break; - default: - *Use = REG_EAX; + if (IsFastcallFunc (E->Type)) { + if (D->LastParam != 0) { + switch (SizeOf (D->LastParam->Type)) { + case 1u: + *Use = REG_A; + break; + case 2u: + *Use = REG_AX; + break; + default: + *Use = REG_EAX; + } + if (D->ParamCount > 1) { + /* Passes other params on the stack */ + *Use |= REG_SP | SLV_TOP; + } + } else { + /* We'll assume all */ + *Use = REG_EAX | REG_SP | SLV_TOP; } } else { - *Use = REG_EAX; + /* Passes all params on the stack */ + *Use = REG_SP | SLV_TOP; } } else { /* Will not use any registers */ @@ -551,6 +588,9 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* Use the information we have */ *Use = Info->Use; *Chg = Info->Chg; + if ((*Use & (SLV_TOP | SLV_IND)) != 0) { + *Use |= REG_SP; + } } else { /* It's an internal function we have no information for. If in ** debug mode, output an additional warning, so we have a chance diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 88e26cdf4..14ef54d8f 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -75,6 +75,8 @@ struct RegContents; #define REG_SP_HI 0x2000U /* Defines for some special register usage */ +#define SLV_IND 0x00010000U /* Accesses (sp),y */ +#define SLV_TOP 0x00020000U /* Accesses (sp),0 */ #define SLV_SP65 0x00200000U /* Accesses 6502 stack pointer */ #define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */ #define SLV_PL65 0x00800000U /* Pops from 6502 stack */ @@ -104,6 +106,7 @@ struct RegContents; #define REG_EAXY (REG_EAX | REG_Y) #define REG_ZP 0xFFF8U #define REG_ALL 0xFFFFU + #define PSTATE_CZ (PSTATE_C | PSTATE_Z) #define PSTATE_CZN (PSTATE_C | PSTATE_Z | PSTATE_N) #define PSTATE_CZVN (PSTATE_C | PSTATE_Z | PSTATE_V | PSTATE_N) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 16c41162a..a4980aa3a 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -827,21 +827,28 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) CodeEntry* E = CS_GetEntry (D->Code, I); + /* Check against some things that should not happen */ + CHECK ((E->Use & SLV_TOP) != SLV_TOP); + /* Check if this entry does a stack access, and if so, if it's a plain ** load from stack, since this is needed later. */ int Correction = 0; - if ((E->Use & REG_SP) != 0) { + if ((E->Use & SLV_IND) == SLV_IND) { - /* Check for some things that should not happen */ - CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); - CHECK (strcmp (E->Arg, "sp") == 0); - /* We need to correct this one */ - Correction = (E->OPC == OP65_LDA)? 2 : 1; + if (E->OPC != OP65_JSR) { + /* Check against some things that should not happen */ + CHECK (E->AM == AM65_ZP_INDY && E->RI->In.RegY >= (short) Offs); + CHECK (strcmp (E->Arg, "sp") == 0); + + /* We need to correct this one */ + Correction = 2; + + } else { + /* We need to correct this one */ + Correction = 1; + } - } else if (CE_IsCallTo (E, "ldaxysp")) { - /* We need to correct this one */ - Correction = 1; } if (Correction) { @@ -849,7 +856,7 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) ** value. */ CodeEntry* P = CS_GetPrevEntry (D->Code, I); - if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) { + if (P && P->OPC == OP65_LDY && CE_IsConstImm (P) && !CE_HasLabel (E)) { /* The Y load is just before the stack access, adjust it */ CE_SetNumArg (P, P->Num - Offs); } else { @@ -860,39 +867,59 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) } /* If we need the value of Y later, be sure to reload it */ - if (RegYUsed (D->Code, I+1)) { - CodeEntry* N; + unsigned R = REG_Y | (E->Chg & ~REG_A); + R = GetRegInfo (D->Code, I + 1, R) & R; + if ((R & REG_Y) != 0) { const char* Arg = MakeHexArg (E->RI->In.RegY); - if (Correction == 2 && (N = CS_GetNextEntry(D->Code, I)) != 0 && - ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { - /* The Y register is used but the load instruction loads A - ** and is followed by a branch that evaluates the zero flag. - ** This means that we cannot just insert the load insn - ** for the Y register at this place, because it would - ** destroy the Z flag. Instead place load insns at the - ** target of the branch and after it. - ** Note: There is a chance that this code won't work. The - ** jump may be a backwards jump (in which case the stack - ** offset has already been adjusted) or there may be other - ** instructions between the load and the conditional jump. - ** Currently the compiler does not generate such code, but - ** it is possible to force the optimizer into something - ** invalid by use of inline assembler. - */ + if ((R & PSTATE_ZN) != 0 && (R & ~(REG_Y | PSTATE_ZN)) == 0) { + CodeEntry* N; + if ((N = CS_GetNextEntry (D->Code, I)) != 0 && + ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { + /* The Y register is used but the load instruction loads A + ** and is followed by a branch that evaluates the zero flag. + ** This means that we cannot just insert the load insn + ** for the Y register at this place, because it would + ** destroy the Z flag. Instead place load insns at the + ** target of the branch and after it. + ** Note: There is a chance that this code won't work. The + ** jump may be a backwards jump (in which case the stack + ** offset has already been adjusted) or there may be other + ** instructions between the load and the conditional jump. + ** Currently the compiler does not generate such code, but + ** it is possible to force the optimizer into something + ** invalid by use of inline assembler. + ** Note: In reality, this route is never taken as all + ** callers of this function will just give up with + ** optimization whenever they detect a branch. + */ - /* Add load insn after the branch */ - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+2); + /* Add load insn after the branch */ + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I+2); - /* Add load insn before branch target */ - CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); - CHECK (J > I); /* Must not happen */ - InsertEntry (D, Y, J); + /* Add load insn before branch target */ + CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); + CHECK (J > I); /* Must not happen */ + InsertEntry (D, Y, J); - /* Move the label to the new insn */ - CodeLabel* L = CS_GenLabel (D->Code, Y); - CS_MoveLabelRef (D->Code, N, L); + /* Move the label to the new insn */ + CodeLabel* L = CS_GenLabel (D->Code, Y); + CS_MoveLabelRef (D->Code, N, L); + + /* Skip the next two instructions in the next round */ + I += 2; + } else { + /* This could be suboptimal but it will always work (unless stack overflows) */ + CodeEntry* X = NewCodeEntry (OP65_PHP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, 0, 0, E->LI); + InsertEntry (D, X, I+2); + X = NewCodeEntry (OP65_PLP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+3); + /* Skip the three inserted instructions in the next round */ + I += 3; + } } else { CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); InsertEntry (D, X, I+1);