mirror of
				https://github.com/RetroDECK/Supermodel.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	
		
			
	
	
		
			1513 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1513 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /**
 | ||
|  |  ** Supermodel | ||
|  |  ** A Sega Model 3 Arcade Emulator. | ||
|  |  ** Copyright 2011 Bart Trzynadlowski  | ||
|  |  ** | ||
|  |  ** This file is part of Supermodel. | ||
|  |  ** | ||
|  |  ** Supermodel is free software: you can redistribute it and/or modify it under | ||
|  |  ** the terms of the GNU General Public License as published by the Free  | ||
|  |  ** Software Foundation, either version 3 of the License, or (at your option) | ||
|  |  ** any later version. | ||
|  |  ** | ||
|  |  ** Supermodel is distributed in the hope that it will be useful, but WITHOUT | ||
|  |  ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
|  |  ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||
|  |  ** more details. | ||
|  |  ** | ||
|  |  ** You should have received a copy of the GNU General Public License along | ||
|  |  ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.
 | ||
|  |  **/ | ||
|  |   | ||
|  | /*
 | ||
|  |  * m68kmake.c | ||
|  |  * | ||
|  |  * Program to generate the Musashi 68K core. | ||
|  |  * | ||
|  |  * Permission was obtained from Karl Stenerud to apply the GPL license to this | ||
|  |  * code. | ||
|  |  */ | ||
|  | 
 | ||
|  | /* ======================================================================== */ | ||
|  | /* ========================= LICENSING & COPYRIGHT ======================== */ | ||
|  | /* ======================================================================== */ | ||
|  | /*
 | ||
|  |  *                                  MUSASHI | ||
|  |  *                                Version 3.3 | ||
|  |  * | ||
|  |  * A portable Motorola M680x0 processor emulation engine. | ||
|  |  * Copyright 1998-2001 Karl Stenerud.  All rights reserved. | ||
|  |  * | ||
|  |  * This code may be freely used for non-commercial purposes as long as this | ||
|  |  * copyright notice remains unaltered in the source code and any binary files | ||
|  |  * containing this code in compiled form. | ||
|  |  * | ||
|  |  * All other lisencing terms must be negotiated with the author | ||
|  |  * (Karl Stenerud). | ||
|  |  * | ||
|  |  * The latest version of this code can be obtained at: | ||
|  |  * http://kstenerud.cjb.net
 | ||
|  |  */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * Modified For OpenVMS By:  Robert Alan Byer | ||
|  |  *                           byer@mail.ourservers.net | ||
|  |  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ======================================================================== */ | ||
|  | /* ============================ 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 <output path> <input file> | ||
|  |  * | ||
|  |  * 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 | ||
|  |  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | const char* g_version = "3.3"; | ||
|  | 
 | ||
|  | /* ======================================================================== */ | ||
|  | /* =============================== INCLUDES =============================== */ | ||
|  | /* ======================================================================== */ | ||
|  | 
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <ctype.h>
 | ||
|  | #include <stdarg.h>
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ======================================================================== */ | ||
|  | /* ============================= 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"
 | ||
|  | #define FILENAME_OPS_AC     "m68kopac.c"
 | ||
|  | #define FILENAME_OPS_DM     "m68kopdm.c"
 | ||
|  | #define FILENAME_OPS_NZ     "m68kopnz.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, | ||
|  | 	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 */ | ||
|  | } opcode_struct; | ||
|  | 
 | ||
|  | 
 | ||
|  | /* All modifications necessary for a specific EA mode of an instruction */ | ||
|  | typedef struct | ||
|  | { | ||
|  | 	const char* fname_add; | ||
|  | 	const 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(const char* fmt, ...); | ||
|  | void perror_exit(const 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, const char* search_str, const 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_prototype(FILE* filep, char* base_name); | ||
|  | 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(void); | ||
|  | 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; | ||
|  | FILE* g_ops_ac_file = NULL; | ||
|  | FILE* g_ops_dm_file = NULL; | ||
|  | FILE* g_ops_nz_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; | ||
|  | 
 | ||
|  | 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* 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) */ | ||
|  | 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 */ | ||
|  | int g_ea_cycle_table[13][NUM_CPUS][3] = | ||
|  | {/*       000           010           020   */ | ||
|  | 	{{ 0,  0,  0}, { 0,  0,  0}, { 0,  0,  0}}, /* EA_MODE_NONE */ | ||
|  | 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  4,  4}}, /* EA_MODE_AI   */ | ||
|  | 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  4,  4}}, /* EA_MODE_PI   */ | ||
|  | 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  4,  4}}, /* EA_MODE_PI7  */ | ||
|  | 	{{ 0,  6, 10}, { 0,  6, 10}, { 0,  5,  5}}, /* EA_MODE_PD   */ | ||
|  | 	{{ 0,  6, 10}, { 0,  6, 10}, { 0,  5,  5}}, /* EA_MODE_PD7  */ | ||
|  | 	{{ 0,  8, 12}, { 0,  8, 12}, { 0,  5,  5}}, /* EA_MODE_DI   */ | ||
|  | 	{{ 0, 10, 14}, { 0, 10, 14}, { 0,  7,  7}}, /* EA_MODE_IX   */ | ||
|  | 	{{ 0,  8, 12}, { 0,  8, 12}, { 0,  4,  4}}, /* EA_MODE_AW   */ | ||
|  | 	{{ 0, 12, 16}, { 0, 12, 16}, { 0,  4,  4}}, /* EA_MODE_AL   */ | ||
|  | 	{{ 0,  8, 12}, { 0,  8, 12}, { 0,  5,  5}}, /* EA_MODE_PCDI */ | ||
|  | 	{{ 0, 10, 14}, { 0, 10, 14}, { 0,  7,  7}}, /* EA_MODE_PCIX */ | ||
|  | 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  2,  4}}, /* EA_MODE_I    */ | ||
|  | }; | ||
|  | 
 | ||
