/* 6303.c -- a simple HD63*03 disassembler * * v1.0: works most of the time. * * Copyright (c) 2016, sigma (sigma dot neocities dot org) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include static int this_byte, next_byte; static int pc = 0; static int bytes_ptr; static char bytes[11]; static FILE* binary; int print_bytes; /* byte -- read byte from standard input */ static uint8_t byte(void) { this_byte = next_byte; if( print_bytes ) sprintf(bytes + bytes_ptr, " %02X", this_byte); next_byte = fgetc(binary); bytes_ptr += 3; pc++; return this_byte; } /* word -- read little-endian word from standard input */ static uint16_t word(void) { uint16_t out = byte() << 8; return out | byte(); } /* copy -- similar to strcpy, but returns pointer to end of string */ static char* copy(char* to, const char* from) { while( *to++ = *from++ ); return to - 1; } /* address -- format (and possibly replace with label) address */ int register_names, tx81z_names, vector_names; static const char* address(uint16_t addr) { static char buffer[8]; if( register_names ) switch( addr ) { case 0x0001: return "P2DIR"; case 0x0003: return "P2"; case 0x0008: return "TCSR1"; case 0x0009: return "FRC"; case 0x000B: return "OCR1"; case 0x000D: return "ICR"; case 0x000F: return "TCSR2"; case 0x0010: return "RMCR"; case 0x0011: return "TRCSR"; case 0x0012: return "RX"; case 0x0013: return "TX"; case 0x0014: return "P5CR"; case 0x0015: return "P5"; case 0x0016: return "P6DIR"; case 0x0017: return "P6"; case 0x0019: return "OCR2"; case 0x001B: return "TCSR3"; case 0x001C: return "TCONR"; case 0x001D: return "T1CNT"; case 0x001F: return "TEST"; } if( tx81z_names ) { if( addr >= 0x2000 && addr <= 0x3FFF ) { snprintf(buffer, sizeof buffer, "OPZ+$%04X", addr - 0x2000); return buffer; } else if( addr >= 0x4000 && addr < 0x5FFF ) return addr & 1 ? "LCDMEM" : "LCDREG"; } if( vector_names && addr >= 0xFFEA) { static const char* vectors[11] = { "IRQ2", "CMI", "TRAP", "SIO", "TOI", "OCI", "ICI", "IRQ1", "SWI", "NMI", "RES", }; strcpy(copy(buffer, vectors[addr - 0xFFEA >> 1]), addr & 1 ? "L" : ""); return buffer; } snprintf(buffer, sizeof buffer, "$%0*X", addr < 256 ? 2 : 4, addr); return buffer; } /* mnemonic -- print instruction mnemonic to string, return pointer to end of string */ static char* mnemonic(char* string, size_t size, uint8_t instruction) { static const char mnemonic64[64][5] = { "", "NOP", "", "", "LSRD", "ASLD", "TAP", "TPA", "INX", "DEX", "CLV", "SEV", "CLC", "SEC", "CLI", "SEI", "SBA", "CBA", "", "", "", "", "TAB", "TBA", "XGDX", "DAA", "SLP", "ABA", "", "", "", "", "BRA", "BRN", "BHI", "BLS", "BCC", "BCS", "BNE", "BEQ", "BVC", "BVS", "BPL", "BMI", "BGE", "BLT", "BGT", "BLE", "TSX", "INS", "PULA", "PULB", "DES", "TXS", "PSHA", "PSHB", "PULX", "RTS", "ABX", "RTI", "PSHX", "MUL", "WAI", "SWI" }; static const char mnemonic128[16][4] = { "NEG", "AIM", "OIM", "COM", "LSR", "EIM", "ROR", "ASR", "ASL", "ROL", "DEC", "TIM", "INC", "TST", "JMP", "CLR" }; static const char mnemonic192[16][5] = { "SUB", "CMP", "SBC", "SUBD", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "CPX", "JSR", "LDS", "STS" }; static const char mnemonic256[16][5] = { "SUB", "CMP", "SBC", "ADDD", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "LDD", "STD", "LDX", "STX" }; char* out = string + snprintf(string, size, "L%04X:%s ", pc - 1 & 0xFFFF, print_bytes ? "%-9s" : ""); return copy(out, instruction < 64 ? mnemonic64[instruction] : instruction < 128 ? mnemonic128[instruction & 15] : instruction < 192 ? instruction == 141 ? "BSR" : mnemonic192[instruction & 15] : mnemonic256[instruction & 15]); } /* parse -- print full representation of single instruction in input to stdout */ static void parse(void) { static const enum { NONE, RELJMP, A, B, IND, EXT, ASPIMM, ASPDIR, ASPIND, ASPEXT, BXIMM, BXDIR, BXIND, BXEXT } instr_type[16] = { NONE, NONE, RELJMP, NONE, A, B, IND, EXT, ASPIMM, ASPDIR, ASPIND, ASPEXT, BXIMM, BXDIR, BXIND, BXEXT }; static char repr[64]; const int instruction = this_byte; char* out = mnemonic(repr, sizeof repr, instruction); switch( instr_type[instruction >> 4] ) { case NONE: /* / */ break; case RELJMP: /* relative jump */ relative_jump: byte(); sprintf(out, " L%04X", pc + (int8_t) this_byte); break; case A: /* accumulator 1 */ strcpy(out, "A"); break; case B: /* accumulator 2 */ strcpy(out, "B"); break; #define MASK(x) ((x) && ((x) < 3 || (x) == 5 || (x) == 11)) case IND: /* indirect [and mask for A/O/E/TIM] */ if( MASK(instruction & 15) ) goto indirect_mask; else { *out++ = ' '; goto indirect; } case EXT: /* extended [or direct and mask for A/O/E/TIM] */ if( MASK(instruction & 15) ) goto direct_mask; else goto extended; #undef MASK case ASPIMM: /* A/SP and immediate */ if( (instruction & 15) == 13 ) goto relative_jump; if( (instruction & 15) == 3 || (instruction & 15) == 12 || (instruction & 15) == 14 ) goto immediate16; *out++ = 'A'; goto immediate; case ASPDIR: /* A/SP and direct */ if( (instruction & 15) != 3 && (instruction & 15) < 12 ) *out++ = 'A'; *out++ = ' '; goto direct; case ASPIND: /* A/SP and indirect */ if( (instruction & 15) != 3 && (instruction & 15) < 12 ) *out++ = 'A'; *out++ = ' '; goto indirect; case ASPEXT: /* A/SP and extended */ if( (instruction & 15) != 3 && (instruction & 15) < 12 ) *out++ = 'A'; goto extended; case BXIMM: /* B/X and immediate */ if( (instruction & 15) == 3 || (instruction & 15) == 12 || (instruction & 15) == 14 ) goto immediate16; *out++ = 'B'; goto immediate; case BXDIR: /* B/X and direct */ if( (instruction & 15) != 3 && (instruction & 15) < 12 ) *out++ = 'B'; *out++ = ' '; goto direct; case BXIND: /* B/X and indirect */ if( (instruction & 15) != 3 && (instruction & 15) < 12 ) *out++ = 'B'; *out++ = ' '; goto indirect; case BXEXT: /* B/X and extended */ if( (instruction & 15) != 3 && (instruction & 15) < 12 ) *out++ = 'B'; goto extended; immediate: sprintf(out, " #$%02X", byte()); break; immediate16: sprintf(out, " #$%04X", word()); break; indirect_mask: out += sprintf(out, " #$%02X,", byte()); indirect: sprintf(out, "%s,X", address(byte())); break; direct_mask: out += sprintf(out, " #$%02X,", byte()); direct: sprintf(out, "%s", address(byte())); break; extended: sprintf(out, " %s", address(word())); break; } bytes[bytes_ptr] = '\0'; printf(repr, bytes); putchar('\n'); bytes_ptr = 0; } int main(int argc, char* argv[]) { unsigned int this_pc = 0, skip = 0; if( argc < 2 ) { printf("usage: %s [OPTIONS, FILES] ...\n" "FILEs: standard files or - for stdin\n" "OPTIONs:\n" " -b print bytes before instructions\n" " -pPC set initial PC\n" " -r enable naming of internal register addresses\n" " -sOFFSET start disassembling at offset\n" " -v enable naming of interrupt vector addresses\n" " -z enable naming of TX81Z-specific addresses\n", argv[0]); } while( argv++, --argc ) { if( **argv == '-' ) { (*argv)++; do switch( **argv ) { case '\0': binary = stdin; while( skip-- ) getchar(); pc = this_pc; while( byte(), this_byte != EOF ) parse(); this_pc = skip = 0; break; case 'b': /* print bytes */ print_bytes = 1; break; case 'r': /* enable register naming */ register_names = 1; break; case 'v': /* enable vector naming */ vector_names = 1; break; case 'z': /* enable tx81z naming */ tx81z_names = 1; break; case 'p': /* set initial pc */ this_pc = strtol(&(*argv)[2], argv, 0) & 0xFFFF; break; case 's': /* skip n bytes */ skip = strtol(&(*argv)[2], argv, 0); this_pc += skip; this_pc &= 0xFFFF; break; } while( *++(*argv) ); } else if( (binary = fopen(*argv, "rb")) == NULL ) fprintf(stderr, "could not open %s\n", *argv); else { fseek(binary, skip, SEEK_SET); pc = this_pc; while( byte(), this_byte != EOF ) parse(); this_pc = skip = 0; fclose(binary); } } return 0; }