#include "opcode-bitmask-gen.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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
#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) {
return opcode + 0x20;
}
break;
case ZMX:
if (addr_mode_mask & AM_ZMX) {
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) {
return opcode + ((ext == BASE) ? 0x16 : 0x94);
}
case IND:
case INDY:
if (ext == BASE && (addr_mode_mask & AM_IND|AM_INDY|AM_INDX2)) {
return ind_ops[get_ind(inst_id, addr_mode)];
} else if (ext == 1) {
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 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);
}
}
}
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
#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])) {
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 <addr_mode>[,<addr_mode> ...] -i <instruction>[,<instruction> ...] -e <isa_extension>\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;
}