So I coded up a quick function that takes a pointer to a location and returns 1) 1-4 in the lower 3 bits with instruction length, 2 bits with the branch type (absolute, relative, return, and jp (hl)), one bit to indicate the branch is conditional, and one bit for instructions that always branch away and will never get to the next instruction.
Note that the 'rst' and 'halt' commands don't have branch information. Anyone wanting to try to use this code will want to add that if they need it.
Here it is:
// The following are found in pBranchInfo
#define BRANCH_CALL (0x08)
// relative to pc + (int8_t)(opcode+1) ALWAYS or COND
#define BRANCH_REL (0x20)
// absolute to (void*)(opcode+1) ALWAYS or COND
#define BRANCH_ABS (0x10)
// return to (void*)(sp-2) ALWAYS or COND
#define BRANCH_RET (0x30)
// to (hl) // ALWAYS but never COND
#define BRANCH_HL (0x00)
// conditional--bp necessary after instruction
#define BRANCH_COND (0x40)
// always--will never got to pc+length--certain to branch
#define BRANCH_ALWAYS (0x80)
struct InstLen
{
uint8_t mask;
uint8_t value;
uint8_t data;
};
static const struct InstLen scan00[] =
{
// ld rr, **
{
0xcf, 0x01, 3
},
// ld (hl)/a <-> **
{
0xe7, 0x22, 3
},
// ld r, *
{
0xc7, 0x06, 2
},
// jr f, *
{
0xe7, 0x20, 2 | BRANCH_REL | BRANCH_COND
},
// djnz *
{
0xff, 0x10, 2 | BRANCH_REL | BRANCH_COND
},
// jr *
{
0xff, 0x18, 2 | BRANCH_REL | BRANCH_ALWAYS
},
};
static const struct InstLen scan11[] =
{
// ret f (not detault b/c branch)
{
0xc7, 0xc0, 1 | BRANCH_RET | BRANCH_COND
},
// ret (not detault b/c branch)
{
0xff, 0xc9, 1 | BRANCH_RET | BRANCH_ALWAYS
},
// jp (hl)
{
0xff, 0xe9, 1 | BRANCH_HL | BRANCH_ALWAYS
},
// jp f, **
{
0xc7, 0xc2, 3 | BRANCH_ABS | BRANCH_COND
},
// call f, **
{
0xc7, 0xc4, 3 | BRANCH_ABS | BRANCH_COND | BRANCH_CALL
},
// jp **
{
0xff, 0xc3, 3 | BRANCH_ABS | BRANCH_ALWAYS
},
// call **
{
0xff, 0xcd, 3 | BRANCH_ABS | BRANCH_CALL
},
// arith *, *
{
0xc7, 0xc6, 2
},
// out/in(*), a
{
0xf7, 0xd3, 2
},
// bit op
{
0xff, 0xcb, 2
}
};
static const struct InstLen scanED[] =
{
// ld (rr) <-> **
{
0xc7, 0x43, 4
}
};
static const struct InstLen scanDDFD[] =
{
// ld ix, **
{
0xff, 0x21, 4
},
// ld ix <-> (**)
{
0xf7, 0x22, 4
},
// ld (ix+*), * order dep
{
0xff, 0x36, 4
},
// various
{
0x07, 0x06, 3
},
// ld (ix+*), r
{
0xb8, 0x30, 3
},
// bit ops
{
0xff, 0xcb, 3
},
};
// Returns the length in the first 3 bits and the BRANCH_* bits in the upper 5 bits
uint8_t z80InstructionLength(uint8_t* start)
{
uint8_t opcode = *start++;
const struct InstLen* scanner;
int i;
uint8_t length;
uint8_t instructionLength = 1;
if ((opcode & 0x80) == 0)
{
if ((opcode & 0x40) == 0)
{
scanner = scan00;
length = sizeof(scan00) / sizeof(scan00[0]);
}
else
{
return 1;
}
}
else
{
if ((opcode & 0x40) == 0)
{
return 1;
}
else
{
if ((opcode & 0xdf) == 0xdd)
{
scanner = scanDDFD;
length = sizeof(scanDDFD) / sizeof(scanDDFD[0]);
instructionLength = 2;
opcode = *start++;
}
else if (opcode == 0xed)
{
scanner = scanED;
length = sizeof(scanED) / sizeof(scanED[0]);
instructionLength = 2;
opcode = *start++;
}
else
{
scanner = scan11;
length = sizeof(scan11) / sizeof(scan11[0]);
}
}
}
for (i = 0; i < length; i++)
{
const struct InstLen* scan = &scanner[i];
if ((scan->mask & opcode) == scan->value)
{
instructionLength = scan->data;
break;
}
}
return instructionLength;
}
No comments:
Post a Comment