#include "opcode-bitmask-gen.h" #include #include #include #include #include #define AM_ORTHO (AM_ORTHO | AM_ORTHO2) #define AM_ADDR (AM_ZM | AM_ZMX | AM_ZMY | \ AM_IND | AM_INDX | AM_INDY | \ AM_ABS | AM_ABX | AM_ABY | \ AM_AIND | AM_AINDX | AM_AINDY | \ AM_INDX2 | AM_ZM2) void vprint_reason(const char *reason, va_list ap) { if (reason != NULL && isprint(reason[0])) { printf("Reason: "); vprintf(reason, ap); } } void print_reason(const char *reason, ...) { va_list ap; va_start(ap, reason); vprint_reason(reason, ap); va_end(ap); } void failed(const char *name, const char *reason, ...) { va_list ap; va_start(ap, reason); printf("%s failed. ", name); vprint_reason(reason, ap); putchar('\n'); va_end(ap); } int vformat_len(const char *fmt, va_list ap) { return vsnprintf(NULL, 0, fmt, ap); } int format_len(const char *fmt, ...) { int len = 0; va_list ap; va_start(ap, fmt); len = vformat_len(fmt, ap); va_end(ap); return len; } #if 0 int log2_int(uint32_t n) { int j = 0; for (int i = 4; i >= 0; i--) { int tmp = 1 << i; j |= ((n >> j) >= (uint32_t)1 << tmp) ? tmp : 0; } return j; } #else int log2_int(uint32_t n) { return (n > 1) ? 1+log2_int(n/2) : 0; } #endif uint32_t get_msb(uint32_t n) { return 1 << log2_int(n); } int get_addr_mode_type(uint32_t addr_mode) { return addr_mode_type[log2_int(addr_mode)]; } uint8_t get_ind(int instr, int addr_mode) { uint8_t base_idx = 0; uint8_t offset = 0; switch (instr) { case CMP: base_idx = CMP_IND; break; case CPB: base_idx = CPB_IND; break; case JMP: base_idx = JMP_IND; break; case JSR: base_idx = JSR_IND; break; case LDA: base_idx = LDA_IND; break; case LDB: base_idx = LDB_IND; break; case LDX: base_idx = LDX_IND; break; case LDY: base_idx = LDY_IND; break; case STA: base_idx = STA_IND; break; case STB: base_idx = STB_IND; break; case STX: base_idx = STX_IND; break; case STY: base_idx = STY_IND; break; } switch (addr_mode) { case IND : offset = 0; break; case INDY: offset += 1; break; case INDX: offset += 2; break; } return base_idx + offset; } uint8_t get_eind(int instr) { switch (instr) { case DEC: return DEC_EIND; case INC: return INC_EIND; case STY: return STY_EIND; case STA: return STA_EIND; case STB: return STB_EIND; case LDX: return LDX_EIND; case STX: return STX_EIND; case CPB: return CPB_EIND; case CPX: return CPX_EIND; case CPY: return CPY_EIND; } return 0xFF; } uint8_t get_ext_ortho(int instr) { switch (instr) { case LEA: return OP_LEA; case PEA: return OP_PEA; case ADD: return OP_ADD; case SUB: return OP_SUB; case NOT: return OP_NOT; case CLZ: return OP_CLZ; case CLO: return OP_CLO; case SWP: return OP_SWP; case PCN: return OP_PCN; } return 0xFF; } uint8_t get_opcode(const instruction *instr, int addr_mode, int inst_ext, int ext, int inst_id) { const uint8_t opcode = instr->op; const uint32_t addr_mode_mask = instr->am; addr_mode = (ext == ORTHO) ? ORTHO_EXT : addr_mode; if (inst_ext < ext) { switch (ext) { case EXT: if (addr_mode != EIND) { return 0xFF; } break; case ORTHO: if (addr_mode != ORTHO_EXT) { return 0xFF; } break; } } switch (addr_mode) { case ZM: if (addr_mode_mask & AM_ZM) { return opcode + 0x04; } else if (addr_mode_mask & AM_ZM2) { /* Add 0x20 to opcode if this is JMP zm, or JSR zm. */ return opcode + 0x20; } break; case ZMX: if (addr_mode_mask & AM_ZMX) { /* Add 0x06 to opcode if we're using the base ISA, and 0x54 if we're using the base extension. */ return opcode + ((ext == BASE) ? 0x06 : 0x54); } break; case ZMY: if (addr_mode_mask & AM_ZMY) { return opcode + 0x14; } break; case INDX: if (addr_mode_mask & AM_INDX) { /* Add 0x16 to opcode if we're using the base ISA, and 0x94 if we're using the base extension. */ return opcode + ((ext == BASE) ? 0x16 : 0x94); } /* Falls Through. */ case IND: case INDY: /* Are we using the base ISA? */ if (ext == BASE && (addr_mode_mask & AM_IND|AM_INDY|AM_INDX2)) { /* Get opcode from lookup table. */ return ind_ops[get_ind(inst_id, addr_mode)]; /* Are we using the base extension? */ } else if (ext == 1) { /* Add 0x44 to opcode if addressing mode is just indirect, and 0x84 otherwise. */ return opcode + ((addr_mode == IND) ? 0x44 : 0x84); } break; case ABS: if (addr_mode_mask & AM_ABS) { return opcode + 0x10; } break; case ABSX: if (addr_mode_mask & AM_ABX) { return opcode + 0x50; } break; case ABSY: if (addr_mode_mask & AM_ABY) { return opcode; } break; case AIND: if (addr_mode_mask & AM_AIND) { return opcode + 0x40; } break; case AINDX: if (addr_mode_mask & AM_AINDX) { return opcode + 0x90; } break; case AINDY: if (addr_mode_mask & AM_AINDY) { return opcode + 0x80; } break; case EIND: if (ext < ORTHO && (addr_mode_mask & AM_EIND|AM_EIND2)) { const int eind_type = ((addr_mode_mask & AM_EIND2) != 0); switch (eind_type) { case 0: return (ext == EXT) ? opcode+0x14 : opcode+0x10; case 1: return (ext == EXT) ? opcode+0x01 : eind_base_ops[get_eind(inst_id)]; } } break; case ORTHO_EXT: if (addr_mode_mask & AM_ORTHO|AM_ORTHO2) { return (inst_ext == EXT) ? ext_ortho_ops[get_ext_ortho(inst_id)] : opcode; } break; case IMM: if (addr_mode_mask & AM_IMM) { return opcode; } break; case IMPL: if (addr_mode_mask & AM_IMPL) { return opcode; } break; case REL: if (addr_mode_mask & AM_REL) { return opcode; } break; case BREG: if (addr_mode_mask & AM_BREG) { return opcode + 0x14; } break; default: break; } return 0xFF; } void set_opcode_bit(uint32_t *mask, int opcode) { const uint32_t o8 = opcode / 32, o8m = 1 << (opcode % 32); mask[o8] |= o8m; } void set_ortho_opcode_bits(uint32_t *mask, int opcode, int is_1op, int is_cc) { for (int i = 0; i < ((is_cc) ? 8 : 1); ++i, opcode += (is_cc) ? 0x20 : 0) { for (int j = 0; j < 4-is_1op; j += 1+is_1op) { set_opcode_bit(mask, opcode | (j << 3)); } } } void search_inst_table_entry(const instruction *inst, uint32_t *mask, uint32_t addr_modes, int inst_ext, int ext, uint8_t inst_id) { addr_modes &= inst->am; switch (ext) { case BASE: addr_modes &= ~(AM_ORTHO|AM_ORTHO2|AM_EIND|AM_EIND2); break; case EXT : addr_modes &= (inst_ext == BASE) ? (AM_EIND|AM_EIND2) : ~(AM_ORTHO|AM_ORTHO2); break; } for (uint32_t i = addr_modes; i; i &= ~get_msb(i)) { const uint8_t opcode = get_opcode(inst, get_addr_mode_type(i), inst_ext, ext, inst_id); const uint32_t o8 = opcode / 32, o8m = 1 << (opcode % 32); if (opcode == 0xFF) { continue; } if (ext == ORTHO) { const int is_1op = (inst->am & AM_ORTHO2) == AM_ORTHO2; const int is_cc = (inst_ext == ORTHO && inst_id == SET); set_ortho_opcode_bits(mask, opcode, is_1op, is_cc); } else { set_opcode_bit(mask, opcode); } /*printf("table: %i, i: %2i, j: 0x%08X, mask: {", table, i, j); for (int k = mask_size-1; k >= 0; --k) { printf("0x%08X%s", mask[k], (k) ? ", " : "}\n"); }*/ } } int print_mask(uint32_t addr_modes, instruction_id *inst_ids, int ext) { char *str = calloc(format_len("t(0, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n", 0, 1, 2, 3, 4, 5, 6, 7) + 1, sizeof(char)); char *tmp = str; uint32_t mask[8] = {}; const size_t mask_size = sizeof(mask) / sizeof(uint32_t); const uint32_t max_addr_mode_mask = -(uint32_t)1 >> (32-AMT_LEN); addr_modes = (addr_modes & ~max_addr_mode_mask) ? max_addr_mode_mask : addr_modes; if (ext > ORTHO) { failed("print_mask()", "Supplied extension is invalid, because it's out of the intended bounds."); free(str); tmp = NULL; str = NULL; return 0; } if (inst_ids == NULL) { for (int table = 0; table <= ext; ++table) { int max_ops = 0; const uint32_t addr_mode_mask = (ext && table < ext) ? ((ext == EXT) ? AM_EIND|AM_EIND2 : AM_ORTHO|AM_ORTHO2) : addr_modes; const instruction *inst_table; switch (ext) { case BASE: inst_table = inst, max_ops = OPNUM; break; case EXT: inst_table = ext_inst, max_ops = EXT_OPNUM; break; case ORTHO: inst_table = ortho_inst, max_ops = ORTHO_OPNUM; break; } for (int i = 0; i < max_ops; ++i) { if (inst_table[i].am & addr_mode_mask) { search_inst_table_entry(&inst_table[i], mask, addr_modes, table, ext, i); #if 0 for (uint32_t j = addr_modes; j; j &= ~get_msb(j)) { const uint8_t opcode = get_opcode(&inst_table[i], get_addr_mode_type(j), table, ext, i); const uint32_t o8 = opcode / 32, o8m = 1 << (opcode % 32); if (opcode == 0xFF) { continue; } if (ext == 2) { const int is_1op = (inst[i].am & AM_ORTHO2) == AM_ORTHO2; const int is_cc = (table == ORTHO && i == SET); set_ortho_opcode_bits(mask, opcode, is_1op, is_cc); } else { set_opcode_bit(mask, opcode); } /*printf("table: %i, i: %2i, j: 0x%08X, mask: {", table, i, j); for (int k = mask_size-1; k >= 0; --k) { printf("0x%08X%s", mask[k], (k) ? ", " : "}\n"); }*/ } #endif } } } } else { for (int i = 0; inst_ids[i].ext >= BASE; ++i) { const uint32_t addr_mode_mask = (ext && inst_ids[i].ext < ext) ? ((ext == EXT) ? AM_EIND|AM_EIND2 : AM_ORTHO|AM_ORTHO2) : addr_modes; const instruction *inst_entry; switch (inst_ids[i].ext) { case BASE : inst_entry = &inst[inst_ids[i].id]; break; case EXT : inst_entry = &ext_inst[inst_ids[i].id]; break; case ORTHO: inst_entry = &ortho_inst[inst_ids[i].id]; break; } if (inst_entry->am & addr_mode_mask) { search_inst_table_entry(inst_entry, mask, addr_modes, inst_ids[i].ext, ext, inst_ids[i].id); } } } tmp += sprintf(tmp, "t(0, "); for (int i = mask_size-1; i >= 0; --i) { tmp += sprintf(tmp, "0x%08X%s", mask[i], (i) ? ", " : ")\n"); } printf("%s", str); free(str); tmp = NULL; str = NULL; return 1; } int get_delm_span(const char *str, const char *delm) { const int inv_span_len = strcspn(str, delm); return inv_span_len + strspn(&str[inv_span_len], delm); } int get_csv_len(const char *str) { int ret = 0; for (int i = 0; str[i] != '\0'; i += get_delm_span(&str[i], ", "), ++ret); return ret; } void free_csv_list(char **list) { for (int i = 0; list[i] != NULL; ++i) { free(list[i]); list[i] = NULL; } } char **parse_csv_list(const char *str) { const char *delm = ", "; char **list = calloc(get_csv_len(str)+1, sizeof(char *)); for (int i = 0, j = 0; str[i] != '\0'; i += get_delm_span(&str[i], delm), ++j) { const int len = strcspn(&str[i], delm); list[j] = calloc(len+1, sizeof(char)); memcpy(list[j], &str[i], len); } return list; } void set_extension(int *ext, int ext2, int force, const char *reason, ...) { int dummy = -1; ext = (ext != NULL) ? ext : &dummy; if (*ext < ext2 || *ext < BASE || force) { va_list ap; printf("Switching extension from %s, to %s. ", (*ext < BASE || *ext >= EXT_LEN) ? ext_names[*ext] : "auto", ext_names[ext2]); va_start(ap, reason); vprint_reason(reason, ap); va_end(ap); *ext = ext2; } } uint32_t get_addr_mode_bits(const char **list, int *ext) { const int is_inv = (list[0][0] == '~'); const uint32_t ext_mask = (AM_ORTHO|AM_ORTHO2|AM_EIND|AM_EIND2|AM_AIND|AM_AINDX|AM_AINDY); const uint32_t max_addr_mode_mask = -(uint32_t)1 >> (32-AMT_LEN); int is_ext = 0; uint32_t addr_modes = 0; uint32_t bits = 0; for (int i = 0; list[i] != NULL; ++i) { const int inv = (!i) ? is_inv : 0; const char *str = list[i]+inv; for (int j = 0; j < AMT_LEN; ++j) { if (!strcasecmp(str, adrmode[j])) { //printf("i: %i, str: %s, adrmode: %s\n", i, str, adrmode[i]); bits |= (1 << j); } } } is_ext = (bits & ext_mask) ? 1 : is_ext; addr_modes |= (is_inv) ? ~(bits) & (max_addr_mode_mask & ((is_ext) ? -1 : ~ext_mask)) : bits; addr_modes &= (is_ext && (bits & ~(AM_ORTHO|AM_ORTHO2))) ? ~(AM_ORTHO|AM_ORTHO2) : -1; if (is_ext) { const int ext2 = (addr_modes & AM_ORTHO) ? ORTHO : EXT; const char *addr_mode_reason = (ext2 == ORTHO) ? "was set to ortho" : "is a base extension addressing mode"; set_extension(ext, ext2, 1, "The addressing mode %s.\n", addr_mode_reason); } return addr_modes; } instruction_id *get_instruction_ids(const char **list, int *ext) { int has_failed = 0; char *fail_reason = NULL; instruction_id *inst_ids = NULL; int list_len = 0; if (list != NULL) { for (list_len = 0; list[list_len] != NULL; ++list_len); if (list_len == 0) { has_failed = 1; fail_reason = "Instruction list length is zero."; } else { inst_ids = calloc(list_len+1, sizeof(instruction_id)); memset(inst_ids, -1, sizeof(instruction_id)*(list_len+1)); } } else { has_failed = 1; fail_reason = "Instruction list is NULL."; } if (has_failed) { failed("get_instruction_ids()", "%s", fail_reason); return NULL; } for (int i = 0; list[i] != NULL; ++i) { for (int j = 0; j < EXT_LEN && inst_ids[i].ext < 0; ++j) { const char **table; switch (j) { case BASE : table = mne; break; case EXT : table = ext_mne; break; case ORTHO: table = ortho_mne; break; } for (int k = 0; table[k] != NULL; ++k) { if (!strcasecmp(list[i], table[k])) { char *ext_str = NULL; inst_ids[i] = (instruction_id){.ext = j, .id = k}; switch (j) { case BASE : ext_str = "Base ISA"; break; case EXT : ext_str = "Base Extension"; break; case ORTHO: ext_str = "Ortho Extension"; break; } set_extension(ext, j, 0, "The instruction is from the %s.\n", ext_str); break; } } } } return inst_ids; } int get_extension_id(const char *str) { for (int i = 0; i < EXT_LEN; ++i) { if (!strcasecmp(str, ext_names[i])) { return i; } } return -1; } void usage(const char *name) { printf("Usage: %s -a [, ...] -i [, ...] -e \n", name); } options *get_options(int argc, char **argv) { options *opts = calloc(1, sizeof(options)); for (int i = 1; i < argc; ++i) { if (argv[i][0] == '-') { const char option = argv[i++][1]; if (option == 'a' || option == 'i') { const char **list = (const char **)parse_csv_list(argv[i]); switch (option) { case 'a': opts->addr_modes = get_addr_mode_bits(list, &opts->ext); break; case 'i': opts->instr = get_instruction_ids(list, &opts->ext); break; } opts->addr_modes = (opts->instr != NULL && opts->addr_modes == 0) ? -1 : opts->addr_modes; free_csv_list((char **)list); } else if (option == 'e') { const int ext = get_extension_id(argv[i]); opts->ext = (opts->ext < ext) ? ext : opts->ext; opts->addr_modes = (opts->addr_modes == 0) ? -1 : opts->addr_modes; } else { failed(argv[0], "Invalid option \"-%c\".", option); usage(argv[0]); free(opts); return NULL; } } } return opts; } options *parse_args(int argc, char **argv) { if (argc == 1) { failed(argv[0], "No arguments."); usage(argv[0]); return NULL; } return get_options(argc, argv); } int main(int argc, char **argv) { int ret; char *reason = NULL; options *opts = parse_args(argc, argv); if (opts == NULL) { return 1; } if (!opts->addr_modes) { reason = "Addressing mode mask is zero."; ret = 1; } else { ret = !print_mask(opts->addr_modes, opts->instr, opts->ext); } if (ret != 0) { if (reason != NULL) { failed(argv[0], reason); usage(argv[0]); } } free(opts->instr); free(opts); return ret; }