|  | /* Extra cycles for JMP instruction (000, 010) */ | ||
|  | 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) */ | ||
|  | 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) */ | ||
|  | 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) */ | ||
|  | 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) */ | ||
|  | 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) */ | ||
|  | 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) */ | ||
|  | 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(const 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_ops_ac_file) fclose(g_ops_ac_file); | ||
|  | 	if(g_ops_dm_file) fclose(g_ops_dm_file); | ||
|  | 	if(g_ops_nz_file) fclose(g_ops_nz_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(const 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_ops_ac_file) fclose(g_ops_ac_file); | ||
|  | 	if(g_ops_dm_file) fclose(g_ops_dm_file); | ||
|  | 	if(g_ops_nz_file) fclose(g_ops_nz_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') | ||
|  | 		memcpy(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 */ | ||
|  | 		/* SV: but only when operating on long, and also on register direct mode */ | ||
|  | 		if(cpu_type == CPU_TYPE_000 && (ea_mode == EA_MODE_I || ea_mode == EA_MODE_NONE) && op->size == 32 && | ||
|  | 		   ((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->name != NULL;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->name != NULL;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, const char* search_str, const 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;i<body->length;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;j<replace->length;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"); | ||
|  | 		} | ||
|  | 		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 prototype of an opcode handler function */ | ||
|  | void write_prototype(FILE* filep, char* base_name) | ||
|  | { | ||
|  | 	fprintf(filep, "void %s(void);\n", base_name); | ||
|  | } | ||
|  | 
 | ||
|  | /* Write the name of an opcode handler function */ | ||
|  | void write_function_name(FILE* filep, char* base_name) | ||
|  | { | ||
|  | 	fprintf(filep, "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;i<g_opcode_output_table_length;i++) | ||
|  | 		write_table_entry(filep, g_opcode_output_table+i); | ||
|  | } | ||
|  | 
 | ||
|  | /* Write an entry in the opcode handler table */ | ||
|  | void write_table_entry(FILE* filep, opcode_struct* op) | ||
|  | { | ||
|  | 	int i; | ||
|  | 
 | ||
|  | 	fprintf(filep, "\t{%-28s, 0x%04x, 0x%04x, {", | ||
|  | 		op->name, op->op_mask, op->op_match); | ||
|  | 
 | ||
|  | 	for(i=0;i<NUM_CPUS;i++) | ||
|  | 	{ | ||
|  | 		fprintf(filep, "%3d", op->cycles[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;i<NUM_CPUS;i++) | ||
|  | 		dst->cycles[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); | ||
|  | 	write_prototype(g_prototype_file, str); | ||
|  | 	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(void) | ||
|  | { | ||
|  | 	FILE* input_file = g_input_file; | ||
|  | 	FILE* output_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)); | ||
|  | 
 | ||
|  | 
 | ||
|  | 	output_file = g_ops_ac_file; | ||
|  | 
 | ||
|  | 	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); | ||
|  | 
 | ||
|  |         /* Change output files if we pass 'c' or 'n' */ | ||
|  | 		if(output_file == g_ops_ac_file && oper_name[0] > 'c') | ||
|  | 			output_file = g_ops_dm_file; | ||
|  | 		else if(output_file == g_ops_dm_file && oper_name[0] > 'm') | ||
|  | 			output_file = g_ops_nz_file; | ||
|  | 
 | ||
|  | 		replace->length = 0; | ||
|  | 
 | ||
|  | 		/* Generate opcode variants */ | ||
|  | 		if(strcmp(opinfo->name, "bcc") == 0 || strcmp(opinfo->name, "scc") == 0) | ||
|  | 			generate_opcode_cc_variants(output_file, body, replace, opinfo, 1); | ||
|  | 		else if(strcmp(opinfo->name, "dbcc") == 0) | ||
|  | 			generate_opcode_cc_variants(output_file, body, replace, opinfo, 2); | ||
|  | 		else if(strcmp(opinfo->name, "trapcc") == 0) | ||
|  | 			generate_opcode_cc_variants(output_file, body, replace, opinfo, 4); | ||
|  | 		else | ||
|  | 			generate_opcode_ea_variants(output_file, 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("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("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;i<NUM_CPUS;i++) | ||
|  | 		{ | ||
|  | 			op->cpu_mode[i] = *ptr++; | ||
|  | 			ptr += skip_spaces(ptr); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/* Allowed CPUs for this instruction */ | ||
|  | 		for(i=0;i<NUM_CPUS;i++) | ||
|  | 		{ | ||
|  | 			ptr += skip_spaces(ptr); | ||
|  | 			if(*ptr == UNSPECIFIED_CH) | ||
|  | 			{ | ||
|  | 				op->cpus[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]; | ||
|  | 	/* 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_footer_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\t\tMusashi v%s 68000, 68008, 68010, 68EC020, 68020 emulator\n", g_version); | ||
|  | 	printf("\t\tCopyright 1998-2000 Karl Stenerud (karl@mame.net)\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 !(defined(__DECC) && defined(VMS))
 | ||
|  |         if(output_path[strlen(output_path)-1] != '/') | ||
|  | 			strcat(output_path, "/"); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 		if(argc > 2) | ||
|  | 			strcpy(g_input_filename, argv[2]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | #if defined(__DECC) && defined(VMS)
 | ||
|  | 
 | ||
|  | 	/* Open the files we need */ | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_PROTOTYPE); | ||
|  | 	if((g_prototype_file = fopen(filename, "w")) == 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, "w")) == NULL) | ||
|  | 		perror_exit("Unable to create table file (%s)\n", filename); | ||
|  | 
 | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_OPS_AC); | ||
|  | 	if((g_ops_ac_file = fopen(filename, "w")) == NULL) | ||
|  | 		perror_exit("Unable to create ops ac file (%s)\n", filename); | ||
|  | 
 | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_OPS_DM); | ||
|  | 	if((g_ops_dm_file = fopen(filename, "w")) == NULL) | ||
|  | 		perror_exit("Unable to create ops dm file (%s)\n", filename); | ||
|  | 
 | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_OPS_NZ); | ||
|  | 	if((g_ops_nz_file = fopen(filename, "w")) == NULL) | ||
|  | 		perror_exit("Unable to create ops nz file (%s)\n", filename); | ||
|  | 
 | ||
|  | 	if((g_input_file=fopen(g_input_filename, "r")) == NULL) | ||
|  | 		perror_exit("can't open %s for input", g_input_filename); | ||
|  | 
 | ||
|  | #else
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 	/* 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); | ||
|  | 
 | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_OPS_AC); | ||
|  | 	if((g_ops_ac_file = fopen(filename, "wt")) == NULL) | ||
|  | 		perror_exit("Unable to create ops ac file (%s)\n", filename); | ||
|  | 
 | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_OPS_DM); | ||
|  | 	if((g_ops_dm_file = fopen(filename, "wt")) == NULL) | ||
|  | 		perror_exit("Unable to create ops dm file (%s)\n", filename); | ||
|  | 
 | ||
|  | 	sprintf(filename, "%s%s", output_path, FILENAME_OPS_NZ); | ||
|  | 	if((g_ops_nz_file = fopen(filename, "wt")) == NULL) | ||
|  | 		perror_exit("Unable to create ops nz 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); | ||
|  | 
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	/* 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(temp_insert); | ||
|  | 			fprintf(g_table_file, "%s", temp_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(temp_insert); | ||
|  | 			fprintf(g_ops_ac_file, "%s\n\n", temp_insert); | ||
|  | 			fprintf(g_ops_dm_file, "%s\n\n", temp_insert); | ||
|  | 			fprintf(g_ops_nz_file, "%s\n\n", temp_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"); | ||
|  | 
 | ||
|  | 			process_opcode_handlers(); | ||
|  | 
 | ||
|  | 			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"); | ||
|  | 
 | ||
|  | 			print_opcode_output_table(g_table_file); | ||
|  | 
 | ||
|  | 			fprintf(g_prototype_file, "%s\n\n", prototype_footer_insert); | ||
|  | 			fprintf(g_table_file, "%s\n\n", table_footer_insert); | ||
|  | 			fprintf(g_ops_ac_file, "%s\n\n", ophandler_footer_insert); | ||
|  | 			fprintf(g_ops_dm_file, "%s\n\n", ophandler_footer_insert); | ||
|  | 			fprintf(g_ops_nz_file, "%s\n\n", ophandler_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_ops_ac_file); | ||
|  | 	fclose(g_ops_dm_file); | ||
|  | 	fclose(g_ops_nz_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 ============================= */ | ||
|  | /* ======================================================================== */ |