diff --git a/pretty6502.c b/pretty6502.c new file mode 100644 index 0000000..02ae2d1 --- /dev/null +++ b/pretty6502.c @@ -0,0 +1,524 @@ +/* +** Pretty6502 +** +** by Oscar Toledo G. +** +** © Copyright 2017 Oscar Toledo G. +** +** Creation date: Nov/03/2017. +** Revision date: Nov/06/2017. Processor selection. Indents nested IF/ENDIF. Tries to preserve vertical structure of comments. Allows label in its own line. +*/ + +#include +#include +#include +#include + +#define VERSION "v0.2" +int tabs; + +/* +** 6502 mnemonics +*/ +char *mnemonics_6502[] = { + "adc", "anc", "and", "ane", "arr", "asl", "asr", "bcc", + "bcs", "beq", "bit", "bmi", "bne", "bpl", "brk", "bvc", + "bvs", "clc", "cld", "cli", "clv", "cmp", "cpx", "cpy", + "dcp", "dec", "dex", "dey", "eor", "inc", "inx", "iny", + "isb", "jmp", "jsr", "las", "lax", "lda", "ldx", "ldy", + "lsr", "lxa", "nop", "ora", "pha", "php", "pla", "plp", + "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", + "sbx", "sec", "sed", "sei", "sha", "shs", "shx", "shy", + "slo", "sre", "sta", "stx", "sty", "tax", "tay", "tsx", + "txa", "txs", "tya", NULL, +}; + +#define DONT_RELOCATE_LABEL 0x01 +#define LEVEL_IN 0x02 +#define LEVEL_OUT 0x04 +#define LEVEL_MINUS 0x08 + +/* +** DASM directives +*/ +struct { + char *directive; + int flags; +} directives_dasm[] = { + "=", DONT_RELOCATE_LABEL, + "align", 0, + "byte", 0, + "dc", 0, + "ds", 0, + "dv", 0, + "echo", 0, + "eif", LEVEL_OUT, + "else", LEVEL_MINUS, + "end", 0, + "endif", LEVEL_OUT, + "endm", LEVEL_OUT, + "eqm", DONT_RELOCATE_LABEL, + "equ", DONT_RELOCATE_LABEL, + "err", 0, + "hex", 0, + "if", LEVEL_IN, + "ifconst", LEVEL_IN, + "ifnconst", LEVEL_IN, + "incbin", 0, + "incdir", 0, + "include", 0, + "list", 0, + "long", 0, + "mac", LEVEL_IN, + "mexit", 0, + "org", 0, + "processor", 0, + "rend", 0, + "repeat", LEVEL_IN, + "repend", LEVEL_OUT, + "rorg", 0, + "seg", 0, + "set", DONT_RELOCATE_LABEL, + "subroutine", DONT_RELOCATE_LABEL, + "trace", 0, + "word", 0, + NULL, 0, +}; + +/* +** Comparison without case +*/ +int memcmpcase(char *p1, char *p2, int size) +{ + while (size--) { + if (tolower(*p1) != tolower(*p2)) + return 1; + p1++; + p2++; + } + return 0; +} + +/* +** Check for opcode or directive +*/ +int check_opcode(char *p1, char *p2) +{ + int c; + int length; + + for (c = 0; directives_dasm[c].directive != NULL; c++) { + length = strlen(directives_dasm[c].directive); + if ((*p1 == '.' && length == p2 - p1 - 1 && memcmpcase(p1 + 1, directives_dasm[c].directive, p2 - p1 - 1) == 0) || (length == p2 - p1 && memcmpcase(p1, directives_dasm[c].directive, p2 - p1) == 0)) { + return c + 1; + } + } + for (c = 0; mnemonics_6502[c] != NULL; c++) { + length = strlen(mnemonics_6502[c]); + if (length == p2 - p1 && memcmpcase(p1, mnemonics_6502[c], p2 - p1) == 0) + return -(c + 1); + } + return 0; +} + +/* +** Request space in line +*/ +void request_space(FILE *output, int *current, int new, int force) +{ + + /* + ** If already exceeded space... + */ + if (*current >= new) { + if (force) + fputc(' ', output); + (*current)++; + return; + } + + /* + ** Advance one step at a time + */ + while (1) { + if (tabs == 0) { + fprintf(output, "%*s", new - *current, ""); + *current = new; + } else { + fputc('\t', output); + *current = (*current + tabs) / tabs * tabs; + } + if (*current >= new) + return; + } +} + +/* +** Main program +*/ +int main(int argc, char *argv[]) +{ + int c; + int style; + int processor; + int start_mnemonic; + int start_operand; + int start_comment; + int align_comment; + int nesting_space; + int labels_own_line; + FILE *input; + FILE *output; + int allocation; + char *data; + char *p; + char *p1; + char *p2; + int current_column; + int request; + int current_level; + int prev_comment_original_location; + int prev_comment_final_location; + int flags; + + /* + ** Show usage if less than 3 arguments (program name counts as one) + */ + if (argc < 3) { + fprintf(stderr, "\n"); + fprintf(stderr, "Pretty6502 " VERSION " by Oscar Toledo G. http://nanochess.org/\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " pretty6502 [args] input.asm output.asm\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "DON'T USE SAME OUTPUT FILE AS INPUT, though it's possible,\n"); + fprintf(stderr, "you can DAMAGE YOUR SOURCE if this program has bugs.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, " -s0 Code in four columns (default)\n"); + fprintf(stderr, " label: mnemonic operand comment\n"); + fprintf(stderr, " -s1 Code in three columns\n"); + fprintf(stderr, " label: mnemonic+operand comment\n"); + fprintf(stderr, " -p0 Processor unknown\n"); + fprintf(stderr, " -p1 Processor 6502 + DASM syntax (default)\n"); + fprintf(stderr, " -m8 Start of mnemonic column (default)\n"); + fprintf(stderr, " -o16 Start of operand column (default)\n"); + fprintf(stderr, " -c32 Start of comment column (default)\n"); + fprintf(stderr, " -t8 Use tabs of size 8 to reach column\n"); + fprintf(stderr, " -t0 Use spaces to align (default)\n"); + fprintf(stderr, " -a0 Align comments to nearest column\n"); + fprintf(stderr, " -a1 Comments at line start are aligned\n"); + fprintf(stderr, " to mnemonic (default)\n"); + fprintf(stderr, " -n4 Nesting spacing (can be any number\n"); + fprintf(stderr, " of spaces or multiple of tab size)\n"); + fprintf(stderr, " -l Puts labels in its own line\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Assumes all your labels are at start of line and there is space\n"); + fprintf(stderr, "before mnemonic.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Accepts any assembler file where ; means comment\n"); + fprintf(stderr, "[label] mnemonic [operand] ; comment\n"); + exit(1); + } + + /* + ** Default settings + */ + style = 0; + processor = 1; + start_mnemonic = 8; + start_operand = 16; + start_comment = 32; + tabs = 0; + align_comment = 1; + nesting_space = 4; + labels_own_line = 0; + + /* + ** Process arguments + */ + c = 1; + while (c < argc - 2) { + if (argv[c][0] != '-') { + fprintf(stderr, "Bad argument\n"); + exit(1); + } + switch (argv[c][1]) { + case 's': /* Style */ + style = atoi(&argv[c][2]); + if (style != 0 && style != 1) { + fprintf(stderr, "Bad style code: %d\n", style); + exit(1); + } + break; + case 'p': /* Processor */ + processor = atoi(&argv[c][2]); + if (processor < 0 || processor > 1) { + fprintf(stderr, "Bad processor code: %d\n", processor); + exit(1); + } + break; + case 'm': /* Mnemonic start */ + start_mnemonic = atoi(&argv[c][2]); + break; + case 'o': /* Operand start */ + start_operand = atoi(&argv[c][2]); + break; + case 'c': /* Comment start */ + start_comment = atoi(&argv[c][2]); + break; + case 't': /* Tab size */ + tabs = atoi(&argv[c][2]); + break; + case 'a': /* Comment alignment */ + align_comment = atoi(&argv[c][2]); + if (align_comment != 0 && align_comment != 1) { + fprintf(stderr, "Bad comment alignment: %d\n", align_comment); + exit(1); + } + break; + case 'n': /* Nesting space */ + nesting_space = atoi(&argv[c][2]); + break; + case 'l': /* Labels in own line */ + labels_own_line = 1; + break; + default: /* Other */ + fprintf(stderr, "Unknown argument: %c\n", argv[c][1]); + exit(1); + } + c++; + } + + /* + ** Validate constraints + */ + if (style == 1) { + if (start_mnemonic > start_comment) { + fprintf(stderr, "Operand error: -m%d > -c%d\n", start_mnemonic, start_comment); + exit(1); + } + start_operand = start_mnemonic; + } else if (style == 0) { + if (start_mnemonic > start_operand) { + fprintf(stderr, "Operand error: -m%d > -o%d\n", start_mnemonic, start_operand); + exit(1); + } + if (start_operand > start_comment) { + fprintf(stderr, "Operand error: -o%d > -c%d\n", start_operand, start_comment); + exit(1); + } + } + if (tabs > 0) { + if (start_mnemonic % tabs) { + fprintf(stderr, "Operand error: -m%d isn't a multiple of %d\n", start_mnemonic, tabs); + exit(1); + } + if (start_operand % tabs) { + fprintf(stderr, "Operand error: -m%d isn't a multiple of %d\n", start_operand, tabs); + exit(1); + } + if (start_comment % tabs) { + fprintf(stderr, "Operand error: -m%d isn't a multiple of %d\n", start_comment, tabs); + exit(1); + } + if (nesting_space % tabs) { + fprintf(stderr, "Operand error: -n%d isn't a multiple of %d\n", nesting_space, tabs); + exit(1); + } + } + + /* + ** Open input file, measure it and read it into buffer + */ + input = fopen(argv[c], "rb"); + if (input == NULL) { + fprintf(stderr, "Unable to open input file: %s\n", argv[c]); + exit(1); + } + fprintf(stderr, "Processing %s...\n", argv[c]); + fseek(input, 0, SEEK_END); + allocation = ftell(input); + data = malloc(allocation + sizeof(char)); + if (data == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + fclose(input); + exit(1); + } + fseek(input, 0, SEEK_SET); + if (fread(data, sizeof(char), allocation, input) != allocation) { + fprintf(stderr, "Something went wrong reading the input file\n"); + fclose(input); + free(data); + exit(1); + } + fclose(input); + + /* + ** Ease processing of input file + */ + request = 0; + p1 = data; + p2 = data; + while (p1 < data + allocation) { + if (*p1 == '\r') { /* Ignore \r characters */ + p1++; + continue; + } + if (*p1 == '\n') { + p1++; + *p2++ = '\0'; /* Break line */ + request = 1; + continue; + } + *p2++ = *p1++; + request = 0; + } + if (request == 0) + *p2++ = '\0'; /* Force line break */ + allocation = p2 - data; + + /* + ** Now generate output file + */ + c++; + output = fopen(argv[c], "w"); + if (output == NULL) { + fprintf(stderr, "Unable to open output file: %s\n", argv[c]); + exit(1); + } + prev_comment_original_location = 0; + prev_comment_final_location = 0; + current_level = 0; + p = data; + while (p < data + allocation) { + current_column = 0; + p1 = p; + p2 = p1; + while (*p2 && !isspace(*p2) && *p2 != ';') + p2++; + if (p2 - p1) { /* Label */ + fwrite(p1, sizeof(char), p2 - p1, output); + current_column = p2 - p1; + p1 = p2; + } else { + current_column = 0; + } + while (*p1 && isspace(*p1)) + p1++; + flags = 0; + if (*p1 && *p1 != ';') { /* Mnemonic */ + p2 = p1; + while (*p2 && !isspace(*p2) && *p2 != ';') + p2++; + if (processor == 1) { + c = check_opcode(p1, p2); + if (c == 0) { + request = start_mnemonic; + } else if (c < 0) { + request = start_mnemonic; + } else { + flags = directives_dasm[c - 1].flags; + if (flags & DONT_RELOCATE_LABEL) + request = start_operand; + else + request = start_mnemonic; + } + } else { + request = start_mnemonic; + } + + /* + ** Move label to own line + */ + if (current_column != 0 && labels_own_line != 0 && (flags & DONT_RELOCATE_LABEL) == 0) { + fputc('\n', output); + current_column = 0; + } + if (flags & LEVEL_OUT) { + if (current_level > 0) + current_level--; + } + request += current_level * nesting_space; + if (flags & LEVEL_MINUS) + request -= nesting_space; + request_space(output, ¤t_column, request, 1); + fwrite(p1, sizeof(char), p2 - p1, output); + current_column += p2 - p1; + p1 = p2; + while (*p1 && isspace(*p1)) + p1++; + if (*p1 && *p1 != ';') { /* Operand */ + request = start_operand; + request += current_level * nesting_space; + request_space(output, ¤t_column, request, 1); + p2 = p1; + while (*p2 && *p2 != ';') { + if (*p2 == '"') { + p2++; + while (*p2 && *p2 != '"') + p2++; + p2++; + } else if (*p2 == '\'') { + p2++; + while (*p2 && *p2 != '"') + p2++; + p2++; + } else { + p2++; + } + } + while (p2 > p1 && isspace(*(p2 - 1))) + p2--; + fwrite(p1, sizeof(char), p2 - p1, output); + current_column += p2 - p1; + p1 = p2; + while (*p1 && isspace(*p1)) + p1++; + } + if (flags & LEVEL_IN) { + current_level++; + } + } + if (*p1 == ';') { /* Comment */ + + /* + ** Try to keep comments horizontally aligned (only works + ** if spaces were used in source file) + */ + p2 = p1; + while (p2 - 1 >= p && isspace(*(p2 - 1))) + p2--; + if (p2 == p && p1 - p == prev_comment_original_location) { + request = prev_comment_final_location; + } else { + prev_comment_original_location = p1 - p; + if (current_column == 0) + request = 0; + else if (current_column < start_mnemonic) + request = start_mnemonic; + else + request = start_comment; + if (current_column == 0 && align_comment == 1) + request = start_mnemonic; + prev_comment_final_location = request; + } + request_space(output, ¤t_column, request, 0); + p2 = p1; + while (*p2) + p2++; + while (p2 > p1 && isspace(*(p2 - 1))) + p2--; + fwrite(p1, sizeof(char), p2 - p1, output); + fputc('\n', output); + current_column += p2 - p1; + while (*p++) ; + continue; + } + fputc('\n', output); + while (*p++) ; + } + fclose(output); + free(data); + exit(0); +}