mirror of
https://github.com/holub/mame
synced 2025-05-28 16:43:04 +03:00

- Rewritten to use SoftFloat instead of unportable native FP math - Support added for Motorola 96-bit extended floats - More addressing modes and conditions supported The taitojc games function identically to the old implementation (I stepped through the main matrix multiply in dendeg and the registers never diverged) and speed on Core 2 Duo is a wash - sometimes softfloat's faster, sometimes not, but the difference on -str 90 never exceeded 1%.
1434 lines
44 KiB
C
1434 lines
44 KiB
C
/* ======================================================================== */
|
|
/* ========================= LICENSING & COPYRIGHT ======================== */
|
|
/* ======================================================================== */
|
|
/*
|
|
* MUSASHI
|
|
* Version 4.60
|
|
*
|
|
* A portable Motorola M680x0 processor emulation engine.
|
|
* Copyright Karl Stenerud. All rights reserved.
|
|
* FPU and MMU by R. Belmont.
|
|
*
|
|
* 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 licensing terms must be negotiated with the author
|
|
* (Karl Stenerud).
|
|
*
|
|
* The latest version of this code can be obtained at:
|
|
* http://kstenerud.cjb.net or http://mamedev.org/
|
|
*/
|
|
|
|
/*
|
|
* Modified For OpenVMS By: Robert Alan Byer
|
|
* byer@mail.ourservers.net
|
|
*
|
|
* 68030 and PMMU by R. Belmont
|
|
* 68040 and FPU by Ville Linde
|
|
*/
|
|
|
|
|
|
/* ======================================================================== */
|
|
/* ============================ 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[] = "4.60";
|
|
|
|
/* ======================================================================== */
|
|
/* =============================== 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_030,
|
|
CPU_TYPE_040,
|
|
NUM_CPUS
|
|
};
|
|
|
|
#define UNSPECIFIED "."
|
|
#define UNSPECIFIED_CH '.'
|
|
|
|
#define HAS_NO_EA_MODE(A) (strcmp(A, "..........") == 0)
|
|
#define HAS_EA_AI(A) ((A)[0] == 'A')
|
|
#define HAS_EA_PI(A) ((A)[1] == '+')
|
|
#define HAS_EA_PD(A) ((A)[2] == '-')
|
|
#define HAS_EA_DI(A) ((A)[3] == 'D')
|
|
#define HAS_EA_IX(A) ((A)[4] == 'X')
|
|
#define HAS_EA_AW(A) ((A)[5] == 'W')
|
|
#define HAS_EA_AL(A) ((A)[6] == 'L')
|
|
#define HAS_EA_PCDI(A) ((A)[7] == 'd')
|
|
#define HAS_EA_PCIX(A) ((A)[8] == 'x')
|
|
#define HAS_EA_I(A) ((A)[9] == 'I')
|
|
|
|
enum
|
|
{
|
|
EA_MODE_NONE, /* No special addressing mode */
|
|
EA_MODE_AI, /* Address register indirect */
|
|
EA_MODE_PI, /* Address register indirect with postincrement */
|
|
EA_MODE_PI7, /* Address register 7 indirect with postincrement */
|
|
EA_MODE_PD, /* Address register indirect with predecrement */
|
|
EA_MODE_PD7, /* Address register 7 indirect with predecrement */
|
|
EA_MODE_DI, /* Address register indirect with displacement */
|
|
EA_MODE_IX, /* Address register indirect with index */
|
|
EA_MODE_AW, /* Absolute word */
|
|
EA_MODE_AL, /* Absolute long */
|
|
EA_MODE_PCDI, /* Program counter indirect with displacement */
|
|
EA_MODE_PCIX, /* Program counter indirect with index */
|
|
EA_MODE_I /* Immediate */
|
|
};
|
|
|
|
|
|
/* Everything we need to know about an opcode */
|
|
typedef struct
|
|
{
|
|
char name[MAX_NAME_LENGTH]; /* opcode handler name */
|
|
unsigned char size; /* Size of operation */
|
|
char spec_proc[MAX_SPEC_PROC_LENGTH]; /* Special processing mode */
|
|
char spec_ea[MAX_SPEC_EA_LENGTH]; /* Specified effective addressing mode */
|
|
unsigned char bits; /* Number of significant bits (used for sorting the table) */
|
|
unsigned short op_mask; /* Mask to apply for matching an opcode to a handler */
|
|
unsigned short op_match; /* Value to match after masking */
|
|
char ea_allowed[EA_ALLOWED_LENGTH]; /* Effective addressing modes allowed */
|
|
char cpu_mode[NUM_CPUS]; /* User or supervisor mode */
|
|
char cpus[NUM_CPUS+1]; /* Allowed CPUs */
|
|
unsigned char cycles[NUM_CPUS]; /* cycles for 000, 010, 020, 030, 040 */
|
|
} opcode_struct;
|
|
|
|
|
|
/* All modifications necessary for a specific EA mode of an instruction */
|
|
typedef struct
|
|
{
|
|
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 */
|
|
static void error_exit(const char* fmt, ...);
|
|
static void perror_exit(const char* fmt, ...);
|
|
static int check_strsncpy(char* dst, char* src, int maxlength);
|
|
static int check_atoi(char* str, int *result);
|
|
static int skip_spaces(char* str);
|
|
static int num_bits(int value);
|
|
//int atoh(char* buff);
|
|
static int fgetline(char* buff, int nchars, FILE* file);
|
|
static int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type);
|
|
static opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea);
|
|
//opcode_struct* find_illegal_opcode(void);
|
|
static int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea);
|
|
static void add_replace_string(replace_struct* replace, const char* search_str, const char* replace_str);
|
|
static void write_body(FILE* filep, body_struct* body, replace_struct* replace);
|
|
static void get_base_name(char* base_name, opcode_struct* op);
|
|
static void write_function_name(FILE* filep, char* base_name);
|
|
static 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);
|
|
static void print_opcode_output_table(FILE* filep);
|
|
static void write_table_entry(FILE* filep, opcode_struct* op);
|
|
static void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode);
|
|
static void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode);
|
|
static void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op);
|
|
static void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset);
|
|
static void process_opcode_handlers(FILE* filep);
|
|
static void populate_table(void);
|
|
static void read_insert(char* insert);
|
|
|
|
|
|
|
|
/* ======================================================================== */
|
|
/* ================================= DATA ================================= */
|
|
/* ======================================================================== */
|
|
|
|
/* Name of the input file */
|
|
static char g_input_filename[M68K_MAX_PATH];
|
|
|
|
/* File handles */
|
|
static FILE* g_input_file = NULL;
|
|
static FILE* g_prototype_file = NULL;
|
|
static FILE* g_table_file = NULL;
|
|
|
|
static int g_num_functions = 0; /* Number of functions processed */
|
|
static int g_num_primitives = 0; /* Number of function primitives read */
|
|
static int g_line_number = 1; /* Current line number */
|
|
|
|
/* Opcode handler table */
|
|
static opcode_struct g_opcode_input_table[MAX_OPCODE_INPUT_TABLE_LENGTH];
|
|
|
|
static opcode_struct g_opcode_output_table[MAX_OPCODE_OUTPUT_TABLE_LENGTH];
|
|
static int g_opcode_output_table_length = 0;
|
|
|
|
static const ea_info_struct g_ea_info_table[13] =
|
|
{/* fname ea mask match */
|
|
{"", "", 0x00, 0x00}, /* EA_MODE_NONE */
|
|
{"ai", "AY_AI", 0x38, 0x10}, /* EA_MODE_AI */
|
|
{"pi", "AY_PI", 0x38, 0x18}, /* EA_MODE_PI */
|
|
{"pi7", "A7_PI", 0x3f, 0x1f}, /* EA_MODE_PI7 */
|
|
{"pd", "AY_PD", 0x38, 0x20}, /* EA_MODE_PD */
|
|
{"pd7", "A7_PD", 0x3f, 0x27}, /* EA_MODE_PD7 */
|
|
{"di", "AY_DI", 0x38, 0x28}, /* EA_MODE_DI */
|
|
{"ix", "AY_IX", 0x38, 0x30}, /* EA_MODE_IX */
|
|
{"aw", "AW", 0x3f, 0x38}, /* EA_MODE_AW */
|
|
{"al", "AL", 0x3f, 0x39}, /* EA_MODE_AL */
|
|
{"pcdi", "PCDI", 0x3f, 0x3a}, /* EA_MODE_PCDI */
|
|
{"pcix", "PCIX", 0x3f, 0x3b}, /* EA_MODE_PCIX */
|
|
{"i", "I", 0x3f, 0x3c}, /* EA_MODE_I */
|
|
};
|
|
|
|
|
|
static const char *const g_cc_table[16][2] =
|
|
{
|
|
{ "t", "T"}, /* 0000 */
|
|
{ "f", "F"}, /* 0001 */
|
|
{"hi", "HI"}, /* 0010 */
|
|
{"ls", "LS"}, /* 0011 */
|
|
{"cc", "CC"}, /* 0100 */
|
|
{"cs", "CS"}, /* 0101 */
|
|
{"ne", "NE"}, /* 0110 */
|
|
{"eq", "EQ"}, /* 0111 */
|
|
{"vc", "VC"}, /* 1000 */
|
|
{"vs", "VS"}, /* 1001 */
|
|
{"pl", "PL"}, /* 1010 */
|
|
{"mi", "MI"}, /* 1011 */
|
|
{"ge", "GE"}, /* 1100 */
|
|
{"lt", "LT"}, /* 1101 */
|
|
{"gt", "GT"}, /* 1110 */
|
|
{"le", "LE"}, /* 1111 */
|
|
};
|
|
|
|
/* size to index translator (0 -> 0, 8 and 16 -> 1, 32 -> 2) */
|
|
static const int g_size_select_table[33] =
|
|
{
|
|
0, /* unsized */
|
|
0, 0, 0, 0, 0, 0, 0, 1, /* 8 */
|
|
0, 0, 0, 0, 0, 0, 0, 1, /* 16 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 /* 32 */
|
|
};
|
|
|
|
/* Extra cycles required for certain EA modes */
|
|
/* TODO: correct timings for 030, 040 */
|
|
static const int g_ea_cycle_table[13][NUM_CPUS][3] =
|
|
{/* 000 010 020 030 040 */
|
|
{{ 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}}, /* EA_MODE_NONE */
|
|
{{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_AI */
|
|
{{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_PI */
|
|
{{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_PI7 */
|
|
{{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_PD */
|
|
{{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_PD7 */
|
|
{{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_DI */
|
|
{{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}, { 0, 7, 7}, { 0, 7, 7}}, /* EA_MODE_IX */
|
|
{{ 0, 8, 12}, { 0, 8, 12}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_AW */
|
|
{{ 0, 12, 16}, { 0, 12, 16}, { 0, 4, 4}, { 0, 4, 4}, { 0, 4, 4}}, /* EA_MODE_AL */
|
|
{{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}, { 0, 5, 5}, { 0, 5, 5}}, /* EA_MODE_PCDI */
|
|
{{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}, { 0, 7, 7}, { 0, 7, 7}}, /* EA_MODE_PCIX */
|
|
{{ 0, 4, 8}, { 0, 4, 8}, { 0, 2, 4}, { 0, 2, 4}, { 0, 2, 4}}, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for JMP instruction (000, 010) */
|
|
static const int g_jmp_cycle_table[13] =
|
|
{
|
|
0, /* EA_MODE_NONE */
|
|
4, /* EA_MODE_AI */
|
|
0, /* EA_MODE_PI */
|
|
0, /* EA_MODE_PI7 */
|
|
0, /* EA_MODE_PD */
|
|
0, /* EA_MODE_PD7 */
|
|
6, /* EA_MODE_DI */
|
|
10, /* EA_MODE_IX */
|
|
6, /* EA_MODE_AW */
|
|
8, /* EA_MODE_AL */
|
|
6, /* EA_MODE_PCDI */
|
|
10, /* EA_MODE_PCIX */
|
|
0, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for JSR instruction (000, 010) */
|
|
static const int g_jsr_cycle_table[13] =
|
|
{
|
|
0, /* EA_MODE_NONE */
|
|
4, /* EA_MODE_AI */
|
|
0, /* EA_MODE_PI */
|
|
0, /* EA_MODE_PI7 */
|
|
0, /* EA_MODE_PD */
|
|
0, /* EA_MODE_PD7 */
|
|
6, /* EA_MODE_DI */
|
|
10, /* EA_MODE_IX */
|
|
6, /* EA_MODE_AW */
|
|
8, /* EA_MODE_AL */
|
|
6, /* EA_MODE_PCDI */
|
|
10, /* EA_MODE_PCIX */
|
|
0, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for LEA instruction (000, 010) */
|
|
static const int g_lea_cycle_table[13] =
|
|
{
|
|
0, /* EA_MODE_NONE */
|
|
4, /* EA_MODE_AI */
|
|
0, /* EA_MODE_PI */
|
|
0, /* EA_MODE_PI7 */
|
|
0, /* EA_MODE_PD */
|
|
0, /* EA_MODE_PD7 */
|
|
8, /* EA_MODE_DI */
|
|
12, /* EA_MODE_IX */
|
|
8, /* EA_MODE_AW */
|
|
12, /* EA_MODE_AL */
|
|
8, /* EA_MODE_PCDI */
|
|
12, /* EA_MODE_PCIX */
|
|
0, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for PEA instruction (000, 010) */
|
|
static const int g_pea_cycle_table[13] =
|
|
{
|
|
0, /* EA_MODE_NONE */
|
|
6, /* EA_MODE_AI */
|
|
0, /* EA_MODE_PI */
|
|
0, /* EA_MODE_PI7 */
|
|
0, /* EA_MODE_PD */
|
|
0, /* EA_MODE_PD7 */
|
|
10, /* EA_MODE_DI */
|
|
14, /* EA_MODE_IX */
|
|
10, /* EA_MODE_AW */
|
|
14, /* EA_MODE_AL */
|
|
10, /* EA_MODE_PCDI */
|
|
14, /* EA_MODE_PCIX */
|
|
0, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for MOVEM instruction (000, 010) */
|
|
static const int g_movem_cycle_table[13] =
|
|
{
|
|
0, /* EA_MODE_NONE */
|
|
0, /* EA_MODE_AI */
|
|
0, /* EA_MODE_PI */
|
|
0, /* EA_MODE_PI7 */
|
|
0, /* EA_MODE_PD */
|
|
0, /* EA_MODE_PD7 */
|
|
4, /* EA_MODE_DI */
|
|
6, /* EA_MODE_IX */
|
|
4, /* EA_MODE_AW */
|
|
8, /* EA_MODE_AL */
|
|
0, /* EA_MODE_PCDI */
|
|
0, /* EA_MODE_PCIX */
|
|
0, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for MOVES instruction (010) */
|
|
static const int g_moves_cycle_table[13][3] =
|
|
{
|
|
{ 0, 0, 0}, /* EA_MODE_NONE */
|
|
{ 0, 4, 6}, /* EA_MODE_AI */
|
|
{ 0, 4, 6}, /* EA_MODE_PI */
|
|
{ 0, 4, 6}, /* EA_MODE_PI7 */
|
|
{ 0, 6, 12}, /* EA_MODE_PD */
|
|
{ 0, 6, 12}, /* EA_MODE_PD7 */
|
|
{ 0, 12, 16}, /* EA_MODE_DI */
|
|
{ 0, 16, 20}, /* EA_MODE_IX */
|
|
{ 0, 12, 16}, /* EA_MODE_AW */
|
|
{ 0, 16, 20}, /* EA_MODE_AL */
|
|
{ 0, 0, 0}, /* EA_MODE_PCDI */
|
|
{ 0, 0, 0}, /* EA_MODE_PCIX */
|
|
{ 0, 0, 0}, /* EA_MODE_I */
|
|
};
|
|
|
|
/* Extra cycles for CLR instruction (010) */
|
|
static const int g_clr_cycle_table[13][3] =
|
|
{
|
|
{ 0, 0, 0}, /* EA_MODE_NONE */
|
|
{ 0, 4, 6}, /* EA_MODE_AI */
|
|
{ 0, 4, 6}, /* EA_MODE_PI */
|
|
{ 0, 4, 6}, /* EA_MODE_PI7 */
|
|
{ 0, 6, 8}, /* EA_MODE_PD */
|
|
{ 0, 6, 8}, /* EA_MODE_PD7 */
|
|
{ 0, 8, 10}, /* EA_MODE_DI */
|
|
{ 0, 10, 14}, /* EA_MODE_IX */
|
|
{ 0, 8, 10}, /* EA_MODE_AW */
|
|
{ 0, 10, 14}, /* EA_MODE_AL */
|
|
{ 0, 0, 0}, /* EA_MODE_PCDI */
|
|
{ 0, 0, 0}, /* EA_MODE_PCIX */
|
|
{ 0, 0, 0}, /* EA_MODE_I */
|
|
};
|
|
|
|
|
|
|
|
/* ======================================================================== */
|
|
/* =========================== UTILITY FUNCTIONS ========================== */
|
|
/* ======================================================================== */
|
|
|
|
/* Print an error message and exit with status error */
|
|
static 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 */
|
|
static 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 */
|
|
static 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 */
|
|
static 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 */
|
|
static 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 */
|
|
static int skip_spaces(char* str)
|
|
{
|
|
char* p = str;
|
|
|
|
while(*p == ' ')
|
|
p++;
|
|
|
|
return p - str;
|
|
}
|
|
|
|
/* Count the number of set bits in a value */
|
|
static 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;
|
|
}
|
|
|
|
#ifdef UNUSED_FUNCTION
|
|
/* 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;
|
|
}
|
|
#endif
|
|
|
|
/* Get a line of text from a file, discarding any end-of-line characters */
|
|
static 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 */
|
|
static 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 */
|
|
static 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;
|
|
}
|
|
|
|
#ifdef UNUSED_FUNCTION
|
|
/* 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;
|
|
}
|
|
#endif
|
|
|
|
/* Parse an opcode handler name */
|
|
static 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 */
|
|
static 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 */
|
|
static 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 [%s]", output);
|
|
}
|
|
fprintf(filep, "%s\n", output);
|
|
}
|
|
fprintf(filep, "\n\n");
|
|
}
|
|
|
|
/* Generate a base function name from an opcode struct */
|
|
static void get_base_name(char* base_name, opcode_struct* op)
|
|
{
|
|
sprintf(base_name, "m68k_op_%s", op->name);
|
|
if(op->size > 0)
|
|
sprintf(base_name+strlen(base_name), "_%d", op->size);
|
|
if(strcmp(op->spec_proc, UNSPECIFIED) != 0)
|
|
sprintf(base_name+strlen(base_name), "_%s", op->spec_proc);
|
|
if(strcmp(op->spec_ea, UNSPECIFIED) != 0)
|
|
sprintf(base_name+strlen(base_name), "_%s", op->spec_ea);
|
|
}
|
|
|
|
/* Write the name of an opcode handler function */
|
|
static void write_function_name(FILE* filep, char* base_name)
|
|
{
|
|
fprintf(filep, "static void %s(m68ki_cpu_core *m68k)\n", base_name);
|
|
}
|
|
|
|
static 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 = (const opcode_struct *)aptr, *b = (const opcode_struct *)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;
|
|
}
|
|
|
|
static 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 */
|
|
static 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 */
|
|
static 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 */
|
|
static 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 = (opcode_struct *)malloc(sizeof(opcode_struct));
|
|
|
|
/* Set the opcode structure and write the tables, prototypes, etc */
|
|
set_opcode_struct(opinfo, op, ea_mode);
|
|
get_base_name(str, op);
|
|
add_opcode_output_table_entry(op, str);
|
|
write_function_name(filep, str);
|
|
|
|
/* Add any replace strings needed */
|
|
if(ea_mode != EA_MODE_NONE)
|
|
{
|
|
sprintf(str, "EA_%s_8(m68k)", g_ea_info_table[ea_mode].ea_add);
|
|
add_replace_string(replace, ID_OPHANDLER_EA_AY_8, str);
|
|
sprintf(str, "EA_%s_16(m68k)", g_ea_info_table[ea_mode].ea_add);
|
|
add_replace_string(replace, ID_OPHANDLER_EA_AY_16, str);
|
|
sprintf(str, "EA_%s_32(m68k)", g_ea_info_table[ea_mode].ea_add);
|
|
add_replace_string(replace, ID_OPHANDLER_EA_AY_32, str);
|
|
sprintf(str, "OPER_%s_8(m68k)", g_ea_info_table[ea_mode].ea_add);
|
|
add_replace_string(replace, ID_OPHANDLER_OPER_AY_8, str);
|
|
sprintf(str, "OPER_%s_16(m68k)", g_ea_info_table[ea_mode].ea_add);
|
|
add_replace_string(replace, ID_OPHANDLER_OPER_AY_16, str);
|
|
sprintf(str, "OPER_%s_32(m68k)", 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 */
|
|
static 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 */
|
|
static 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 = (opcode_struct *)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(m68k)", g_cc_table[i][1]);
|
|
sprintf(replnot, "COND_NOT_%s(m68k)", 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 */
|
|
static 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 = 0;
|
|
char oper_spec_proc[MAX_LINE_LENGTH+1];
|
|
char oper_spec_ea[MAX_LINE_LENGTH+1];
|
|
opcode_struct* opinfo;
|
|
replace_struct* replace = (replace_struct*)malloc(sizeof(replace_struct));
|
|
body_struct* body = (body_struct*)malloc(sizeof(body_struct));
|
|
|
|
for(;;)
|
|
{
|
|
/* Find the first line of the function */
|
|
func_name[0] = 0;
|
|
while(strstr(func_name, ID_OPHANDLER_NAME) == NULL)
|
|
{
|
|
if(strcmp(func_name, ID_INPUT_SEPARATOR) == 0)
|
|
{
|
|
free(replace);
|
|
free(body);
|
|
return; /* all done */
|
|
}
|
|
if(fgetline(func_name, MAX_LINE_LENGTH, input_file) < 0)
|
|
error_exit("Premature end of file when getting function name");
|
|
}
|
|
/* Get the rest of the function */
|
|
for(body->length=0;;body->length++)
|
|
{
|
|
if(body->length > MAX_BODY_LENGTH)
|
|
error_exit("Function too long");
|
|
|
|
if(fgetline(body->body[body->length], MAX_LINE_LENGTH, input_file) < 0)
|
|
error_exit("Premature end of file when getting function body");
|
|
|
|
if(body->body[body->length][0] == '}')
|
|
{
|
|
body->length++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_num_primitives++;
|
|
|
|
/* Extract the function name information */
|
|
if(!extract_opcode_info(func_name, oper_name, &oper_size, oper_spec_proc, oper_spec_ea))
|
|
error_exit("Invalid " ID_OPHANDLER_NAME " format");
|
|
|
|
/* Find the corresponding table entry */
|
|
opinfo = find_opcode(oper_name, oper_size, oper_spec_proc, oper_spec_ea);
|
|
if(opinfo == NULL)
|
|
error_exit("Unable to find matching table entry for %s", func_name);
|
|
|
|
replace->length = 0;
|
|
|
|
/* Generate opcode variants */
|
|
if(strcmp(opinfo->name, "bcc") == 0 || strcmp(opinfo->name, "scc") == 0)
|
|
generate_opcode_cc_variants(filep, body, replace, opinfo, 1);
|
|
else if(strcmp(opinfo->name, "dbcc") == 0)
|
|
generate_opcode_cc_variants(filep, body, replace, opinfo, 2);
|
|
else if(strcmp(opinfo->name, "trapcc") == 0)
|
|
generate_opcode_cc_variants(filep, body, replace, opinfo, 4);
|
|
else
|
|
generate_opcode_ea_variants(filep, body, replace, opinfo);
|
|
}
|
|
|
|
free(replace);
|
|
free(body);
|
|
}
|
|
|
|
|
|
/* Populate the opcode handler table from the input file */
|
|
static void populate_table(void)
|
|
{
|
|
char* ptr;
|
|
char bitpattern[17];
|
|
opcode_struct* op;
|
|
char buff[MAX_LINE_LENGTH];
|
|
int i;
|
|
int temp;
|
|
|
|
buff[0] = 0;
|
|
|
|
/* Find the start of the table */
|
|
while(strcmp(buff, ID_TABLE_START) != 0)
|
|
if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0)
|
|
error_exit("(table_start) Premature EOF while reading table");
|
|
|
|
/* Process the entire table */
|
|
for(op = g_opcode_input_table;;op++)
|
|
{
|
|
if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0)
|
|
error_exit("(inline) Premature EOF while reading table");
|
|
if(strlen(buff) == 0)
|
|
continue;
|
|
/* We finish when we find an input separator */
|
|
if(strcmp(buff, ID_INPUT_SEPARATOR) == 0)
|
|
break;
|
|
|
|
/* Extract the info from the table */
|
|
ptr = buff;
|
|
|
|
/* Name */
|
|
ptr += skip_spaces(ptr);
|
|
ptr += check_strsncpy(op->name, ptr, MAX_NAME_LENGTH);
|
|
|
|
/* Size */
|
|
ptr += skip_spaces(ptr);
|
|
ptr += check_atoi(ptr, &temp);
|
|
op->size = (unsigned char)temp;
|
|
|
|
/* Special processing */
|
|
ptr += skip_spaces(ptr);
|
|
ptr += check_strsncpy(op->spec_proc, ptr, MAX_SPEC_PROC_LENGTH);
|
|
|
|
/* Specified EA Mode */
|
|
ptr += skip_spaces(ptr);
|
|
ptr += check_strsncpy(op->spec_ea, ptr, MAX_SPEC_EA_LENGTH);
|
|
|
|
/* Bit Pattern (more processing later) */
|
|
ptr += skip_spaces(ptr);
|
|
ptr += check_strsncpy(bitpattern, ptr, 17);
|
|
|
|
/* Allowed Addressing Mode List */
|
|
ptr += skip_spaces(ptr);
|
|
ptr += check_strsncpy(op->ea_allowed, ptr, EA_ALLOWED_LENGTH);
|
|
|
|
/* CPU operating mode (U = user or supervisor, S = supervisor only */
|
|
ptr += skip_spaces(ptr);
|
|
for(i=0;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 */
|
|
static 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, 68EC030, 68030, 68EC040, 68040 emulator\n", g_version);
|
|
printf("\tCopyright Karl Stenerud\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
|
|
}
|
|
|
|
strcpy(g_input_filename, (argc > 2) ? argv[2] : FILENAME_INPUT);
|
|
|
|
#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 ============================= */
|
|
/* ======================================================================== */
|