mirror of
				https://github.com/RaySollium99/picodrive.git
				synced 2025-10-27 00:29:39 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1438 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1438 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ======================================================================== */
 | |
| /* ========================= LICENSING & COPYRIGHT ======================== */
 | |
| /* ======================================================================== */
 | |
| /*
 | |
|  *                                  MUSASHI
 | |
|  *                                Version 3.31
 | |
|  *
 | |
|  * A portable Motorola M680x0 processor emulation engine.
 | |
|  * Copyright 1998-2007 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
 | |
|  */
 | |
| 
 | |
| 
 | |
| static const char* g_version = "3.31";
 | |
| 
 | |
| /* ======================================================================== */
 | |
| /* =============================== 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"
 | |
| 
 | |
| 
 | |
| /* 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_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 */
 | |
| } 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(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;
 | |
| 
 | |
| 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 */
 | |
| /* TODO: correct timings for 040 */
 | |
| int g_ea_cycle_table[13][NUM_CPUS][3] =
 | |
| {/*       000           010           020           040  */
 | |
| 	{{ 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}}, /* EA_MODE_AI   */
 | |
| 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  4,  4}, { 0,  4,  4}}, /* EA_MODE_PI   */
 | |
| 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  4,  4}, { 0,  4,  4}}, /* EA_MODE_PI7  */
 | |
| 	{{ 0,  6, 10}, { 0,  6, 10}, { 0,  5,  5}, { 0,  5,  5}}, /* EA_MODE_PD   */
 | |
| 	{{ 0,  6, 10}, { 0,  6, 10}, { 0,  5,  5}, { 0,  5,  5}}, /* EA_MODE_PD7  */
 | |
| 	{{ 0,  8, 12}, { 0,  8, 12}, { 0,  5,  5}, { 0,  5,  5}}, /* EA_MODE_DI   */
 | |
| 	{{ 0, 10, 14}, { 0, 10, 14}, { 0,  7,  7}, { 0,  7,  7}}, /* EA_MODE_IX   */
 | |
| 	{{ 0,  8, 12}, { 0,  8, 12}, { 0,  4,  4}, { 0,  4,  4}}, /* EA_MODE_AW   */
 | |
| 	{{ 0, 12, 16}, { 0, 12, 16}, { 0,  4,  4}, { 0,  4,  4}}, /* EA_MODE_AL   */
 | |
| 	{{ 0,  8, 12}, { 0,  8, 12}, { 0,  5,  5}, { 0,  5,  5}}, /* EA_MODE_PCDI */
 | |
| 	{{ 0, 10, 14}, { 0, 10, 14}, { 0,  7,  7}, { 0,  7,  7}}, /* EA_MODE_PCIX */
 | |
| 	{{ 0,  4,  8}, { 0,  4,  8}, { 0,  2,  4}, { 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_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_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(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);
 | |
| 
 | |
| #if 1 /* PD hack: 000 only */
 | |
| 		if (opinfo->cpus[0] == UNSPECIFIED_CH)
 | |
| 			continue;
 | |
| #endif
 | |
| 
 | |
| 		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("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_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, 68040 emulator\n", g_version);
 | |
| 	printf("\tCopyright 1998-2007 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);
 | |
| 
 | |
| 	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);
 | |
| 
 | |
| 	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(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 ============================= */
 | |
| /* ======================================================================== */
 | 
