executor/util/disasm/label.c

303 lines
6.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "disasm.h"
#include "label.h"
#include "hash.h"
static const struct
{
const char *orig_name;
const char *canon_name;
} branches[] =
{
{ "bhi", "bhi" },
{ "bls", "bls" },
{ "bcc", "bcc" },
{ "bcs", "bcs" },
{ "bne", "bne" },
{ "beq", "beq" },
{ "bvc", "bvc" },
{ "bvs", "bvs" },
{ "bpl", "bpl" },
{ "bmi", "bmi" },
{ "bge", "bge" },
{ "blt", "blt" },
{ "bgt", "bgt" },
{ "ble", "ble" },
{ "bsr", "bsr" },
{ "bra", "bra" },
{ "jmp", "jmp" },
{ "jsr", "jsr" },
{ "bhis", "bhi" },
{ "blss", "bls" },
{ "bccs", "bcc" },
{ "bcss", "bcs" },
{ "bnes", "bne" },
{ "beqs", "beq" },
{ "bvcs", "bvc" },
{ "bvss", "bvs" },
{ "bpls", "bpl" },
{ "bmis", "bmi" },
{ "bges", "bge" },
{ "blts", "blt" },
{ "bgts", "bgt" },
{ "bles", "ble" },
{ "bsrs", "bsr" },
{ "bras", "bra" },
{ "jmps", "jmp" },
{ "jsrs", "jsr" },
{ "bhil", "bhi" },
{ "blsl", "bls" },
{ "bccl", "bcc" },
{ "bcsl", "bcs" },
{ "bnel", "bne" },
{ "beql", "beq" },
{ "bvcl", "bvc" },
{ "bvsl", "bvs" },
{ "bpll", "bpl" },
{ "bmil", "bmi" },
{ "bgel", "bge" },
{ "bltl", "blt" },
{ "bgtl", "bgt" },
{ "blel", "ble" },
{ "bsrl", "bsr" },
{ "bral", "bra" },
{ "jmpl", "jmp" },
{ "jsrl", "jsr" },
{ "dbhi", "dbhi" },
{ "dbls", "dbls" },
{ "dbcc", "dbcc" },
{ "dbcs", "dbcs" },
{ "dbne", "dbne" },
{ "dbeq", "dbeq" },
{ "dbvc", "dbvc" },
{ "dbvs", "dbvs" },
{ "dbpl", "dbpl" },
{ "dbmi", "dbmi" },
{ "dbge", "dbge" },
{ "dblt", "dblt" },
{ "dbgt", "dbgt" },
{ "dble", "dble" },
{ "dbra", "dbra" },
{ "dbf", "dbra" },
{ "dbt", "dbt" },
};
/* Moves on to the start of the next line. */
static char *
next_line (char *s)
{
while (*s && *s != '\n')
s++;
if (*s == '\n')
s++;
return s;
}
static char *
extract_branch_target (const char *opcode, char *operand)
{
char *start;
int i;
/* Skip leading @# that gdb throws in for PC relative jsr's. */
if (!strcmp (opcode, "jsr")
&& operand[0] == '@'
&& operand[1] == '#')
operand += 2;
if (operand[0] == '0' && operand[1] == 'x')
start = operand + 2;
else if (operand[0] == 'd' && operand[1] >= '0' && operand[1] <= '7'
&& operand[2] == ',' && operand[3] == '0' && operand[4] == 'x')
start = operand + 5;
else
return NULL;
for (i = 0; start[i] != '\0'; i++)
if (!isxdigit (start[i]))
return NULL;
return start - 2;
}
static int
call_p (const char *opcode)
{
return (!strncmp (opcode, "bsr", 3)
|| !strncmp (opcode, "jsr", 3));
}
char *
add_labels (char *code, unsigned long entry_point, int keep_addrs_p)
{
static hash_table_t *branch_hash = NULL;
char *s, *d, *new, *final, buf[32];
hash_table_t *label_hash;
unsigned long label_num;
char addr[1024], opcode[1024], operand[1024], junk[1024];
const char *label;
if (code == NULL)
return NULL;
/* Set up the branch hash table (if we haven't already). */
if (branch_hash == NULL)
{
int i;
branch_hash = hash_new ();
for (i = 0; i < sizeof branches / sizeof branches[0]; i++)
hash_insert (branch_hash, branches[i].orig_name,
branches[i].canon_name);
}
/* Phase 1: identify all branch targets. */
label_hash = hash_new ();
sprintf (buf, "0x%lx", entry_point);
hash_insert (label_hash, buf, "_main");
for (s = code; *s != '\0'; s = next_line (s))
{
if (extract_field (s, 1, opcode)
&& hash_lookup (branch_hash, opcode)
&& extract_field (s, 2, operand)
&& extract_branch_target (opcode, operand)
&& !extract_field (s, 3, junk))
{
hash_insert (label_hash, extract_branch_target (opcode, operand),
call_p (opcode) ? "F" : "L");
}
}
/* Phase 2: assign sorted, contiguous label names. */
for (s = code, label_num = 0; *s != '\0'; s = next_line (s))
{
if (extract_field (s, 0, addr))
{
const char *s = hash_lookup (label_hash, addr);
if (s != NULL && s[1] == '\0') /* Don't overwrite "_main". */
{
sprintf (buf, "%c%lu_", s[0], label_num++);
hash_remove (label_hash, addr);
hash_insert (label_hash, addr, buf);
}
}
}
/* Phase 3: change all branch targets to reflect the new labels,
* replace all branch instructions with their canonical
* opcode name, and copy the input to the output.
*/
new = (char *)malloc (100 + strlen (code) * 2);
for (s = code, d = new; *s != '\0'; )
{
int changed_p;
changed_p = FALSE;
if (extract_field (s, 1, opcode)
&& hash_lookup (branch_hash, opcode)
&& extract_field (s, 2, operand)
&& extract_branch_target (opcode, operand)
&& !extract_field (s, 3, junk))
{
label = hash_lookup (label_hash,
extract_branch_target (opcode, operand));
if (label != NULL && label[0] != '\0' && label[1] != '\0')
{
if (!extract_field (s, 0, addr))
abort ();
sprintf (d, "%s %s ", addr, opcode);
d += strlen (d);
/* If it's a dbra, etc. then copy the data register specifier. */
if (operand[0] == 'd')
{
strncpy (d, operand, 3);
d += 3;
}
/* Replace the raw address with the target label. */
strcpy (d, label);
d += strlen (d);
*d++ = '\n';
s = next_line (s);
changed_p = TRUE;
}
}
/* If we didn't rewrite the branch target above, just copy the line. */
if (!changed_p)
{
while (*s != '\n' && *s != '\0')
*d++ = *s++;
if (*s == '\n')
*d++ = *s++;
}
}
*d = '\0';
/* Phase 4: print out every line with leading labels and canonicalize
* all opcodes.
*/
free (code);
final = (char *)malloc (100 + strlen (new) * 2);
for (s = new, d = final; *s != '\0'; s = next_line (s))
{
int f;
if (!extract_field (s, 0, addr))
abort ();
/* Separate call targets with blank lines. */
label = hash_lookup (label_hash, addr);
if (label != NULL && label[0] == 'F')
*d++ = '\n';
if (keep_addrs_p)
{
sprintf (d, "%s\t", addr);
d += strlen (d);
}
/* Print out any label for this line. */
if (label != NULL)
{
sprintf (d, "%s:", label);
d += strlen (d);
}
/* Print out the opcode. */
if (extract_field (s, 1, opcode))
{
const char *canon_opcode;
canon_opcode = hash_lookup (branch_hash, opcode);
sprintf (d, "\t%s", canon_opcode ? canon_opcode : opcode);
d += strlen (d);
}
/* Dump out the rest of the operands, separated by spaces. */
for (f = 2; extract_field (s, f, operand); f++)
{
sprintf (d, "%s%s", (f == 2) ? "\t" : " ", operand);
d += strlen (d);
}
*d++ = '\n';
}
*d = '\0';
free (new);
hash_free (label_hash);
return (char *)realloc (final, strlen (final) + 1);
}