336 lines
5.3 KiB
C
336 lines
5.3 KiB
C
#include "config.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#ifdef HAVE_LIBGEN_H
|
|
# include <libgen.h>
|
|
#endif /* HAVE_LIBGEN_H */
|
|
#include "lib/op.h"
|
|
|
|
#define OP_ADDR_MODE (OP_ADDR_IMPL | OP_ADDR_REL | OP_ADDR_IND \
|
|
| OP_ADDR_IMM | OP_ADDR_ABS)
|
|
|
|
char *prg_name;
|
|
FILE *input;
|
|
FILE *output;
|
|
|
|
uint8_t
|
|
readu8(void)
|
|
{
|
|
uint8_t val;
|
|
|
|
if (fread(&val, 1, 1, input) != 1)
|
|
{
|
|
return (-1); /* XXX: TODO */
|
|
}
|
|
|
|
return (val);
|
|
}
|
|
|
|
uint16_t
|
|
readu16(void)
|
|
{
|
|
uint16_t val;
|
|
|
|
if (fread(&val, 1, 2, input) != 2)
|
|
{
|
|
return (-1); /* XXX: TODO */
|
|
}
|
|
|
|
return (val);
|
|
}
|
|
|
|
uint32_t
|
|
readu32(void)
|
|
{
|
|
uint32_t val;
|
|
|
|
if (fread(&val, 1, 4, input) != 4)
|
|
{
|
|
return (-1); /* XXX: TODO */
|
|
}
|
|
|
|
return (val);
|
|
}
|
|
|
|
int16_t
|
|
read16(void)
|
|
{
|
|
int16_t val;
|
|
|
|
if (fread(&val, 1, 2, input) != 2)
|
|
{
|
|
return (-1); /* XXX: TODO */
|
|
}
|
|
|
|
return (val);
|
|
}
|
|
|
|
int32_t
|
|
read32(void)
|
|
{
|
|
int32_t val;
|
|
|
|
if (fread(&val, 1, 4, input) != 4)
|
|
{
|
|
return (-1); /* XXX: TODO */
|
|
}
|
|
|
|
return (val);
|
|
}
|
|
|
|
void
|
|
fatal(const char *str, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "%s: ", prg_name);
|
|
va_start(ap, str);
|
|
vfprintf(stderr, str, ap);
|
|
va_end(ap);
|
|
fprintf(stderr, "\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void
|
|
decode_target_suffix(uint8_t attr)
|
|
{
|
|
fprintf(output, ".");
|
|
if (IS_TARGET_ZEXT(attr))
|
|
{
|
|
fprintf(output, "U");
|
|
}
|
|
|
|
switch (GET_TARGET_SIZE(attr))
|
|
{
|
|
case SIZE_BYTE:
|
|
fprintf(output, "B");
|
|
break;
|
|
case SIZE_WORD:
|
|
fprintf(output, "W");
|
|
break;
|
|
case SIZE_LONG:
|
|
fprintf(output, "L");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
decode_address(uint8_t attr)
|
|
{
|
|
switch (GET_ADDRESS_SIZE(attr))
|
|
{
|
|
case SIZE_BYTE:
|
|
fprintf(output, " %%%X", readu8());
|
|
break;
|
|
case SIZE_WORD:
|
|
fprintf(output, " %%%hX", readu16());
|
|
break;
|
|
case SIZE_LONG:
|
|
fprintf(output, " %%%X", readu32());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
decode_imm(uint8_t opcode)
|
|
{
|
|
uint8_t attr;
|
|
|
|
attr = readu8();
|
|
|
|
fprintf(output, "%s", opcode_str[opcode]);
|
|
decode_target_suffix(attr);
|
|
switch (GET_TARGET_SIZE(attr))
|
|
{
|
|
case SIZE_BYTE:
|
|
fprintf(output, " #$%X\n", readu8());
|
|
break;
|
|
case SIZE_WORD:
|
|
fprintf(output, " #$%hX\n", readu16());
|
|
break;
|
|
case SIZE_LONG:
|
|
fprintf(output, " #$%X\n", readu32());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
decode_ind(uint8_t opcode)
|
|
{
|
|
uint8_t attr;
|
|
|
|
fprintf(output, "%s", opcode_str[opcode]);
|
|
attr = readu8();
|
|
if (opcode != OP_JMP_ind)
|
|
{
|
|
decode_target_suffix(attr);
|
|
}
|
|
if (opcode_addr[opcode] & (OP_ADDR_X | OP_ADDR_Y))
|
|
{
|
|
fprintf(output, " (");
|
|
}
|
|
else
|
|
{
|
|
fprintf(output, " ");
|
|
}
|
|
|
|
decode_address(attr);
|
|
|
|
if (opcode_addr[opcode] & OP_ADDR_X)
|
|
{
|
|
fprintf(output, ", X)");
|
|
}
|
|
else if (opcode_addr[opcode] & OP_ADDR_Y)
|
|
{
|
|
fprintf(output, "), Y");
|
|
}
|
|
fprintf(output, "\n");
|
|
}
|
|
|
|
void
|
|
decode_abs(uint8_t opcode)
|
|
{
|
|
uint8_t attr;
|
|
|
|
fprintf(output, "%s", opcode_str[opcode]);
|
|
attr = readu8();
|
|
if (opcode != OP_JSR_abs
|
|
|| opcode != OP_JMP_abs)
|
|
{
|
|
decode_target_suffix(attr);
|
|
}
|
|
decode_address(attr);
|
|
|
|
if (opcode_addr[opcode] & OP_ADDR_X)
|
|
{
|
|
fprintf(output, ", X");
|
|
}
|
|
else if (opcode_addr[opcode] & OP_ADDR_Y)
|
|
{
|
|
fprintf(output, ", Y");
|
|
}
|
|
fprintf(output, "\n");
|
|
}
|
|
|
|
void
|
|
decode(void)
|
|
{
|
|
uint8_t opcode;
|
|
int16_t relative;
|
|
|
|
do {
|
|
opcode = readu8();
|
|
if (feof(input)) return;
|
|
switch (opcode_addr[opcode] & OP_ADDR_MODE)
|
|
{
|
|
case OP_ADDR_IMPL:
|
|
if (opcode_addr[opcode] & OP_ADDR_A)
|
|
{
|
|
fprintf(output, "%s A\n", opcode_str[opcode]);
|
|
}
|
|
else
|
|
{
|
|
fprintf(output, "%s\n", opcode_str[opcode]);
|
|
}
|
|
break;
|
|
case OP_ADDR_REL:
|
|
relative = read16();
|
|
fprintf(output, "%s $%hX\n", opcode_str[opcode], relative);
|
|
break;
|
|
case OP_ADDR_IMM:
|
|
decode_imm(opcode);
|
|
break;
|
|
case OP_ADDR_IND:
|
|
decode_ind(opcode);
|
|
break;
|
|
case OP_ADDR_ABS:
|
|
decode_abs(opcode);
|
|
break;
|
|
default:
|
|
fprintf(output, "???\n", opcode);
|
|
break;
|
|
}
|
|
} while (!feof(input));
|
|
}
|
|
|
|
void
|
|
usage(int retcode)
|
|
{
|
|
if (retcode == EXIT_FAILURE)
|
|
{
|
|
fprintf(stderr,
|
|
"Try '%s -h' for more information.\n", prg_name);
|
|
}
|
|
else
|
|
{
|
|
printf("Usage: %s [-hV] [INPUT] [OUTPUT]\n", prg_name);
|
|
printf("Disassemble INPUT to OUTPUT\n\n");
|
|
printf("With no INPUT read standard input.\n");
|
|
printf("With no OUTPUT write standard output.\n\n");
|
|
printf("\t-h\tdisplay this help and exit\n");
|
|
printf("\t-V\toutput version information\n");
|
|
printf("\nReport bugs to <%s>\n", PACKAGE_BUGREPORT);
|
|
}
|
|
|
|
exit(retcode);
|
|
}
|
|
|
|
void
|
|
version(void)
|
|
{
|
|
printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
#ifdef HAVE_LIBGEN_H
|
|
prg_name = basename(argv[0]);
|
|
#else
|
|
prg_name = argv[0];
|
|
#endif /* HAVE_LIBGEN_H */
|
|
input = stdin;
|
|
output = stdout;
|
|
|
|
while ((argc > 1) && (argv[1][0] == '-'))
|
|
{
|
|
switch (argv[1][1])
|
|
{
|
|
case 'h':
|
|
usage(EXIT_SUCCESS);
|
|
break;
|
|
case 'V':
|
|
version();
|
|
break;
|
|
default:
|
|
usage(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
if (argc > 2)
|
|
{
|
|
output = fopen(argv[2], "w");
|
|
if (output == NULL) fatal("can't open %s.", argv[2]);
|
|
}
|
|
if (argc > 1)
|
|
{
|
|
input = fopen(argv[1], "rb");
|
|
if (input == NULL) fatal("can't open %s.", argv[1]);
|
|
}
|
|
|
|
decode();
|
|
return (EXIT_SUCCESS);
|
|
}
|