Updated usage information for `neddis`.
[ned1] / neddis / neddis.c
/*
* © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include "../common/a.out.h"
#define VERSION 2
enum syllables {
MVSTCK = 0b00001111,
JMP = 0b00001110,
SWAP = 0b00001101,
ADD = 0b00001100,
XOR = 0b00001011,
NOT = 0b00001010,
OR = 0b00001001,
AND = 0b00001000,
BRZ = 0b00000111,
TEST = 0b00000110,
CMPSWP = 0b00000101,
SHIFT = 0b00000100,
STORE = 0b00000011,
LOAD = 0b00000010,
NOP = 0b00000001,
HALT = 0b00000000
};
#define WIDTH_MNEMONICS 14
/*
* Output format:
* 10 characters for address in hexadecimal
* 4 spaces
* 10 characters for hex dump of 32-bit word
* 4 spaces
* 40 characters for binary representation of 32-bit word
* 4 spaces
* remainder of line for mnemonics
*/
void
print_header(void)
{
printf(" Offset || Hex || Binary "
" || Description\n"
"============================================================"
"============================================================"
"===================\n");
}
void
print_usage(char ** argv)
{
printf( "NED Disassembler v%d (www.subgeniuskitty.com)\n"
"Usage: %s -i <file> [-l] [-s]\n"
" -h Help (prints this message)\n"
" -i <file> Specify a binary image file to disassemble.\n"
" -s Print summary of information in a.out exec header.\n"
" -l Print labels and organize disassembled code by label.\n"
, VERSION, argv[0]
);
}
void
print_formatA_binary(uint32_t word)
{
printf("1 ");
int chunk_length = 0;
for (int bit_index = 30; bit_index >= 0; bit_index--) {
/* Insert space every 8 bits. Leading space is intentional. */
if ((chunk_length++ % 8) == 0) printf(" ");
if ((word >> bit_index) & 0b1) {
printf("1");
} else {
printf("0");
}
}
printf(" ");
}
void
print_formatA_mnemonics(uint32_t word)
{
int position;
printf("WORD =====>%n", &position);
while (position++ != WIDTH_MNEMONICS) printf(" ");
printf("0x%x%n", ((word & 0b01111111111111111111111111111111) << 1), &position);
while (position++ != WIDTH_MNEMONICS) printf(" ");
printf("0%o%n", ((word & 0b01111111111111111111111111111111) << 1), &position);
while (position++ != WIDTH_MNEMONICS) printf(" ");
printf("%u%n", ((word & 0b01111111111111111111111111111111) << 1), &position);
while (position++ != WIDTH_MNEMONICS) printf(" ");
}
uint8_t
extract_syllable_from_word(uint32_t word, uint8_t index)
{
uint32_t mask = 0b111111 << 6*(4-index);
return (word & mask) >> 6*(4-index);
}
void
print_formatC_binary(uint32_t word)
{
printf("00 ");
for (int syllable_index = 0; syllable_index < 5; syllable_index++) {
uint8_t syllable = extract_syllable_from_word(word, syllable_index);
for (int i = 5; i >= 0; i--) {
if (((syllable >> i) & 0b1) == 1) {
printf("1");
} else {
printf("0");
}
}
printf(" ");
}
printf(" ");
}
void
print_formatC_mnemonics(uint32_t word)
{
for (int syllable_index = 0; syllable_index < 5; syllable_index++) {
int position = 0;
uint8_t syllable = extract_syllable_from_word(word, syllable_index);
if (syllable & 0b100000) { /* Check the first bit of the syllable. 1 means IM_x. */
printf("IM_%d%n", (syllable & 0b11111), &position);
} else if (syllable & 0b10000) { /* 1 in 2nd bit means LDSP+x or STSP+x instruction. */
if (syllable & 0b1000) { /* LDSP+x */
printf("LDSP+%d%n", (syllable & 0b111), &position);
} else { /* STSP+x */
printf("STSP+%d%n", (syllable & 0b111), &position);
}
} else {
switch (syllable) {
case AND: printf("AND%n", &position); break;
case OR: printf("OR%n", &position); break;
case NOT: printf("NOT%n", &position); break;
case XOR: printf("XOR%n", &position); break;
case ADD: printf("ADD%n", &position); break;
case MVSTCK: printf("MVSTCK%n", &position); break;
case SHIFT: printf("SHIFT%n", &position); break;
case CMPSWP: printf("CMPSWP%n", &position); break;
case TEST: printf("TEST%n", &position); break;
case BRZ: printf("BRZ%n", &position); break;
case LOAD: printf("LOAD%n", &position); break;
case STORE: printf("STORE%n", &position); break;
case NOP: printf("NOP%n", &position); break;
case HALT: printf("HALT%n", &position); break;
case JMP: printf("JMP%n", &position); break;
case SWAP: printf("SWAP%n", &position); break;
default: printf("?ERR?%n", &position); break;
}
}
while (position++ != WIDTH_MNEMONICS) printf(" ");
}
}
void
parse_aout_file(FILE * input, struct exec * aout_exec, uint32_t ** text_segment,
struct nlist ** symbol_table, uint32_t * symbol_count)
{
uint32_t read_count = 0;
/* Read in and check the a.out header. */
for (uint32_t i=0; i<8; i++) {
switch (i) {
case 0: read_count = fread(&(aout_exec->a_midmag), 4, 1, input); break;
case 1: read_count = fread(&(aout_exec->a_text), 4, 1, input); break;
case 2: read_count = fread(&(aout_exec->a_data), 4, 1, input); break;
case 3: read_count = fread(&(aout_exec->a_bss), 4, 1, input); break;
case 4: read_count = fread(&(aout_exec->a_syms), 4, 1, input); break;
case 5: read_count = fread(&(aout_exec->a_entry), 4, 1, input); break;
case 6: read_count = fread(&(aout_exec->a_trsize), 4, 1, input); break;
case 7: read_count = fread(&(aout_exec->a_drsize), 4, 1, input); break;
}
if (read_count != 1) {
fprintf(stderr, "ERROR: Invalid a.out header.\n");
exit(EXIT_FAILURE);
}
}
if (N_BADMAG(*aout_exec)) {
fprintf(stderr, "ERROR: Invalid magic number in a.out header.\n");
exit(EXIT_FAILURE);
} else if (N_GETMID(*aout_exec) != MID_NED) {
fprintf(stderr, "ERROR: Executable not intended for NED Machine ID.\n");
exit(EXIT_FAILURE);
}
/* Read in the text segment. */
uint32_t text_segment_size = (N_DATOFF(*aout_exec) - N_TXTOFF(*aout_exec));
*text_segment = malloc(text_segment_size + 4);
(*text_segment)[0] = text_segment_size / 4;
read_count = fread(&((*text_segment)[1]), 1, text_segment_size, input);
if (read_count != text_segment_size) {
fprintf(stderr, "ERROR: Failed to read entire text segment.\n");
exit(EXIT_FAILURE);
}
/* Read in the symbol table. */
*symbol_count = ((N_STROFF(*aout_exec) - N_SYMOFF(*aout_exec)) / 20); /* 20 bytes per symbol. */
*symbol_table = malloc((*symbol_count) * sizeof(struct nlist));
for (uint32_t i=0; i < *symbol_count; i++) {
for (uint32_t j=0; j<5; j++) {
switch (j) {
case 0: read_count = fread(&((*symbol_table)[i].n_un.n_strx), 4, 1, input); break;
case 1: read_count = fread(&((*symbol_table)[i].n_type), 4, 1, input); break;
case 2: read_count = fread(&((*symbol_table)[i].n_other), 4, 1, input); break;
case 3: read_count = fread(&((*symbol_table)[i].n_desc), 4, 1, input); break;
case 4: read_count = fread(&((*symbol_table)[i].n_value), 4, 1, input); break;
}
if (read_count != 1) {
fprintf(stderr, "ERROR: Unable to read entire symbol table.\n");
exit(EXIT_FAILURE);
}
}
}
/* Read in the string table and update the symbol table entries with pointers to new strings. */
uint32_t string_table_size;
read_count = fread(&string_table_size, 4, 1, input);
if (read_count != 1) {
fprintf(stderr, "ERROR: Failed to read string table size.\n");
exit(EXIT_FAILURE);
}
for (uint32_t i=0; i < *symbol_count; i++) {
uint32_t len = 0;
if (i < ((*symbol_count)-1)) {
len = ((*symbol_table)[i+1].n_un.n_strx - (*symbol_table)[i].n_un.n_strx);
} else {
len = (string_table_size - (*symbol_table)[i].n_un.n_strx);
}
(*symbol_table)[i].n_un.n_name = malloc(len);
read_count = fread((*symbol_table)[i].n_un.n_name, 1, len, input);
if (read_count != len) {
fprintf(stderr, "ERROR: Failed to read a string from the string table.\n");
exit(EXIT_FAILURE);
}
}
}
void
print_aout_summary(struct exec * aout_exec)
{
printf("\n Summary\n"
" =====================================\n"
" Magic: 0x%08x\n"
" Machine ID: 0x%08x\n"
" Flags: 0x%08x\n"
" Text Size: 0x%08x bytes\n"
" Data Size: 0x%08x bytes\n"
" BSS Size: 0x%08x bytes\n"
" Symbol Table Size: 0x%08x bytes\n"
" Entry Point: 0x%08x\n"
" Text Reloc. Size: 0x%08x bytes\n"
" Data Reloc. Size: 0x%08x bytes\n\n\n",
N_GETMAGIC(*aout_exec), N_GETMID(*aout_exec), N_GETFLAG(*aout_exec),
aout_exec->a_text, aout_exec->a_data, aout_exec->a_bss, aout_exec->a_syms,
aout_exec->a_entry, aout_exec->a_trsize, aout_exec->a_drsize
);
}
int
main(int argc, char ** argv)
{
/*
* Process command line arguments
*/
int c;
FILE * input = NULL;
bool display_summary = false;
bool display_labels = false;
while ((c = getopt(argc,argv,"i:hsl")) != -1) {
switch (c) {
case 'l':
display_labels = true;
break;
case 's':
display_summary = true;
break;
case 'i':
if ((input = fopen(optarg, "r")) == NULL) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
}
break;
case 'h':
print_usage(argv);
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
if (input == NULL) {
fprintf(stderr, "ERROR: Must specify a binary image file with -i flag.\n");
print_usage(argv);
exit(EXIT_FAILURE);
}
struct exec aout_exec;
uint32_t * text_segment;
struct nlist * symbol_table;
uint32_t symbol_count;
parse_aout_file(input, &aout_exec, &text_segment, &symbol_table, &symbol_count);
if (display_summary) print_aout_summary(&aout_exec);
/*
* Main Loop
*/
print_header();
uint32_t word;
uint32_t offset = aout_exec.a_entry;
/* Since all NED instructions are one word (4 bytes) wide, read in one word increments. */
uint32_t i = 1;
while (i <= text_segment[0]) {
if (display_labels) {
for (uint32_t i=0; i < symbol_count; i++) {
if (offset == symbol_table[i].n_value) {
printf("\n%s:\n", symbol_table[i].n_un.n_name);
}
}
}
word = text_segment[i];
i++;
printf("0x%08x", offset);
printf(" ");
printf("0x%08x", word);
printf(" ");
if (word & (0b1 << 31)) { /* Format A instruction word */
print_formatA_binary(word);
printf(" ");
print_formatA_mnemonics(word);
} else if ((word & (0b11 << 30)) == 0) { /* Format C instruction word */
print_formatC_binary(word);
printf(" ");
print_formatC_mnemonics(word);
} else {
fprintf(stderr, "ERROR: Malformed instruction word: %o\n", word);
exit(EXIT_FAILURE);
}
printf("\n");
offset += 4; /* Increment by one word (4 bytes). */
}
exit(EXIT_SUCCESS);
}