mirror of
https://github.com/cc65/cc65.git
synced 2025-03-04 00:30:35 +00:00
Added backend optimizations
git-svn-id: svn://svn.cc65.org/cc65/trunk@722 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
3ef00e7d2a
commit
a1da98103a
@ -33,9 +33,11 @@
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* common */
|
||||
#include "chartype.h"
|
||||
#include "check.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
@ -90,6 +92,47 @@ static char* GetArgCopy (const char* Arg)
|
||||
|
||||
|
||||
|
||||
static int NumArg (const char* Arg, unsigned long* Num)
|
||||
/* If the given argument is numerical, convert it and return true. Otherwise
|
||||
* set Num to zero and return false.
|
||||
*/
|
||||
{
|
||||
char* End;
|
||||
unsigned long Val;
|
||||
|
||||
/* Determine the base */
|
||||
int Base = 10;
|
||||
if (*Arg == '$') {
|
||||
++Arg;
|
||||
Base = 16;
|
||||
} else if (*Arg == '%') {
|
||||
++Arg;
|
||||
Base = 2;
|
||||
}
|
||||
|
||||
/* Convert the value. strtol is not exactly what we want here, but it's
|
||||
* cheap and may be replaced by something fancier later.
|
||||
*/
|
||||
Val = strtoul (Arg, &End, Base);
|
||||
|
||||
/* Check if the conversion was successful */
|
||||
if (*End != '\0') {
|
||||
|
||||
/* Could not convert */
|
||||
*Num = 0;
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Conversion ok */
|
||||
*Num = Val;
|
||||
return 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
@ -108,8 +151,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
|
||||
E->Size = GetInsnSize (E->OPC, E->AM);
|
||||
E->Hints = 0;
|
||||
E->Arg = GetArgCopy (Arg);
|
||||
E->Num = 0;
|
||||
E->Flags = 0;
|
||||
if (NumArg (E->Arg, &E->Num)) {
|
||||
E-> Flags = CEF_NUMARG;
|
||||
} else {
|
||||
E->Flags = 0;
|
||||
}
|
||||
E->Info = D->Info;
|
||||
E->Use = D->Use;
|
||||
E->Chg = D->Chg;
|
||||
@ -118,7 +164,7 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
|
||||
GetFuncInfo (E->Arg, &E->Use, &E->Chg);
|
||||
} else {
|
||||
/* Some other instruction */
|
||||
E->Use |= GetAMUseInfo (AM);
|
||||
E->Use |= GetAMUseInfo (E->AM);
|
||||
}
|
||||
E->JumpTo = JumpTo;
|
||||
InitCollection (&E->Labels);
|
||||
@ -149,6 +195,31 @@ void FreeCodeEntry (CodeEntry* E)
|
||||
|
||||
|
||||
|
||||
void ReplaceOPC (CodeEntry* E, opc_t OPC)
|
||||
/* Replace the opcode of the instruction. This will also replace related info,
|
||||
* Size, Use and Chg, but it will NOT update any arguments or labels.
|
||||
*/
|
||||
{
|
||||
/* Get the opcode descriptor */
|
||||
const OPCDesc* D = GetOPCDesc (OPC);
|
||||
|
||||
/* Replace the opcode */
|
||||
E->OPC = OPC;
|
||||
E->Size = GetInsnSize (E->OPC, E->AM);
|
||||
E->Info = D->Info;
|
||||
E->Use = D->Use;
|
||||
E->Chg = D->Chg;
|
||||
if (E->OPC == OPC_JSR) {
|
||||
/* A subroutine call */
|
||||
GetFuncInfo (E->Arg, &E->Use, &E->Chg);
|
||||
} else {
|
||||
/* Some other instruction */
|
||||
E->Use |= GetAMUseInfo (E->AM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2)
|
||||
/* Check if both code entries are equal */
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ struct CodeEntry {
|
||||
unsigned char Size; /* Estimated size */
|
||||
unsigned char Hints; /* Hints for this entry */
|
||||
char* Arg; /* Argument as string */
|
||||
unsigned Num; /* Numeric argument */
|
||||
unsigned long Num; /* Numeric argument */
|
||||
unsigned short Flags; /* Flags */
|
||||
unsigned char Info; /* Additional code info */
|
||||
unsigned char Use; /* Registers used */
|
||||
@ -90,6 +90,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
|
||||
void FreeCodeEntry (CodeEntry* E);
|
||||
/* Free the given code entry */
|
||||
|
||||
void ReplaceOPC (CodeEntry* E, opc_t OPC);
|
||||
/* Replace the opcode of the instruction. This will also replace related info,
|
||||
* Size, Use and Chg, but it will NOT update any arguments or labels.
|
||||
*/
|
||||
|
||||
int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2);
|
||||
/* Check if both code entries are equal */
|
||||
|
||||
|
@ -33,13 +33,17 @@
|
||||
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* common */
|
||||
#include "abend.h"
|
||||
#include "print.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "asmlabel.h"
|
||||
#include "codeent.h"
|
||||
#include "codeinfo.h"
|
||||
#include "error.h"
|
||||
#include "global.h"
|
||||
#include "codeopt.h"
|
||||
|
||||
@ -56,10 +60,67 @@
|
||||
*/
|
||||
static unsigned OptChanges;
|
||||
|
||||
/* Defines for the conditions in a compare */
|
||||
typedef enum {
|
||||
CMP_INV = -1,
|
||||
CMP_EQ,
|
||||
CMP_NE,
|
||||
CMP_GT,
|
||||
CMP_GE,
|
||||
CMP_LT,
|
||||
CMP_LE,
|
||||
CMP_UGT,
|
||||
CMP_UGE,
|
||||
CMP_ULT,
|
||||
CMP_ULE
|
||||
} cmp_t;
|
||||
|
||||
/* Table with the compare suffixes */
|
||||
static const char CmpSuffixTab [][4] = {
|
||||
"eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
|
||||
};
|
||||
|
||||
/* Table used to invert a condition, indexed by condition */
|
||||
static const unsigned char CmpInvertTab [] = {
|
||||
CMP_NE, CMP_EQ,
|
||||
CMP_LE, CMP_LT, CMP_GE, CMP_GT,
|
||||
CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
|
||||
};
|
||||
|
||||
/* Table to show which compares are signed (use the N flag) */
|
||||
static const char CmpSignedTab [] = {
|
||||
0, 0, 1, 1, 1, 1, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead jumps */
|
||||
/* Helper functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static cmp_t FindCmpCond (const char* Suffix)
|
||||
/* Map a condition suffix to a code. Return the code or CMP_INV on failure */
|
||||
{
|
||||
int I;
|
||||
|
||||
/* Linear search */
|
||||
for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
|
||||
if (strcmp (Suffix, CmpSuffixTab [I]) == 0) {
|
||||
/* Found */
|
||||
return I;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return CMP_INV;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead jumps */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -109,7 +170,7 @@ static void OptDeadJumps (CodeSeg* S)
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead code */
|
||||
/* Remove dead code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -237,7 +298,7 @@ static void OptJumpCascades (CodeSeg* S)
|
||||
++OptChanges;
|
||||
|
||||
/* Done */
|
||||
goto NextEntry;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
@ -260,7 +321,7 @@ NextEntry:
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jsr/rts */
|
||||
/* Optimize jsr/rts */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -377,7 +438,7 @@ static void OptJumpTarget (CodeSeg* S)
|
||||
* move references to this label to the new label.
|
||||
*/
|
||||
if (CodeEntryHasLabel (E1)) {
|
||||
MoveCodeLabels (S, E1, T1);
|
||||
MoveCodeLabels (S, E1, T1);
|
||||
}
|
||||
|
||||
/* Remove the entry preceeding the jump */
|
||||
@ -398,44 +459,315 @@ NextEntry:
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove conditional jumps never taken */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptDeadCondBranches (CodeSeg* S)
|
||||
/* If an immidiate load of a register is followed by a conditional jump that
|
||||
* is never taken because the load of the register sets the flags in such a
|
||||
* manner, remove the conditional branch.
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have not enough */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over the entries */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get next entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a register load */
|
||||
if ((E->Info & OF_LOAD) != 0 && E->AM == AM_IMM && (E->Flags & CEF_NUMARG) != 0) {
|
||||
|
||||
bc_t BC;
|
||||
|
||||
/* Immidiate register load, get next instruction */
|
||||
CodeEntry* N = GetCodeEntry (S, I+1);
|
||||
|
||||
/* Check if the following insn is a conditional branch or if it
|
||||
* has a label attached.
|
||||
*/
|
||||
if ((N->Info & OF_CBRA) == 0 || CodeEntryHasLabel (E)) {
|
||||
/* No conditional jump or label attached, bail out */
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Get the branch condition */
|
||||
BC = GetBranchCond (N->OPC);
|
||||
|
||||
/* Check the argument against the branch condition */
|
||||
if ((BC == BC_EQ && E->Num != 0) ||
|
||||
(BC == BC_NE && E->Num == 0) ||
|
||||
(BC == BC_PL && (E->Num & 0x80) != 0) ||
|
||||
(BC == BC_MI && (E->Num & 0x80) == 0)) {
|
||||
|
||||
/* Remove the conditional branch */
|
||||
DelCodeEntry (S, I+1);
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove calls to the bool transformer subroutines */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptBoolTransforms (CodeSeg* S)
|
||||
/* Try to remove the call to boolean transformer routines where the call is
|
||||
* not really needed.
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have not enough */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over the entries */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get next entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check for a boolean transformer */
|
||||
if (E->OPC == OPC_JSR && strncmp (E->Arg, "bool", 4) == 0) {
|
||||
|
||||
cmp_t Cond;
|
||||
|
||||
/* Get the next entry */
|
||||
CodeEntry* N = GetCodeEntry (S, I+1);
|
||||
|
||||
/* Check if this is a conditional branch */
|
||||
if ((N->Info & OF_CBRA) == 0) {
|
||||
/* No conditional branch, bail out */
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Make the boolean transformer unnecessary by changing the
|
||||
* the conditional jump to evaluate the condition flags that
|
||||
* are set after the compare directly. Note: jeq jumps if
|
||||
* the condition is not met, jne jumps if the condition is met.
|
||||
*/
|
||||
Cond = FindCmpCond (E->Arg + 4);
|
||||
if (Cond == CMP_INV) {
|
||||
/* Unknown function */
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Invert the code if we jump on condition not met. */
|
||||
if (GetBranchCond (N->OPC) == BC_EQ) {
|
||||
/* Jumps if condition false, invert condition */
|
||||
Cond = CmpInvertTab [Cond];
|
||||
}
|
||||
|
||||
/* Check if we can replace the code by something better */
|
||||
switch (Cond) {
|
||||
|
||||
case CMP_EQ:
|
||||
ReplaceOPC (N, OPC_JEQ);
|
||||
break;
|
||||
|
||||
case CMP_NE:
|
||||
ReplaceOPC (N, OPC_JNE);
|
||||
break;
|
||||
|
||||
case CMP_GT:
|
||||
/* Not now ### */
|
||||
goto NextEntry;
|
||||
|
||||
case CMP_GE:
|
||||
ReplaceOPC (N, OPC_JPL);
|
||||
break;
|
||||
|
||||
case CMP_LT:
|
||||
ReplaceOPC (N, OPC_JMI);
|
||||
break;
|
||||
|
||||
case CMP_LE:
|
||||
/* Not now ### */
|
||||
goto NextEntry;
|
||||
|
||||
case CMP_UGT:
|
||||
/* Not now ### */
|
||||
goto NextEntry;
|
||||
|
||||
case CMP_UGE:
|
||||
ReplaceOPC (N, OPC_JCS);
|
||||
break;
|
||||
|
||||
case CMP_ULT:
|
||||
ReplaceOPC (N, OPC_JCC);
|
||||
break;
|
||||
|
||||
case CMP_ULE:
|
||||
/* Not now ### */
|
||||
goto NextEntry;
|
||||
|
||||
default:
|
||||
Internal ("Unknown jump condition: %d", Cond);
|
||||
|
||||
}
|
||||
|
||||
/* Remove the call to the bool transformer */
|
||||
DelCodeEntry (S, I);
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Table with all the optimization functions */
|
||||
typedef struct OptFunc OptFunc;
|
||||
struct OptFunc {
|
||||
void (*Func) (CodeSeg*); /* Optimizer function */
|
||||
const char* Name; /* Name of optimizer step */
|
||||
char Disabled; /* True if pass disabled */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Table with optimizer steps - are called in this order */
|
||||
static OptFunc OptFuncs [] = {
|
||||
/* Optimize jump cascades */
|
||||
{ OptJumpCascades, "OptJumpCascades", 0 },
|
||||
/* Remove dead jumps */
|
||||
{ OptDeadJumps, "OptDeadJumps", 0 },
|
||||
/* Change jsr/rts to jmp */
|
||||
{ OptRTS, "OptRTS", 0 },
|
||||
/* Remove dead code */
|
||||
{ OptDeadCode, "OptDeadCode", 0 },
|
||||
/* Optimize jump targets */
|
||||
{ OptJumpTarget, "OptJumpTarget", 0 },
|
||||
/* Remove dead conditional branches */
|
||||
{ OptDeadCondBranches, "OptDeadCondBranches", 0 },
|
||||
/* Remove calls to the bool transformer subroutines */
|
||||
{ OptBoolTransforms, "OptBoolTransforms", 0 },
|
||||
};
|
||||
|
||||
|
||||
|
||||
static OptFunc* FindOptStep (const char* Name)
|
||||
/* Find an optimizer step by name in the table and return a pointer. Print an
|
||||
* error and cann AbEnd if not found.
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Run all optimization steps */
|
||||
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
||||
if (strcmp (OptFuncs[I].Name, Name) == 0) {
|
||||
/* Found */
|
||||
return OptFuncs+I;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
AbEnd ("Optimization step `%s' not found", Name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DisableOpt (const char* Name)
|
||||
/* Disable the optimization with the given name */
|
||||
{
|
||||
OptFunc* F = FindOptStep (Name);
|
||||
F->Disabled = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EnableOpt (const char* Name)
|
||||
/* Enable the optimization with the given name */
|
||||
{
|
||||
OptFunc* F = FindOptStep (Name);
|
||||
F->Disabled = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RunOpt (CodeSeg* S)
|
||||
/* Run the optimizer */
|
||||
{
|
||||
typedef void (*OptFunc) (CodeSeg*);
|
||||
unsigned Pass = 0;
|
||||
|
||||
/* Table with optimizer steps - are called in this order */
|
||||
static const OptFunc OptFuncs [] = {
|
||||
OptJumpCascades, /* Optimize jump cascades */
|
||||
OptDeadJumps, /* Remove dead jumps */
|
||||
OptDeadCode, /* Remove dead code */
|
||||
OptRTS, /* Change jsr/rts to jmp */
|
||||
OptJumpTarget, /* Optimize jump targets */
|
||||
};
|
||||
/* Print the name of the function we are working on */
|
||||
if (S->Func) {
|
||||
Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
|
||||
} else {
|
||||
Print (stdout, 1, "Running optimizer for global code segment\n");
|
||||
}
|
||||
|
||||
/* Repeat all steps until there are no more changes */
|
||||
do {
|
||||
|
||||
unsigned long Flags;
|
||||
unsigned I;
|
||||
unsigned I;
|
||||
|
||||
/* Reset the number of changes */
|
||||
OptChanges = 0;
|
||||
|
||||
/* Keep the user hapy */
|
||||
Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
|
||||
|
||||
/* Run all optimization steps */
|
||||
Flags = 1UL;
|
||||
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
||||
if ((OptDisable & Flags) == 0) {
|
||||
OptFuncs[I] (S);
|
||||
} else if (Verbosity > 0 || Debug) {
|
||||
printf ("Optimizer pass %u skipped\n", I);
|
||||
|
||||
/* Print the name of the following optimizer step */
|
||||
Print (stdout, 1, " %s:%*s", OptFuncs[I].Name,
|
||||
(int) (30-strlen(OptFuncs[I].Name)), "");
|
||||
|
||||
/* Check if the step is disabled */
|
||||
if (OptFuncs[I].Disabled) {
|
||||
Print (stdout, 1, "Disabled\n");
|
||||
} else {
|
||||
unsigned Changes = OptChanges;
|
||||
OptFuncs[I].Func (S);
|
||||
Changes = OptChanges - Changes;
|
||||
Print (stdout, 1, "%u Changes\n", Changes);
|
||||
}
|
||||
Flags <<= 1;
|
||||
}
|
||||
|
||||
} while (OptChanges > 0);
|
||||
|
@ -55,6 +55,12 @@
|
||||
|
||||
|
||||
|
||||
void DisableOpt (const char* Name);
|
||||
/* Disable the optimization with the given name */
|
||||
|
||||
void EnableOpt (const char* Name);
|
||||
/* Enable the optimization with the given name */
|
||||
|
||||
void RunOpt (CodeSeg* S);
|
||||
/* Run the optimizer */
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
/* cc65 */
|
||||
#include "asmcode.h"
|
||||
#include "compile.h"
|
||||
#include "codeopt.h"
|
||||
#include "cpu.h"
|
||||
#include "error.h"
|
||||
#include "global.h"
|
||||
@ -103,6 +104,8 @@ static void Usage (void)
|
||||
" --data-name seg\tSet the name of the DATA segment\n"
|
||||
" --debug\t\tDebug mode\n"
|
||||
" --debug-info\t\tAdd debug info to object file\n"
|
||||
" --disable-opt name\tDisable an optimization step\n"
|
||||
" --enable-opt name\tEnable an optimization step\n"
|
||||
" --help\t\tHelp (this text)\n"
|
||||
" --include-dir dir\tSet an include directory search path\n"
|
||||
" --rodata-name seg\tSet the name of the RODATA segment\n"
|
||||
@ -377,6 +380,22 @@ static void OptDebugInfo (const char* Opt, const char* Arg)
|
||||
|
||||
|
||||
|
||||
static void OptDisableOpt (const char* Opt, const char* Arg)
|
||||
/* Disable an optimization step */
|
||||
{
|
||||
DisableOpt (Arg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void OptEnableOpt (const char* Opt, const char* Arg)
|
||||
/* Enable an optimization step */
|
||||
{
|
||||
EnableOpt (Arg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void OptHelp (const char* Opt, const char* Arg)
|
||||
/* Print usage information and exit */
|
||||
{
|
||||
@ -463,6 +482,8 @@ int main (int argc, char* argv[])
|
||||
{ "--data-name", 1, OptDataName },
|
||||
{ "--debug", 0, OptDebug },
|
||||
{ "--debug-info", 0, OptDebugInfo },
|
||||
{ "--disable-opt", 1, OptDisableOpt },
|
||||
{ "--enable-opt", 1, OptEnableOpt, },
|
||||
{ "--help", 0, OptHelp },
|
||||
{ "--include-dir", 1, OptIncludeDir },
|
||||
{ "--rodata-name", 1, OptRodataName },
|
||||
|
@ -96,9 +96,9 @@ static const OPCDesc OPCTable[OPC_COUNT] = {
|
||||
{ OPC_JSR, "jsr", 3, REG_NONE, REG_NONE, OF_NONE },
|
||||
{ OPC_JVC, "jvc", 5, REG_NONE, REG_NONE, OF_CBRA },
|
||||
{ OPC_JVS, "jvs", 5, REG_NONE, REG_NONE, OF_CBRA },
|
||||
{ OPC_LDA, "lda", 0, REG_NONE, REG_A, OF_NONE },
|
||||
{ OPC_LDX, "ldx", 0, REG_NONE, REG_X, OF_NONE },
|
||||
{ OPC_LDY, "ldy", 0, REG_NONE, REG_Y, OF_NONE },
|
||||
{ OPC_LDA, "lda", 0, REG_NONE, REG_A, OF_LOAD },
|
||||
{ OPC_LDX, "ldx", 0, REG_NONE, REG_X, OF_LOAD },
|
||||
{ OPC_LDY, "ldy", 0, REG_NONE, REG_Y, OF_LOAD },
|
||||
{ OPC_LSR, "lsr", 0, REG_A, REG_A, OF_NONE },
|
||||
{ OPC_NOP, "nop", 1, REG_NONE, REG_NONE, OF_NONE },
|
||||
{ OPC_ORA, "ora", 0, REG_A, REG_A, OF_NONE },
|
||||
|
@ -156,6 +156,7 @@ typedef enum {
|
||||
#define OF_UBRA 0x0001U /* Unconditional branch */
|
||||
#define OF_CBRA 0x0002U /* Conditional branch */
|
||||
#define OF_RET 0x0004U /* Return from function */
|
||||
#define OF_LOAD 0x0008U /* Register load */
|
||||
#define OF_BRA (OF_UBRA|OF_CBRA) /* Operation is a jump/branch */
|
||||
#define OF_DEAD (OF_UBRA|OF_RET) /* Dead end - no exec behind this point */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user