Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
439 lines (355 sloc) 8 KB
/*
* mipsdis is a simple command line MIPS disassembler. The core dispatch and
* decoding routines are autogenerated by mipsgen.
*
* usage:
* mipsdis input.txt
*
* The expected input format is the first two columns from objdump:
*
* hex_addr hex_opcode
* 80001678: 27bd0008
*
* The decoder will ignore any extra columns, and any lines not matching the
* above format.
*
* The output format is similar to objdump output:
*
* hex_addr hex_opcode asm
* 80001678: 27bd0008 addiu $29,$29,8
*
* This is not a fully featured disassembler, as it lacks quite a few
* usability features, but it is sufficient for debugging and exercising the
* code generator.
*
* See codegen/cgen.rb for C specific information.
* See scripts/mipsgen.rb for the code generator framework.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
/*
* The field_xxx functions provide raw access to the bits denoted by code
* letter in the 'opcode_bits' file.
*
* They are not responsible for interpretation, they just pick out the right
* bits, shift them and return as unsigned.
*/
static uint32_t field_m(uint32_t op)
{
return (op & 0xfc000000) >> 26;
}
static uint32_t field_u(uint32_t op)
{
return (op & 0x0000003f);
}
static uint32_t field_s(uint32_t op)
{
return (op & 0x03e00000) >> 21;
}
static uint32_t field_t(uint32_t op)
{
return (op & 0x001f0000) >> 16;
}
static uint32_t field_o(uint32_t op)
{
return (op & 0x0000ffff);
}
static uint32_t field_i(uint32_t op)
{
return (op & 0x0000ffff);
}
static uint32_t field_b(uint32_t op)
{
return (op & 0x03e00000) >> 21;
}
static uint32_t field_p(uint32_t op)
{
return (op & 0x001f0000) >> 16;
}
static uint32_t field_f(uint32_t op)
{
return (op & 0x001f0000) >> 16;
}
static uint32_t field_d(uint32_t op)
{
return (op & 0x0000f800) >> 11;
}
static uint32_t field_a(uint32_t op)
{
return (op & 0x000007c0) >> 6;
}
static uint32_t field_c(uint32_t op)
{
return (op & 0x03ffffc0) >> 6;
}
static uint32_t field_y(uint32_t op)
{
return (op & 0x000007c0) >> 6;
}
static uint32_t field_e(uint32_t op)
{
return (op & 0x0000ffc0) >> 6;
}
static uint32_t field_n(uint32_t op)
{
return (op & 0x00000007);
}
static uint32_t field_x(uint32_t op)
{
return (op & 0x03ffffff);
}
static uint32_t field_z(uint32_t op)
{
return (op & 0x01ffffc0) >> 6;
}
/* Top 10 bits of syscode. */
static uint32_t field_bc1(uint32_t op)
{
return (op & 0x03ff0000) >> 16;
}
/* Bottom 10 bits of syscode. */
static uint32_t field_bc2(uint32_t op)
{
return (op & 0x0000ffc0) >> 6;
}
/*
* The getxxx functions interpret the results returned by field_xxx and any
* extra arithmetic or sign handling is done here
*/
static uint32_t getopcode(uint32_t op)
{
return field_m(op);
}
static uint32_t getfunction(uint32_t op)
{
return field_u(op);
}
static uint32_t getrt(uint32_t op)
{
return field_t(op);
}
static uint32_t getrs(uint32_t op)
{
return field_s(op);
}
static uint32_t getrd(uint32_t op)
{
return field_d(op);
}
/* Compute the jump target address (used in j, jal) */
static uint32_t gettarget(uint32_t pc, uint32_t op)
{
uint32_t x = field_x(op);
uint32_t hibits = (pc + 4) & 0xf0000000;
uint32_t lobits = x << 2;
return hibits | lobits;
}
/* compute the branch offset (used in beq, bne, etc) */
static uint32_t getbroff(uint32_t pc, uint32_t op)
{
int32_t o = (int32_t) (field_o(op) << 2);
/* from bithacks */
const int32_t m = 1U << (18 - 1);
int32_t r = (o ^ m) - m;
return (pc + 4) + (uint32_t) r;
}
static int32_t getsimm(uint32_t op)
{
return (int16_t) (field_i(op));
}
static uint32_t getimm(uint32_t op)
{
return field_i(op);
}
static int32_t getoffset(uint32_t op)
{
return getsimm(op);
}
static uint32_t getbase(uint32_t op)
{
return field_b(op);
}
static uint32_t getcacheop(uint32_t op)
{
return field_p(op);
}
static uint32_t getprefhint(uint32_t op)
{
return field_f(op);
}
static uint32_t getsa(uint32_t op)
{
return field_a(op);
}
static uint32_t getbc1(uint32_t op)
{
return field_bc1(op);
}
static uint32_t getbc2(uint32_t op)
{
return field_bc2(op);
}
static uint32_t getsyscode(uint32_t op)
{
return field_c(op);
}
static uint32_t getstype(uint32_t op)
{
return field_y(op);
}
static uint32_t gettrapcode(uint32_t op)
{
return field_e(op);
}
static uint32_t getsel(uint32_t op)
{
return field_n(op);
}
static uint32_t getwaitcode(uint32_t op)
{
return field_z(op);
}
/* Error handling functions. */
static void decode_illegal(char *outbuf, size_t n, uint32_t addr,
uint32_t opcode)
{
sprintf(outbuf, "illegal");
}
static void decode_unusable(char *outbuf, size_t n, uint32_t addr,
uint32_t opcode)
{
decode_illegal(outbuf, n, addr, opcode);
}
static void decode_reserved(char *outbuf, size_t n, uint32_t addr,
uint32_t opcode)
{
decode_illegal(outbuf, n, addr, opcode);
}
/*
* Instruction validation functions.
* Returns 1 for OK, 0 otherwise.
*/
static int check_opcode(uint32_t op, uint32_t mask, uint32_t value)
{
return (op & mask) == value;
}
static int check_cl(uint32_t rt, uint32_t rd)
{
return rt == rd;
}
static int check_jalr(uint32_t rs, uint32_t rd)
{
return rs != rd;
}
#include "mips_dispatch.h"
/* The entry point into the auto generated code. */
static void decode(char *out_buf, size_t n, uint32_t addr, uint32_t opcode)
{
decode_OPCODE(out_buf, n, addr, opcode);
}
/*
* Determines if the c string is a valid address.
* Returns 1 for true, 0 otherwise
*
* A regex matching this would look like this:
* [0-9a-f]+:
*/
static int is_addr(const char *s)
{
size_t n = strlen(s);
size_t i = 0;
if (n <= 1)
return 0;
n--;
for (i = 0; i < n; ++i) {
if (!isxdigit(s[i]))
return 0;
}
return 1;
}
/*
* Determines if the c string is a valid hexword.
* Returns 1 for yes, 0 otherwise.
*
* A regex matching this would look like this:
* [0-9a-f]{8}
*/
static int is_hexword(const char *s)
{
size_t n = strlen(s);
size_t i = 0;
if (n != 8)
return 0;
for (i = 0; i < n; ++i) {
if (!isxdigit(s[i]))
return 0;
}
return 1;
}
/* Convert the c_string containing hex chars to a u32. */
static uint32_t hex_to_u32(const char *s)
{
return (uint32_t) strtoul(s, 0, 16);
}
/*
* For each line of the given stream, call the decoder and print the
* input address, opcode and disasm to stdout.
*/
static void decode_stream(FILE * stream)
{
const char *sep = " \t\r\n";
char *lineptr = 0;
size_t n = 0;
char outbuf[64] = { 0 };
ssize_t rn = 0;
char *saveptr = 0;
char *addrtok = 0;
char *optok = 0;
uint32_t address = 0;
uint32_t opcode = 0;
while (1) {
rn = getline(&lineptr, &n, stream);
if (rn <= 0)
break;
addrtok = strtok_r(lineptr, sep, &saveptr);
optok = strtok_r(0, sep, &saveptr);
if (!addrtok || !optok)
continue;
if (!is_addr(addrtok) || !is_hexword(optok))
continue;
address = hex_to_u32(addrtok);
opcode = hex_to_u32(optok);
decode(outbuf, 64, address, opcode);
printf("%08x:\t%08x\t%s\n", address, opcode, outbuf);
}
free(lineptr);
}
/* Handle the command line options and handoff to decode_stream. */
int main(int argc, char **argv)
{
const char *filename = NULL;
FILE *stream = 0;
int do_close = 0;
if (argc != 2) {
fprintf(stderr, "usage: decoder file\n");
return 1;
}
filename = argv[1];
if (strcmp(filename, "-") == 0) {
stream = stdin;
} else {
stream = fopen(filename, "r");
if (!stream) {
fprintf(stderr, "could not open %s\n", filename);
return 1;
}
do_close = 1;
}
decode_stream(stream);
if (do_close)
fclose(stream);
return 0;
}