/* ======================================================================== */ /* ========================= LICENSING & COPYRIGHT ======================== */ /* ======================================================================== */ /* * MUSASHI * Version 4.60 * * A portable Motorola M680x0 processor emulation engine. * Copyright Karl Stenerud. All rights reserved. * FPU and MMU by R. Belmont. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* ======================================================================== */ /* ============================ CODE GENERATOR ============================ */ /* ======================================================================== */ /* * This is the code generator program which will generate the opcode table * and the final opcode handlers. * * It requires an input file to function (default m68k_in.c), but you can * specify your own like so: * * m68kmake * * where output path is the path where the output files should be placed, and * input file is the file to use for input. * * If you modify the input file greatly from its released form, you may have * to tweak the configuration section a bit since I'm using static allocation * to keep things simple. * * * TODO: - build a better code generator for the move instruction. * - Add callm and rtm instructions * - Fix RTE to handle other format words * - Add address error (and bus error?) handling */ static const char g_version[] = "4.60"; /* ======================================================================== */ /* =============================== INCLUDES =============================== */ /* ======================================================================== */ #include #include #include #include #include /* ======================================================================== */ /* ============================= CONFIGURATION ============================ */ /* ======================================================================== */ #define M68K_MAX_PATH 1024 #define M68K_MAX_DIR 1024 #define MAX_LINE_LENGTH 200 /* length of 1 line */ #define MAX_BODY_LENGTH 300 /* Number of lines in 1 function */ #define MAX_REPLACE_LENGTH 30 /* Max number of replace strings */ #define MAX_INSERT_LENGTH 5000 /* Max size of insert piece */ #define MAX_NAME_LENGTH 30 /* Max length of ophandler name */ #define MAX_SPEC_PROC_LENGTH 4 /* Max length of special processing str */ #define MAX_SPEC_EA_LENGTH 5 /* Max length of specified EA str */ #define EA_ALLOWED_LENGTH 11 /* Max length of ea allowed str */ #define MAX_OPCODE_INPUT_TABLE_LENGTH 1000 /* Max length of opcode handler tbl */ #define MAX_OPCODE_OUTPUT_TABLE_LENGTH 3000 /* Max length of opcode handler tbl */ /* Default filenames */ #define FILENAME_INPUT "m68k_in.c" #define FILENAME_PROTOTYPE "m68kops.h" #define FILENAME_TABLE "m68kops.c" /* Identifier sequences recognized by this program */ #define ID_INPUT_SEPARATOR "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" #define ID_BASE "M68KMAKE" #define ID_PROTOTYPE_HEADER ID_BASE "_PROTOTYPE_HEADER" #define ID_PROTOTYPE_FOOTER ID_BASE "_PROTOTYPE_FOOTER" #define ID_TABLE_HEADER ID_BASE "_TABLE_HEADER" #define ID_TABLE_FOOTER ID_BASE "_TABLE_FOOTER" #define ID_TABLE_BODY ID_BASE "_TABLE_BODY" #define ID_TABLE_START ID_BASE "_TABLE_START" #define ID_OPHANDLER_HEADER ID_BASE "_OPCODE_HANDLER_HEADER" #define ID_OPHANDLER_FOOTER ID_BASE "_OPCODE_HANDLER_FOOTER" #define ID_OPHANDLER_BODY ID_BASE "_OPCODE_HANDLER_BODY" #define ID_END ID_BASE "_END" #define ID_OPHANDLER_NAME ID_BASE "_OP" #define ID_OPHANDLER_EA_AY_8 ID_BASE "_GET_EA_AY_8" #define ID_OPHANDLER_EA_AY_16 ID_BASE "_GET_EA_AY_16" #define ID_OPHANDLER_EA_AY_32 ID_BASE "_GET_EA_AY_32" #define ID_OPHANDLER_OPER_AY_8 ID_BASE "_GET_OPER_AY_8" #define ID_OPHANDLER_OPER_AY_16 ID_BASE "_GET_OPER_AY_16" #define ID_OPHANDLER_OPER_AY_32 ID_BASE "_GET_OPER_AY_32" #define ID_OPHANDLER_CC ID_BASE "_CC" #define ID_OPHANDLER_NOT_CC ID_BASE "_NOT_CC" #ifndef DECL_SPEC #define DECL_SPEC #endif /* DECL_SPEC */ /* ======================================================================== */ /* ============================== PROTOTYPES ============================== */ /* ======================================================================== */ enum { CPU_TYPE_000=0, CPU_TYPE_010, CPU_TYPE_020, CPU_TYPE_030, CPU_TYPE_040, NUM_CPUS }; #define UNSPECIFIED "." #define UNSPECIFIED_CH '.' #define HAS_NO_EA_MODE(A) (strcmp(A, "..........") == 0) #define HAS_EA_AI(A) ((A)[0] == 'A') #define HAS_EA_PI(A) ((A)[1] == '+') #define HAS_EA_PD(A) ((A)[2] == '-') #define HAS_EA_DI(A) ((A)[3] == 'D') #define HAS_EA_IX(A) ((A)[4] == 'X') #define HAS_EA_AW(A) ((A)[5] == 'W') #define HAS_EA_AL(A) ((A)[6] == 'L') #define HAS_EA_PCDI(A) ((A)[7] == 'd') #define HAS_EA_PCIX(A) ((A)[8] == 'x') #define HAS_EA_I(A) ((A)[9] == 'I') enum { EA_MODE_NONE, /* No special addressing mode */ EA_MODE_AI, /* Address register indirect */ EA_MODE_PI, /* Address register indirect with postincrement */ EA_MODE_PI7, /* Address register 7 indirect with postincrement */ EA_MODE_PD, /* Address register indirect with predecrement */ EA_MODE_PD7, /* Address register 7 indirect with predecrement */ EA_MODE_DI, /* Address register indirect with displacement */ EA_MODE_IX, /* Address register indirect with index */ EA_MODE_AW, /* Absolute word */ EA_MODE_AL, /* Absolute long */ EA_MODE_PCDI, /* Program counter indirect with displacement */ EA_MODE_PCIX, /* Program counter indirect with index */ EA_MODE_I /* Immediate */ }; /* Everything we need to know about an opcode */ typedef struct { char name[MAX_NAME_LENGTH]; /* opcode handler name */ unsigned char size; /* Size of operation */ char spec_proc[MAX_SPEC_PROC_LENGTH]; /* Special processing mode */ char spec_ea[MAX_SPEC_EA_LENGTH]; /* Specified effective addressing mode */ unsigned char bits; /* Number of significant bits (used for sorting the table) */ unsigned short op_mask; /* Mask to apply for matching an opcode to a handler */ unsigned short op_match; /* Value to match after masking */ char ea_allowed[EA_ALLOWED_LENGTH]; /* Effective addressing modes allowed */ char cpu_mode[NUM_CPUS]; /* User or supervisor mode */ char cpus[NUM_CPUS+1]; /* Allowed CPUs */ unsigned char cycles[NUM_CPUS]; /* cycles for 000, 010, 020, 030, 040 */ } opcode_struct; /* All modifications necessary for a specific EA mode of an instruction */ typedef struct { char* fname_add; char* ea_add; unsigned int mask_add; unsigned int match_add; } ea_info_struct; /* Holds the body of a function */ typedef struct { char body[MAX_BODY_LENGTH][MAX_LINE_LENGTH+1]; int length; } body_struct; /* Holds a sequence of search / replace strings */ typedef struct { char replace[MAX_REPLACE_LENGTH][2][MAX_LINE_LENGTH+1]; int length; } replace_struct; /* Function Prototypes */ void error_exit(char* fmt, ...); void perror_exit(char* fmt, ...); int check_strsncpy(char* dst, char* src, int maxlength); int check_atoi(char* str, int *result); int skip_spaces(char* str); int num_bits(int value); int atoh(char* buff); int fgetline(char* buff, int nchars, FILE* file); int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type); opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea); opcode_struct* find_illegal_opcode(void); int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea); void add_replace_string(replace_struct* replace, char* search_str, char* replace_str); void write_body(FILE* filep, body_struct* body, replace_struct* replace); void get_base_name(char* base_name, opcode_struct* op); void write_function_name(FILE* filep, char* base_name); void add_opcode_output_table_entry(opcode_struct* op, char* name); static int DECL_SPEC compare_nof_true_bits(const void* aptr, const void* bptr); void print_opcode_output_table(FILE* filep); void write_table_entry(FILE* filep, opcode_struct* op); void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode); void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode); void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op); void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset); void process_opcode_handlers(FILE* filep); void populate_table(void); void read_insert(char* insert); /* ======================================================================== */ /* ================================= DATA ================================= */ /* ======================================================================== */ /* Name of the input file */ char g_input_filename[M68K_MAX_PATH] = FILENAME_INPUT; /* File handles */ FILE* g_input_file = NULL; FILE* g_prototype_file = NULL; FILE* g_table_file = NULL; int g_num_functions = 0; /* Number of functions processed */ int g_num_primitives = 0; /* Number of function primitives read */ int g_line_number = 1; /* Current line number */ /* Opcode handler table */ opcode_struct g_opcode_input_table[MAX_OPCODE_INPUT_TABLE_LENGTH]; opcode_struct g_opcode_output_table[MAX_OPCODE_OUTPUT_TABLE_LENGTH]; int g_opcode_output_table_length = 0; const ea_info_struct g_ea_info_table[13] = {/* fname ea mask match */ {"", "", 0x00, 0x00}, /* EA_MODE_NONE */ {"ai", "AY_AI", 0x38, 0x10}, /* EA_MODE_AI */ {"pi", "AY_PI", 0x38, 0x18}, /* EA_MODE_PI */ {"pi7", "A7_PI", 0x3f, 0x1f}, /* EA_MODE_PI7 */ {"pd", "AY_PD", 0x38, 0x20}, /* EA_MODE_PD */ {"pd7", "A7_PD", 0x3f, 0x27}, /* EA_MODE_PD7 */ {"di", "AY_DI", 0x38, 0x28}, /* EA_MODE_DI */ {"ix", "AY_IX", 0x38, 0x30}, /* EA_MODE_IX */ {"aw", "AW", 0x3f, 0x38}, /* EA_MODE_AW */ {"al", "AL", 0x3f, 0x39}, /* EA_MODE_AL */ {"pcdi", "PCDI", 0x3f, 0x3a}, /* EA_MODE_PCDI */ {"pcix", "PCIX", 0x3f, 0x3b}, /* EA_MODE_PCIX */ {"i", "I", 0x3f, 0x3c}, /* EA_MODE_I */ }; const char *const g_cc_table[16][2] = { { "t", "T"}, /* 0000 */ { "f", "F"}, /* 0001 */ {"hi", "HI"}, /* 0010 */ {"ls", "LS"}, /* 0011 */ {"cc", "CC"}, /* 0100 */ {"cs", "CS"}, /* 0101 */ {"ne", "NE"}, /* 0110 */ {"eq", "EQ"}, /* 0111 */ {"vc", "VC"}, /* 1000 */ {"vs", "VS"}, /* 1001 */ {"pl", "PL"}, /* 1010 */ {"mi", "MI"}, /* 1011 */ {"ge", "GE"}, /* 1100 */ {"lt", "LT"}, /* 1101 */ {"gt", "GT"}, /* 1110 */ {"le", "LE"}, /* 1111 */ }; /* size to index translator (0 -> 0, 8 and 16 -> 1, 32 -> 2) */ const int g_size_select_table[33] = { 0, /* unsized */ 0, 0, 0, 0, 0, 0, 0, 1, /* 8 */ 0, 0, 0, 0, 0, 0, 0, 1, /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 /* 32 */ }; /* Extra cycles required for certain EA modes */ /* TODO: correct timings for 030, 040 */ const int g_ea_cycle_table[13][NUM_CPUS][3] = {/* 000 010 020 030 040 */ {{ 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}}, /* EA_MODE_NONE */ {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_AI */ {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_PI */ {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_PI7 */ {{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_PD */ {{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_PD7 */ {{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_DI */ {{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}, { 0, 7, 7}, { 0, 7, 7}}, /* EA_MODE_IX */ {{ 0, 8, 12}, { 0, 8, 12}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_AW */ {{ 0, 12, 16}, { 0, 12, 16}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_AL */ {{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_PCDI */ {{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}, { 0, 7, 7}, { 0, 7, 7}}, /* EA_MODE_PCIX */ {{ 0, 4, 8}, { 0, 4, 8}, { 0, 2, 4}, { 0, 2, 4}, { 0, 2, 4}}, /* EA_MODE_I */ }; /* Extra cycles for JMP instruction (000, 010) */ const int g_jmp_cycle_table[13] = { 0, /* EA_MODE_NONE */ 4, /* EA_MODE_AI */ 0, /* EA_MODE_PI */ 0, /* EA_MODE_PI7 */ 0, /* EA_MODE_PD */ 0, /* EA_MODE_PD7 */ 6, /* EA_MODE_DI */ 10, /* EA_MODE_IX */ 6, /* EA_MODE_AW */ 8, /* EA_MODE_AL */ 6, /* EA_MODE_PCDI */ 10, /* EA_MODE_PCIX */ 0, /* EA_MODE_I */ }; /* Extra cycles for JSR instruction (000, 010) */ const int g_jsr_cycle_table[13] = { 0, /* EA_MODE_NONE */ 4, /* EA_MODE_AI */ 0, /* EA_MODE_PI */ 0, /* EA_MODE_PI7 */ 0, /* EA_MODE_PD */ 0, /* EA_MODE_PD7 */ 6, /* EA_MODE_DI */ 10, /* EA_MODE_IX */ 6, /* EA_MODE_AW */ 8, /* EA_MODE_AL */ 6, /* EA_MODE_PCDI */ 10, /* EA_MODE_PCIX */ 0, /* EA_MODE_I */ }; /* Extra cycles for LEA instruction (000, 010) */ const int g_lea_cycle_table[13] = { 0, /* EA_MODE_NONE */ 4, /* EA_MODE_AI */ 0, /* EA_MODE_PI */ 0, /* EA_MODE_PI7 */ 0, /* EA_MODE_PD */ 0, /* EA_MODE_PD7 */ 8, /* EA_MODE_DI */ 12, /* EA_MODE_IX */ 8, /* EA_MODE_AW */ 12, /* EA_MODE_AL */ 8, /* EA_MODE_PCDI */ 12, /* EA_MODE_PCIX */ 0, /* EA_MODE_I */ }; /* Extra cycles for PEA instruction (000, 010) */ const int g_pea_cycle_table[13] = { 0, /* EA_MODE_NONE */ 6, /* EA_MODE_AI */ 0, /* EA_MODE_PI */ 0, /* EA_MODE_PI7 */ 0, /* EA_MODE_PD */ 0, /* EA_MODE_PD7 */ 10, /* EA_MODE_DI */ 14, /* EA_MODE_IX */ 10, /* EA_MODE_AW */ 14, /* EA_MODE_AL */ 10, /* EA_MODE_PCDI */ 14, /* EA_MODE_PCIX */ 0, /* EA_MODE_I */ }; /* Extra cycles for MOVEM instruction (000, 010) */ const int g_movem_cycle_table[13] = { 0, /* EA_MODE_NONE */ 0, /* EA_MODE_AI */ 0, /* EA_MODE_PI */ 0, /* EA_MODE_PI7 */ 0, /* EA_MODE_PD */ 0, /* EA_MODE_PD7 */ 4, /* EA_MODE_DI */ 6, /* EA_MODE_IX */ 4, /* EA_MODE_AW */ 8, /* EA_MODE_AL */ 0, /* EA_MODE_PCDI */ 0, /* EA_MODE_PCIX */ 0, /* EA_MODE_I */ }; /* Extra cycles for MOVES instruction (010) */ const int g_moves_cycle_table[13][3] = { { 0, 0, 0}, /* EA_MODE_NONE */ { 0, 4, 6}, /* EA_MODE_AI */ { 0, 4, 6}, /* EA_MODE_PI */ { 0, 4, 6}, /* EA_MODE_PI7 */ { 0, 6, 12}, /* EA_MODE_PD */ { 0, 6, 12}, /* EA_MODE_PD7 */ { 0, 12, 16}, /* EA_MODE_DI */ { 0, 16, 20}, /* EA_MODE_IX */ { 0, 12, 16}, /* EA_MODE_AW */ { 0, 16, 20}, /* EA_MODE_AL */ { 0, 0, 0}, /* EA_MODE_PCDI */ { 0, 0, 0}, /* EA_MODE_PCIX */ { 0, 0, 0}, /* EA_MODE_I */ }; /* Extra cycles for CLR instruction (010) */ const int g_clr_cycle_table[13][3] = { { 0, 0, 0}, /* EA_MODE_NONE */ { 0, 4, 6}, /* EA_MODE_AI */ { 0, 4, 6}, /* EA_MODE_PI */ { 0, 4, 6}, /* EA_MODE_PI7 */ { 0, 6, 8}, /* EA_MODE_PD */ { 0, 6, 8}, /* EA_MODE_PD7 */ { 0, 8, 10}, /* EA_MODE_DI */ { 0, 10, 14}, /* EA_MODE_IX */ { 0, 8, 10}, /* EA_MODE_AW */ { 0, 10, 14}, /* EA_MODE_AL */ { 0, 0, 0}, /* EA_MODE_PCDI */ { 0, 0, 0}, /* EA_MODE_PCIX */ { 0, 0, 0}, /* EA_MODE_I */ }; /* ======================================================================== */ /* =========================== UTILITY FUNCTIONS ========================== */ /* ======================================================================== */ /* Print an error message and exit with status error */ void error_exit(char* fmt, ...) { va_list args; fprintf(stderr, "In %s, near or on line %d:\n\t", g_input_filename, g_line_number); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); if(g_prototype_file) fclose(g_prototype_file); if(g_table_file) fclose(g_table_file); if(g_input_file) fclose(g_input_file); exit(EXIT_FAILURE); } /* Print an error message, call perror(), and exit with status error */ void perror_exit(char* fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); perror(""); if(g_prototype_file) fclose(g_prototype_file); if(g_table_file) fclose(g_table_file); if(g_input_file) fclose(g_input_file); exit(EXIT_FAILURE); } /* copy until 0 or space and exit with error if we read too far */ int check_strsncpy(char* dst, char* src, int maxlength) { char* p = dst; while(*src && *src != ' ') { *p++ = *src++; if(p - dst > maxlength) error_exit("Field too long"); } *p = 0; return p - dst; } /* copy until 0 or specified character and exit with error if we read too far */ int check_strcncpy(char* dst, char* src, char delim, int maxlength) { char* p = dst; while(*src && *src != delim) { *p++ = *src++; if(p - dst > maxlength) error_exit("Field too long"); } *p = 0; return p - dst; } /* convert ascii to integer and exit with error if we find invalid data */ int check_atoi(char* str, int *result) { int accum = 0; char* p = str; while(*p >= '0' && *p <= '9') { accum *= 10; accum += *p++ - '0'; } if(*p != ' ' && *p != 0) error_exit("Malformed integer value (%c)", *p); *result = accum; return p - str; } /* Skip past spaces in a string */ int skip_spaces(char* str) { char* p = str; while(*p == ' ') p++; return p - str; } /* Count the number of set bits in a value */ int num_bits(int value) { value = ((value & 0xaaaa) >> 1) + (value & 0x5555); value = ((value & 0xcccc) >> 2) + (value & 0x3333); value = ((value & 0xf0f0) >> 4) + (value & 0x0f0f); value = ((value & 0xff00) >> 8) + (value & 0x00ff); return value; } /* Convert a hex value written in ASCII */ int atoh(char* buff) { int accum = 0; for(;;buff++) { if(*buff >= '0' && *buff <= '9') { accum <<= 4; accum += *buff - '0'; } else if(*buff >= 'a' && *buff <= 'f') { accum <<= 4; accum += *buff - 'a' + 10; } else break; } return accum; } /* Get a line of text from a file, discarding any end-of-line characters */ int fgetline(char* buff, int nchars, FILE* file) { int length; if(fgets(buff, nchars, file) == NULL) return -1; if(buff[0] == '\r') memmove(buff, buff + 1, nchars - 1); length = strlen(buff); while(length && (buff[length-1] == '\r' || buff[length-1] == '\n')) length--; buff[length] = 0; g_line_number++; return length; } /* ======================================================================== */ /* =========================== HELPER FUNCTIONS =========================== */ /* ======================================================================== */ /* Calculate the number of cycles an opcode requires */ int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type) { int size = g_size_select_table[op->size]; if(op->cpus[cpu_type] == '.') return 0; if(cpu_type < CPU_TYPE_020) { if(cpu_type == CPU_TYPE_010) { if(strcmp(op->name, "moves") == 0) return op->cycles[cpu_type] + g_moves_cycle_table[ea_mode][size]; if(strcmp(op->name, "clr") == 0) return op->cycles[cpu_type] + g_clr_cycle_table[ea_mode][size]; } /* ASG: added these cases -- immediate modes take 2 extra cycles here */ if(cpu_type == CPU_TYPE_000 && ea_mode == EA_MODE_I && ((strcmp(op->name, "add") == 0 && strcmp(op->spec_proc, "er") == 0) || strcmp(op->name, "adda") == 0 || (strcmp(op->name, "and") == 0 && strcmp(op->spec_proc, "er") == 0) || (strcmp(op->name, "or") == 0 && strcmp(op->spec_proc, "er") == 0) || (strcmp(op->name, "sub") == 0 && strcmp(op->spec_proc, "er") == 0) || strcmp(op->name, "suba") == 0)) return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size] + 2; if(strcmp(op->name, "jmp") == 0) return op->cycles[cpu_type] + g_jmp_cycle_table[ea_mode]; if(strcmp(op->name, "jsr") == 0) return op->cycles[cpu_type] + g_jsr_cycle_table[ea_mode]; if(strcmp(op->name, "lea") == 0) return op->cycles[cpu_type] + g_lea_cycle_table[ea_mode]; if(strcmp(op->name, "pea") == 0) return op->cycles[cpu_type] + g_pea_cycle_table[ea_mode]; if(strcmp(op->name, "movem") == 0) return op->cycles[cpu_type] + g_movem_cycle_table[ea_mode]; } return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size]; } /* Find an opcode in the opcode handler list */ opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea) { opcode_struct* op; for(op = g_opcode_input_table;op < &g_opcode_input_table[MAX_OPCODE_INPUT_TABLE_LENGTH];op++) { if( strcmp(name, op->name) == 0 && (size == op->size) && strcmp(spec_proc, op->spec_proc) == 0 && strcmp(spec_ea, op->spec_ea) == 0) return op; } return NULL; } /* Specifically find the illegal opcode in the list */ opcode_struct* find_illegal_opcode(void) { opcode_struct* op; for(op = g_opcode_input_table;op < &g_opcode_input_table[MAX_OPCODE_INPUT_TABLE_LENGTH];op++) { if(strcmp(op->name, "illegal") == 0) return op; } return NULL; } /* Parse an opcode handler name */ int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea) { char* ptr = strstr(src, ID_OPHANDLER_NAME); if(ptr == NULL) return 0; ptr += strlen(ID_OPHANDLER_NAME) + 1; ptr += check_strcncpy(name, ptr, ',', MAX_NAME_LENGTH); if(*ptr != ',') return 0; ptr++; ptr += skip_spaces(ptr); *size = atoi(ptr); ptr = strstr(ptr, ","); if(ptr == NULL) return 0; ptr++; ptr += skip_spaces(ptr); ptr += check_strcncpy(spec_proc, ptr, ',', MAX_SPEC_PROC_LENGTH); if(*ptr != ',') return 0; ptr++; ptr += skip_spaces(ptr); ptr += check_strcncpy(spec_ea, ptr, ')', MAX_SPEC_EA_LENGTH); if(*ptr != ')') return 0; ptr++; ptr += skip_spaces(ptr); return 1; } /* Add a search/replace pair to a replace structure */ void add_replace_string(replace_struct* replace, char* search_str, char* replace_str) { if(replace->length >= MAX_REPLACE_LENGTH) error_exit("overflow in replace structure"); strcpy(replace->replace[replace->length][0], search_str); strcpy(replace->replace[replace->length++][1], replace_str); } /* Write a function body while replacing any selected strings */ void write_body(FILE* filep, body_struct* body, replace_struct* replace) { int i; int j; char* ptr; char output[MAX_LINE_LENGTH+1]; char temp_buff[MAX_LINE_LENGTH+1]; int found; for(i=0;ilength;i++) { strcpy(output, body->body[i]); /* Check for the base directive header */ if(strstr(output, ID_BASE) != NULL) { /* Search for any text we need to replace */ found = 0; for(j=0;jlength;j++) { ptr = strstr(output, replace->replace[j][0]); if(ptr) { /* We found something to replace */ found = 1; strcpy(temp_buff, ptr+strlen(replace->replace[j][0])); strcpy(ptr, replace->replace[j][1]); strcat(ptr, temp_buff); } } /* Found a directive with no matching replace string */ if(!found) error_exit("Unknown " ID_BASE " directive [%s]", output); } fprintf(filep, "%s\n", output); } fprintf(filep, "\n\n"); } /* Generate a base function name from an opcode struct */ void get_base_name(char* base_name, opcode_struct* op) { sprintf(base_name, "m68k_op_%s", op->name); if(op->size > 0) sprintf(base_name+strlen(base_name), "_%d", op->size); if(strcmp(op->spec_proc, UNSPECIFIED) != 0) sprintf(base_name+strlen(base_name), "_%s", op->spec_proc); if(strcmp(op->spec_ea, UNSPECIFIED) != 0) sprintf(base_name+strlen(base_name), "_%s", op->spec_ea); } /* Write the name of an opcode handler function */ void write_function_name(FILE* filep, char* base_name) { fprintf(filep, "static void %s(void)\n", base_name); } void add_opcode_output_table_entry(opcode_struct* op, char* name) { opcode_struct* ptr; if(g_opcode_output_table_length > MAX_OPCODE_OUTPUT_TABLE_LENGTH) error_exit("Opcode output table overflow"); ptr = g_opcode_output_table + g_opcode_output_table_length++; *ptr = *op; strcpy(ptr->name, name); ptr->bits = num_bits(ptr->op_mask); } /* * Comparison function for qsort() * For entries with an equal number of set bits in * the mask compare the match values */ static int DECL_SPEC compare_nof_true_bits(const void* aptr, const void* bptr) { const opcode_struct *a = aptr, *b = bptr; if(a->bits != b->bits) return a->bits - b->bits; if(a->op_mask != b->op_mask) return a->op_mask - b->op_mask; return a->op_match - b->op_match; } void print_opcode_output_table(FILE* filep) { int i; qsort((void *)g_opcode_output_table, g_opcode_output_table_length, sizeof(g_opcode_output_table[0]), compare_nof_true_bits); for(i=0;iname, op->op_mask, op->op_match); for(i=0;icycles[i]); if(i < NUM_CPUS-1) fprintf(filep, ", "); } fprintf(filep, "}},\n"); } /* Fill out an opcode struct with a specific addressing mode of the source opcode struct */ void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode) { int i; *dst = *src; for(i=0;icycles[i] = get_oper_cycles(dst, ea_mode, i); if(strcmp(dst->spec_ea, UNSPECIFIED) == 0 && ea_mode != EA_MODE_NONE) sprintf(dst->spec_ea, "%s", g_ea_info_table[ea_mode].fname_add); dst->op_mask |= g_ea_info_table[ea_mode].mask_add; dst->op_match |= g_ea_info_table[ea_mode].match_add; } /* Generate a final opcode handler from the provided data */ void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode) { char str[MAX_LINE_LENGTH+1]; opcode_struct* op = malloc(sizeof(opcode_struct)); /* Set the opcode structure and write the tables, prototypes, etc */ set_opcode_struct(opinfo, op, ea_mode); get_base_name(str, op); add_opcode_output_table_entry(op, str); write_function_name(filep, str); /* Add any replace strings needed */ if(ea_mode != EA_MODE_NONE) { sprintf(str, "EA_%s_8()", g_ea_info_table[ea_mode].ea_add); add_replace_string(replace, ID_OPHANDLER_EA_AY_8, str); sprintf(str, "EA_%s_16()", g_ea_info_table[ea_mode].ea_add); add_replace_string(replace, ID_OPHANDLER_EA_AY_16, str); sprintf(str, "EA_%s_32()", g_ea_info_table[ea_mode].ea_add); add_replace_string(replace, ID_OPHANDLER_EA_AY_32, str); sprintf(str, "OPER_%s_8()", g_ea_info_table[ea_mode].ea_add); add_replace_string(replace, ID_OPHANDLER_OPER_AY_8, str); sprintf(str, "OPER_%s_16()", g_ea_info_table[ea_mode].ea_add); add_replace_string(replace, ID_OPHANDLER_OPER_AY_16, str); sprintf(str, "OPER_%s_32()", g_ea_info_table[ea_mode].ea_add); add_replace_string(replace, ID_OPHANDLER_OPER_AY_32, str); } /* Now write the function body with the selected replace strings */ write_body(filep, body, replace); g_num_functions++; free(op); } /* Generate opcode variants based on available addressing modes */ void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op) { int old_length = replace->length; /* No ea modes available for this opcode */ if(HAS_NO_EA_MODE(op->ea_allowed)) { generate_opcode_handler(filep, body, replace, op, EA_MODE_NONE); return; } /* Check for and create specific opcodes for each available addressing mode */ if(HAS_EA_AI(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_AI); replace->length = old_length; if(HAS_EA_PI(op->ea_allowed)) { generate_opcode_handler(filep, body, replace, op, EA_MODE_PI); replace->length = old_length; if(op->size == 8) generate_opcode_handler(filep, body, replace, op, EA_MODE_PI7); } replace->length = old_length; if(HAS_EA_PD(op->ea_allowed)) { generate_opcode_handler(filep, body, replace, op, EA_MODE_PD); replace->length = old_length; if(op->size == 8) generate_opcode_handler(filep, body, replace, op, EA_MODE_PD7); } replace->length = old_length; if(HAS_EA_DI(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_DI); replace->length = old_length; if(HAS_EA_IX(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_IX); replace->length = old_length; if(HAS_EA_AW(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_AW); replace->length = old_length; if(HAS_EA_AL(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_AL); replace->length = old_length; if(HAS_EA_PCDI(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_PCDI); replace->length = old_length; if(HAS_EA_PCIX(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_PCIX); replace->length = old_length; if(HAS_EA_I(op->ea_allowed)) generate_opcode_handler(filep, body, replace, op, EA_MODE_I); replace->length = old_length; } /* Generate variants of condition code opcodes */ void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset) { char repl[20]; char replnot[20]; int i; int old_length = replace->length; opcode_struct* op = malloc(sizeof(opcode_struct)); *op = *op_in; op->op_mask |= 0x0f00; /* Do all condition codes except t and f */ for(i=2;i<16;i++) { /* Add replace strings for this condition code */ sprintf(repl, "COND_%s()", g_cc_table[i][1]); sprintf(replnot, "COND_NOT_%s()", g_cc_table[i][1]); add_replace_string(replace, ID_OPHANDLER_CC, repl); add_replace_string(replace, ID_OPHANDLER_NOT_CC, replnot); /* Set the new opcode info */ strcpy(op->name+offset, g_cc_table[i][0]); op->op_match = (op->op_match & 0xf0ff) | (i<<8); /* Generate all opcode variants for this modified opcode */ generate_opcode_ea_variants(filep, body, replace, op); /* Remove the above replace strings */ replace->length = old_length; } free(op); } /* Process the opcode handlers section of the input file */ void process_opcode_handlers(FILE* filep) { FILE* input_file = g_input_file; char func_name[MAX_LINE_LENGTH+1]; char oper_name[MAX_LINE_LENGTH+1]; int oper_size; char oper_spec_proc[MAX_LINE_LENGTH+1]; char oper_spec_ea[MAX_LINE_LENGTH+1]; opcode_struct* opinfo; replace_struct* replace = malloc(sizeof(replace_struct)); body_struct* body = malloc(sizeof(body_struct)); for(;;) { /* Find the first line of the function */ func_name[0] = 0; while(strstr(func_name, ID_OPHANDLER_NAME) == NULL) { if(strcmp(func_name, ID_INPUT_SEPARATOR) == 0) { free(replace); free(body); return; /* all done */ } if(fgetline(func_name, MAX_LINE_LENGTH, input_file) < 0) error_exit("Premature end of file when getting function name"); } /* Get the rest of the function */ for(body->length=0;;body->length++) { if(body->length > MAX_BODY_LENGTH) error_exit("Function too long"); if(fgetline(body->body[body->length], MAX_LINE_LENGTH, input_file) < 0) error_exit("Premature end of file when getting function body"); if(body->body[body->length][0] == '}') { body->length++; break; } } g_num_primitives++; /* Extract the function name information */ if(!extract_opcode_info(func_name, oper_name, &oper_size, oper_spec_proc, oper_spec_ea)) error_exit("Invalid " ID_OPHANDLER_NAME " format"); /* Find the corresponding table entry */ opinfo = find_opcode(oper_name, oper_size, oper_spec_proc, oper_spec_ea); if(opinfo == NULL) error_exit("Unable to find matching table entry for %s", func_name); replace->length = 0; /* Generate opcode variants */ if(strcmp(opinfo->name, "bcc") == 0 || strcmp(opinfo->name, "scc") == 0) generate_opcode_cc_variants(filep, body, replace, opinfo, 1); else if(strcmp(opinfo->name, "dbcc") == 0) generate_opcode_cc_variants(filep, body, replace, opinfo, 2); else if(strcmp(opinfo->name, "trapcc") == 0) generate_opcode_cc_variants(filep, body, replace, opinfo, 4); else generate_opcode_ea_variants(filep, body, replace, opinfo); } free(replace); free(body); } /* Populate the opcode handler table from the input file */ void populate_table(void) { char* ptr; char bitpattern[17]; opcode_struct* op; char buff[MAX_LINE_LENGTH]; int i; int temp; buff[0] = 0; /* Find the start of the table */ while(strcmp(buff, ID_TABLE_START) != 0) if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0) error_exit("(table_start) Premature EOF while reading table"); /* Process the entire table */ for(op = g_opcode_input_table;;op++) { if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0) error_exit("(inline) Premature EOF while reading table"); if(strlen(buff) == 0) continue; /* We finish when we find an input separator */ if(strcmp(buff, ID_INPUT_SEPARATOR) == 0) break; /* Extract the info from the table */ ptr = buff; /* Name */ ptr += skip_spaces(ptr); ptr += check_strsncpy(op->name, ptr, MAX_NAME_LENGTH); /* Size */ ptr += skip_spaces(ptr); ptr += check_atoi(ptr, &temp); op->size = (unsigned char)temp; /* Special processing */ ptr += skip_spaces(ptr); ptr += check_strsncpy(op->spec_proc, ptr, MAX_SPEC_PROC_LENGTH); /* Specified EA Mode */ ptr += skip_spaces(ptr); ptr += check_strsncpy(op->spec_ea, ptr, MAX_SPEC_EA_LENGTH); /* Bit Pattern (more processing later) */ ptr += skip_spaces(ptr); ptr += check_strsncpy(bitpattern, ptr, 17); /* Allowed Addressing Mode List */ ptr += skip_spaces(ptr); ptr += check_strsncpy(op->ea_allowed, ptr, EA_ALLOWED_LENGTH); /* CPU operating mode (U = user or supervisor, S = supervisor only */ ptr += skip_spaces(ptr); for(i=0;icpu_mode[i] = *ptr++; ptr += skip_spaces(ptr); } /* Allowed CPUs for this instruction */ for(i=0;icpus[i] = UNSPECIFIED_CH; op->cycles[i] = 0; ptr++; } else { op->cpus[i] = '0' + i; ptr += check_atoi(ptr, &temp); op->cycles[i] = (unsigned char)temp; } } /* generate mask and match from bitpattern */ op->op_mask = 0; op->op_match = 0; for(i=0;i<16;i++) { op->op_mask |= (bitpattern[i] != '.') << (15-i); op->op_match |= (bitpattern[i] == '1') << (15-i); } } /* Terminate the list */ op->name[0] = 0; } /* Read a header or footer insert from the input file */ void read_insert(char* insert) { char* ptr = insert; char* overflow = insert + MAX_INSERT_LENGTH - MAX_LINE_LENGTH; int length; char* first_blank = NULL; first_blank = NULL; /* Skip any leading blank lines */ for(length = 0;length == 0;length = fgetline(ptr, MAX_LINE_LENGTH, g_input_file)) if(ptr >= overflow) error_exit("Buffer overflow reading inserts"); if(length < 0) error_exit("Premature EOF while reading inserts"); /* Advance and append newline */ ptr += length; strcpy(ptr++, "\n"); /* Read until next separator */ for(;;) { /* Read a new line */ if(ptr >= overflow) error_exit("Buffer overflow reading inserts"); if((length = fgetline(ptr, MAX_LINE_LENGTH, g_input_file)) < 0) error_exit("Premature EOF while reading inserts"); /* Stop if we read a separator */ if(strcmp(ptr, ID_INPUT_SEPARATOR) == 0) break; /* keep track in case there are trailing blanks */ if(length == 0) { if(first_blank == NULL) first_blank = ptr; } else first_blank = NULL; /* Advance and append newline */ ptr += length; strcpy(ptr++, "\n"); } /* kill any trailing blank lines */ if(first_blank) ptr = first_blank; *ptr++ = 0; } /* ======================================================================== */ /* ============================= MAIN FUNCTION ============================ */ /* ======================================================================== */ int main(int argc, char **argv) { /* File stuff */ char output_path[M68K_MAX_DIR] = ""; char filename[M68K_MAX_PATH*2]; /* Section identifier */ char section_id[MAX_LINE_LENGTH+1]; /* Inserts */ char temp_insert[MAX_INSERT_LENGTH+1]; char prototype_footer_insert[MAX_INSERT_LENGTH+1]; char table_header_insert[MAX_INSERT_LENGTH+1]; char table_footer_insert[MAX_INSERT_LENGTH+1]; char ophandler_header_insert[MAX_INSERT_LENGTH+1]; char ophandler_footer_insert[MAX_INSERT_LENGTH+1]; /* Flags if we've processed certain parts already */ int prototype_header_read = 0; int prototype_footer_read = 0; int table_header_read = 0; int table_footer_read = 0; int ophandler_header_read = 0; int ophandler_footer_read = 0; int table_body_read = 0; int ophandler_body_read = 0; printf("\n\tMusashi v%s 68000, 68008, 68010, 68EC020, 68020, 68EC030, 68030, 68EC040, 68040 emulator\n", g_version); printf("\t\tCopyright Karl Stenerud (kstenerud@gmail.com)\n\n"); /* Check if output path and source for the input file are given */ if(argc > 1) { char *ptr; strcpy(output_path, argv[1]); for(ptr = strchr(output_path, '\\'); ptr; ptr = strchr(ptr, '\\')) *ptr = '/'; if(output_path[strlen(output_path)-1] != '/') strcat(output_path, "/"); if(argc > 2) strcpy(g_input_filename, argv[2]); } /* Open the files we need */ sprintf(filename, "%s%s", output_path, FILENAME_PROTOTYPE); if((g_prototype_file = fopen(filename, "wt")) == NULL) perror_exit("Unable to create prototype file (%s)\n", filename); sprintf(filename, "%s%s", output_path, FILENAME_TABLE); if((g_table_file = fopen(filename, "wt")) == NULL) perror_exit("Unable to create table file (%s)\n", filename); if((g_input_file=fopen(g_input_filename, "rt")) == NULL) perror_exit("can't open %s for input", g_input_filename); /* Get to the first section of the input file */ section_id[0] = 0; while(strcmp(section_id, ID_INPUT_SEPARATOR) != 0) if(fgetline(section_id, MAX_LINE_LENGTH, g_input_file) < 0) error_exit("Premature EOF while reading input file"); /* Now process all sections */ for(;;) { if(fgetline(section_id, MAX_LINE_LENGTH, g_input_file) < 0) error_exit("Premature EOF while reading input file"); if(strcmp(section_id, ID_PROTOTYPE_HEADER) == 0) { if(prototype_header_read) error_exit("Duplicate prototype header"); read_insert(temp_insert); fprintf(g_prototype_file, "%s\n\n", temp_insert); prototype_header_read = 1; } else if(strcmp(section_id, ID_TABLE_HEADER) == 0) { if(table_header_read) error_exit("Duplicate table header"); read_insert(table_header_insert); table_header_read = 1; } else if(strcmp(section_id, ID_OPHANDLER_HEADER) == 0) { if(ophandler_header_read) error_exit("Duplicate opcode handler header"); read_insert(ophandler_header_insert); ophandler_header_read = 1; } else if(strcmp(section_id, ID_PROTOTYPE_FOOTER) == 0) { if(prototype_footer_read) error_exit("Duplicate prototype footer"); read_insert(prototype_footer_insert); prototype_footer_read = 1; } else if(strcmp(section_id, ID_TABLE_FOOTER) == 0) { if(table_footer_read) error_exit("Duplicate table footer"); read_insert(table_footer_insert); table_footer_read = 1; } else if(strcmp(section_id, ID_OPHANDLER_FOOTER) == 0) { if(ophandler_footer_read) error_exit("Duplicate opcode handler footer"); read_insert(ophandler_footer_insert); ophandler_footer_read = 1; } else if(strcmp(section_id, ID_TABLE_BODY) == 0) { if(!prototype_header_read) error_exit("Table body encountered before prototype header"); if(!table_header_read) error_exit("Table body encountered before table header"); if(!ophandler_header_read) error_exit("Table body encountered before opcode handler header"); if(table_body_read) error_exit("Duplicate table body"); populate_table(); table_body_read = 1; } else if(strcmp(section_id, ID_OPHANDLER_BODY) == 0) { if(!prototype_header_read) error_exit("Opcode handlers encountered before prototype header"); if(!table_header_read) error_exit("Opcode handlers encountered before table header"); if(!ophandler_header_read) error_exit("Opcode handlers encountered before opcode handler header"); if(!table_body_read) error_exit("Opcode handlers encountered before table body"); if(ophandler_body_read) error_exit("Duplicate opcode handler section"); fprintf(g_table_file, "%s\n\n", ophandler_header_insert); process_opcode_handlers(g_table_file); fprintf(g_table_file, "%s\n\n", ophandler_footer_insert); ophandler_body_read = 1; } else if(strcmp(section_id, ID_END) == 0) { /* End of input file. Do a sanity check and then write footers */ if(!prototype_header_read) error_exit("Missing prototype header"); if(!prototype_footer_read) error_exit("Missing prototype footer"); if(!table_header_read) error_exit("Missing table header"); if(!table_footer_read) error_exit("Missing table footer"); if(!table_body_read) error_exit("Missing table body"); if(!ophandler_header_read) error_exit("Missing opcode handler header"); if(!ophandler_footer_read) error_exit("Missing opcode handler footer"); if(!ophandler_body_read) error_exit("Missing opcode handler body"); fprintf(g_table_file, "%s\n\n", table_header_insert); print_opcode_output_table(g_table_file); fprintf(g_table_file, "%s\n\n", table_footer_insert); fprintf(g_prototype_file, "%s\n\n", prototype_footer_insert); break; } else { error_exit("Unknown section identifier: %s", section_id); } } /* Close all files and exit */ fclose(g_prototype_file); fclose(g_table_file); fclose(g_input_file); printf("Generated %d opcode handlers from %d primitives\n", g_num_functions, g_num_primitives); return 0; } /* ======================================================================== */ /* ============================== END OF FILE ============================= */ /* ======================================================================== */