/* * Disassembler for the Microchip PIC16C84 Microcontroller * * Markus Kuhn -- mskuhn@cip.informatik.uni-erlangen.de -- 1995-04-05 * */ #include #include #include #include char usage[] = "usage: PICDIS {options} input-file [control-file]\n\n" "options: -a produce output suitable for an assembler\n" " -b binary input file (default: Intel hex16)\n" " -l littleendian binary input file\n"; #define COMMANDS 36 struct cmdmask { unsigned pattern; unsigned mask; int type; /* 0=-; 1=f; 2=f,d; 3=f,b; 4=k(8-bit); 5=k(11-bit) */ char mnemonic[7]; } cmdlist[COMMANDS] = { 0x0700, 0xff00, 2, "ADDWF", 0x0500, 0xff00, 2, "ANDWF", 0x0180, 0xff80, 1, "CLRF", 0x0100, 0xff80, 0, "CLRW", 0x0900, 0xff00, 2, "COMF", 0x0300, 0xff00, 2, "DECF", 0x0b00, 0xff00, 2, "DECFSZ", 0x0a00, 0xff00, 2, "INCF", 0x0f00, 0xff00, 2, "INCFSZ", 0x0400, 0xff00, 2, "IORWF", 0x0800, 0xff00, 2, "MOVF", 0x0080, 0xff80, 1, "MOVWF", 0x0000, 0xff9f, 0, "NOP", 0x0d00, 0xff00, 2, "RLF", 0x0c00, 0xff00, 2, "RRF", 0x0200, 0xff00, 2, "SUBWF", 0x0e00, 0xff00, 2, "SWAPF", 0x0600, 0xff00, 2, "XORWF", 0x1000, 0xfc00, 3, "BCF", 0x1400, 0xfc00, 3, "BSF", 0x1800, 0xfc00, 3, "BTFSC", 0x1c00, 0xfc00, 3, "BTFSS", 0x3e00, 0xfe00, 4, "ADDLW", 0x3900, 0xff00, 4, "ANDLW", 0x2000, 0xf800, 5, "CALL", 0x0064, 0xffff, 0, "CLRWDT", 0x2800, 0xf800, 5, "GOTO", 0x3800, 0xff00, 4, "IORLW", 0x3000, 0xfc00, 4, "MOVLW", 0x0009, 0xffff, 0, "RETFIE", 0x3400, 0xfc00, 4, "RETLW", 0x0008, 0xffff, 0, "RETURN", 0x0063, 0xffff, 0, "SLEEP", 0x3c00, 0xfe00, 4, "SUBLW", 0x3a00, 0xff00, 4, "XORLW", 0x0062, 0xffff, 0, "OPTION" }; #define CTL_LIST_SIZE 256 struct ctl_entry { unsigned address; char type[3]; char *text; struct ctl_entry *next; } *(ctl_list[CTL_LIST_SIZE]); long checksum = -1; int binary = 0; int littleendian = 0; int for_assembler = 0; FILE *fin; /* input file */ char *fnin = NULL; /* input file name */ unsigned int input_address = -1; /* input file address counter */ int hex_left = 0; /* remaining words in this hex input file line */ char hex_line[1032]; #define ROM_SIZE 0x800 int ref_count[ROM_SIZE]; /* number of references to an address */ /* * Disassemble a single command word */ int command(unsigned cmd, char *line) { int i; line[0] = 0; /* special commands */ if ((cmd & 0xfff8) == 0x0060) { sprintf(line, "TRIS 0x%x", cmd & 7); return -1; } for (i = 0; i < COMMANDS; i++) if ((cmd & cmdlist[i].mask) == cmdlist[i].pattern) break; if (i >= COMMANDS) { sprintf(line, "???"); return -1; } else switch (cmdlist[i].type) { case 0: sprintf(line, "%s", cmdlist[i].mnemonic); break; case 1: sprintf(line, "%s 0x%02x", cmdlist[i].mnemonic, cmd & 0x7f); break; case 2: sprintf(line, "%s 0x%02x,%d", cmdlist[i].mnemonic, cmd & 0x7f, (cmd & 0x80) != 0); break; case 3: sprintf(line, "%s 0x%02x,%d", cmdlist[i].mnemonic, cmd & 0x7f, (cmd >> 7) & 7); break; case 4: sprintf(line, "%s 0x%02x", cmdlist[i].mnemonic, cmd & 0xff); break; case 5: if (for_assembler) sprintf(line, "%s L%c%03X", cmdlist[i].mnemonic, 'A' - 1 + ref_count[cmd & 0x7ff], cmd & 0x7ff); else sprintf(line, "%s 0x%03x", cmdlist[i].mnemonic, cmd & 0x7ff); break; default: fprintf(stderr, "Internal error (cmdlist[].type)!\n"); exit(1); } return cmdlist[i].type; } /* * Read in control file and store it in a hash data structure */ void read_ctl(char *fn) { FILE *fin; char line[200], type[3]; int i, pos; unsigned addr; struct ctl_entry *entry; char *text; fin = fopen(fn, "r"); if (!fin) { fprintf(stderr, "Can't open control file '%s", fn); perror("'"); exit(1); } i = 0; type[2] = 0; while (!feof(fin)) { fgets(line, 199, fin); i++; while (*line && (isspace(line[strlen(line)-1]) || line[strlen(line)-1] == '\n')) line[strlen(line)-1] = 0; type[0] = tolower(line[0]); type[1] = tolower(line[1]); if (!strcmp(type, "pr")) { printf(" ; %s\n", line + 3); continue; } if (strlen(line) < 3) continue; if (sscanf(line + 2, "%x %n", &addr, &pos) != 1) { fprintf(stderr, "Syntax error in line %d of '%s':\n%s\n", i, fn, line); exit(1); } if (!strcmp(type, "cs")) { checksum = addr; continue; } entry = (struct ctl_entry *) malloc(sizeof(struct ctl_entry)); text = (char *) malloc(sizeof(char) * (strlen(line) - pos + 1)); if (!entry || !text) { fprintf(stderr, "Sorry, not enough memory for this control file.\n"); exit(1); } strcpy(text, line + pos + 2); strcpy(entry->type, type); entry->address = addr; entry->text = text; entry->next = ctl_list[addr % CTL_LIST_SIZE]; ctl_list[addr % CTL_LIST_SIZE] = entry; } fclose(fin); return; } /* * Search a control file entry in hash data structure */ char *find_entry(unsigned address, char *type) { struct ctl_entry *ctl; ctl = ctl_list[address % CTL_LIST_SIZE]; while (ctl) { if (ctl->address == address && !strcmp(ctl->type, type)) return ctl->text; ctl = ctl->next; } return NULL; } /* * Rewind input file */ void rewind_input(void) { rewind(fin); hex_left = 0; input_address = -1; return; } /* * hexadecimal to integer conversion */ unsigned int htoi(char *hex, int length) { int i; unsigned int v = 0; for (i = 0; i < length; i++) { v <<= 4; if (hex[i] >= '0' && hex[i] <= '9') v += hex[i] - '0'; else if (hex[i] >= 'a' && hex[i] <= 'f') v += hex[i] - 'a' + 10; else if (hex[i] >= 'A' && hex[i] <= 'F') v += hex[i] - 'A' + 10; else { fprintf(stderr, "Error: '%.4s' doesn't look very hexadecimal, right?\n", hex); exit(1); } } return v; } /* * Read next word from input file. Returns 0 if EOF has been reached, * 1 otherwise. */ int get_word(unsigned int *value) { static hex_count = 0; int i, sum; if (littleendian) { *value = getc(fin); *value |= getc(fin) << 8; } else if (binary) { *value = getc(fin) << 8; *value |= getc(fin); } else { if (hex_left < 1) { do { fgets(hex_line, 1031, fin); } while (!feof(fin) && (strlen(hex_line) < 9 || hex_line[0] != ':')); hex_left = hex_count = htoi(hex_line + 1, 2); if (strlen(hex_line) - 11 < hex_count * 4) { fprintf(stderr, "Intel hex16 line too short:\n%s\n", hex_line); exit(1); } input_address = htoi(hex_line + 3, 4) - 1; sum = 0; for (i = 1; i <= hex_count*4 + 9; i += 2) sum += htoi(hex_line + i, 2); if ((sum & 0xff) != 0) { fprintf(stderr, "Intel hex16 checksum error in line:\n%s\n", hex_line); exit(1); } if (htoi(hex_line + 7, 2) == 1) return 0; if (htoi(hex_line + 7, 2) != 0) { fprintf(stderr, "Unknown Intel hex16 type code 0x%02x!\n", htoi(hex_line + 7, 2)); exit(1); } } *value = htoi(hex_line + 9 + (hex_count - hex_left) * 4, 4); hex_left--; } input_address++; return !feof(fin); } int main(int argc, char **argv) { char *fnctl = NULL; unsigned expected_address, cmd = 0; char line[100], comment[300], *text; const char indent[] = " "; unsigned sum = 0; int i, j, count = 0; int references, type; fprintf(stderr, "PICDIS 1.2 -- PIC16C84 Disassembler -- Markus Kuhn\n\n"); for (i = 0; i < CTL_LIST_SIZE; i++) ctl_list[i] = NULL; /* parse command line */ for (i = 1; i < argc; i++) if (argv[i][0] == '-') for (j = 1; argv[i][j]; j++) switch (argv[i][j]) { case 'a': case 'A': for_assembler = 1; break; case 'b': case 'B': binary = 1; break; case 'l': case 'L': littleendian = 1; break; default: fprintf(stderr, usage); exit(1); } else if (fnin == NULL) fnin = argv[i]; else if (fnctl == NULL) fnctl = argv[i]; else { fprintf(stderr, usage); exit(1); } if (fnin == NULL) { fprintf(stderr, usage); exit(1); } /* open input files */ fin = fopen(fnin, (binary || littleendian) ? "rb" : "r"); if (!fin) { fprintf(stderr, "Can't open input file '%s", fnin); perror("'"); exit(1); } if (fnctl) read_ctl(fnctl); /* first pass (find references for labels) */ for (i = 0; i < ROM_SIZE; i++) ref_count[i] = 0; rewind_input(); while (get_word(&cmd)) if ((cmd & 0xf000) == 0x2000) { /* cmd is CALL or GOTO */ ref_count[cmd & 0x7ff]++; } for (i = 0; i < ROM_SIZE; i++) if (ref_count[i] > 26) ref_count[i] = 26; /* we have only 'A' - 'Z' */ expected_address = 0; rewind_input(); /* main loop */ while (get_word(&cmd)) { sum = sum ^ cmd; sum = ((sum << 1) | (sum >> 15)) & 0xffff; type = command(cmd, line); text = find_entry(input_address, "hd"); if (text) { printf("%s;\n", indent); printf("%s; %s\n", indent, text); printf("%s;\n", indent); } text = find_entry(input_address, "sc"); if (text) printf("%s; %s\n", indent, text); /* default comment */ comment[0] = 0; if (type == 3) { /* bit operation */ text = find_entry(((cmd >> 7) & 7) | ((cmd & 0x7f) << 4), "bt"); if (text) sprintf(comment, "%s", text); } if (cmd == 0x1683) sprintf(comment, "switch to page 1"); if (cmd == 0x1283) sprintf(comment, "switch to page 0"); if ((cmd & 0xf000) == 0x2000) { /* cmd is CALL or GOTO */ text = find_entry(cmd & 0x7ff, "hd"); if (text) sprintf(comment, "%s", text); } else if ((cmd & 0xfc00) == 0x3400) { /* cmd is RETLW */ if ((cmd & 0xff) >= 32 && (cmd & 0xff) < 127) sprintf(comment, "'%c'", cmd & 0xff); } text = find_entry(input_address, "cc"); if (text && *text) sprintf(comment, "%s", text); if (for_assembler && expected_address != input_address) printf("%sORG 0x%04x\n", indent, input_address); references = ref_count[input_address & 0x7ff]; if (for_assembler) { if (references) printf("L%c%03X ", 'A' - 1 + references, input_address & 0x7ff); else printf("%s", indent); } else printf("%04x%c %04x ", input_address, references ? 'A' - 1 + references : ' ', cmd); printf("%s", line); if (*comment) printf("%*s; %s", 20-strlen(line), "", comment); putchar('\n'); expected_address = input_address + 1; count++; } sum = (sum + count) & 0xffff; if (for_assembler) printf("%sEND\n", indent); putchar('\n'); if (checksum != -1 && sum != checksum) fprintf(stderr, "Warning: Checksum in control file (0x%04lx) is not " "equal to\n checksum of '%s' which is 0x%04x.\n", checksum, fnin, sum); return 0; }