nkeynes@359: /** nkeynes@420: * $Id: gendec.c,v 1.2 2007-10-06 08:48:47 nkeynes Exp $ nkeynes@359: * nkeynes@359: * Parse the instruction and action files and generate an appropriate nkeynes@359: * instruction decoder. nkeynes@359: * nkeynes@359: * Copyright (c) 2005 Nathan Keynes. nkeynes@359: * nkeynes@359: * This program is free software; you can redistribute it and/or modify nkeynes@359: * it under the terms of the GNU General Public License as published by nkeynes@359: * the Free Software Foundation; either version 2 of the License, or nkeynes@359: * (at your option) any later version. nkeynes@359: * nkeynes@359: * This program is distributed in the hope that it will be useful, nkeynes@359: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@359: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@359: * GNU General Public License for more details. nkeynes@359: */ nkeynes@359: nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include nkeynes@359: #include "tools/gendec.h" nkeynes@359: nkeynes@359: #define DEFAULT_OUT_EXT ".c" nkeynes@359: nkeynes@359: const char *ins_filename = NULL; nkeynes@359: const char *act_filename = NULL; nkeynes@359: const char *out_filename = NULL; nkeynes@359: nkeynes@359: #define GEN_SOURCE 1 nkeynes@359: #define GEN_TEMPLATE 2 nkeynes@359: nkeynes@359: FILE *ins_file, *act_file, *out_file; nkeynes@359: nkeynes@359: char *option_list = "tmho:"; nkeynes@359: int gen_mode = GEN_SOURCE; nkeynes@359: struct option longopts[1] = { { NULL, 0, 0, 0 } }; nkeynes@359: nkeynes@359: void usage() { nkeynes@359: printf( "gendec [ -o ]\n" ); nkeynes@359: } nkeynes@359: nkeynes@359: /** nkeynes@359: * Find a mask that can be used to split up the given rules nkeynes@359: */ nkeynes@359: uint32_t find_mask( struct ruleset *rules, int ruleidx[], int rule_count, nkeynes@359: uint32_t input_mask ) nkeynes@359: { nkeynes@359: int i; nkeynes@359: uint32_t mask = rules->rules[ruleidx[0]]->mask; nkeynes@359: nkeynes@359: for( i=1; irules[ruleidx[i]]->mask; nkeynes@359: } nkeynes@359: nkeynes@359: assert( (mask & input_mask) == input_mask ); /* input_mask should always be included in the mask */ nkeynes@359: nkeynes@359: return mask & (~input_mask); /* but we don't want to see the input mask again */ nkeynes@359: } nkeynes@359: nkeynes@359: int get_option_count_for_mask( uint32_t mask ) { nkeynes@359: int count = 0; nkeynes@359: nkeynes@359: while( mask ) { nkeynes@359: if( mask&1 ) nkeynes@359: count++; nkeynes@359: mask >>= 1; nkeynes@359: } nkeynes@359: return 1<>= 1; nkeynes@359: } nkeynes@359: return shift; nkeynes@359: } nkeynes@359: nkeynes@359: void get_option_values_for_mask( uint32_t *options, nkeynes@359: uint32_t mask ) nkeynes@359: { nkeynes@359: /* This could be a lot smarter. But it's not */ nkeynes@359: int i; nkeynes@359: *options = 0; nkeynes@359: for( i=1; i<=mask; i++ ) { nkeynes@359: if( (i & mask) > *options ) { nkeynes@359: options++; nkeynes@359: *options = (i&mask); nkeynes@359: } nkeynes@359: } nkeynes@359: } nkeynes@359: nkeynes@420: void fprint_indent( char *action, int depth, FILE *f ) nkeynes@359: { nkeynes@359: int spaces = 0, needed = depth*8, i; nkeynes@359: char *text = action; nkeynes@359: nkeynes@359: /* Determine number of spaces in first line of input */ nkeynes@359: for( i=0; isspace(action[i]); i++ ) { nkeynes@359: if( action[i] == '\n' ) { nkeynes@359: spaces = 0; nkeynes@359: text = &action[i+1]; nkeynes@359: } else { nkeynes@359: spaces++; nkeynes@359: } nkeynes@359: } nkeynes@359: nkeynes@359: needed -= spaces; nkeynes@359: fprintf( f, "%*c", needed, ' ' ); nkeynes@359: for( i=0; text[i] != '\0'; i++ ) { nkeynes@359: fputc( text[i], f ); nkeynes@359: if( text[i] == '\n' && text[i+1] != '\0' ) { nkeynes@359: fprintf( f, "%*c", needed, ' ' ); nkeynes@359: } nkeynes@359: } nkeynes@359: if( text[i-1] != '\n' ) { nkeynes@359: fprintf( f, "\n" ); nkeynes@359: } nkeynes@359: } nkeynes@359: nkeynes@420: void fprint_action( struct rule *rule, char *action, int depth, FILE *f ) nkeynes@359: { nkeynes@359: int i; nkeynes@359: if( action == NULL ) { nkeynes@359: fprintf( f, "%*cUNIMP(ir); /* %s */\n", depth*8, ' ', rule->format ); nkeynes@359: } else { nkeynes@359: fprintf( f, "%*c{ /* %s */", depth*8, ' ', rule->format ); nkeynes@359: if( rule->operand_count != 0 ) { nkeynes@359: fprintf( f, "\n%*c", depth*8, ' ' ); nkeynes@359: for( i=0; ioperand_count; i++ ) { nkeynes@359: if( rule->operands[i].is_signed ) { nkeynes@359: fprintf( f, "int32_t %s = SIGNEXT%d", rule->operands[i].name, rule->operands[i].bit_count ); nkeynes@359: } else { nkeynes@359: fprintf( f, "uint32_t %s = ", rule->operands[i].name ); nkeynes@359: } nkeynes@359: if( rule->operands[i].bit_shift == 0 ) { nkeynes@359: fprintf( f, "(ir&0x%X)", (1<<(rule->operands[i].bit_count))-1 ); nkeynes@359: } else { nkeynes@359: fprintf( f, "((ir>>%d)&0x%X)", rule->operands[i].bit_shift, nkeynes@359: (1<<(rule->operands[i].bit_count))-1 ); nkeynes@359: } nkeynes@359: if( rule->operands[i].left_shift != 0 ) { nkeynes@359: fprintf( f, "<<%d", rule->operands[i].left_shift ); nkeynes@359: } nkeynes@359: fprintf( f, "; " ); nkeynes@359: } nkeynes@359: } nkeynes@359: fputs( "\n", f ); nkeynes@359: if( action[0] != '\0' ) { nkeynes@359: fprint_indent( action, depth, f ); nkeynes@359: } nkeynes@359: fprintf( f, "%*c}\n", depth*8, ' ' ); nkeynes@359: } nkeynes@359: } nkeynes@359: nkeynes@420: void split_and_generate( struct ruleset *rules, struct actionset *actions, nkeynes@359: int ruleidx[], int rule_count, int input_mask, nkeynes@359: int depth, FILE *f ) { nkeynes@359: uint32_t mask; nkeynes@359: int i,j; nkeynes@359: nkeynes@359: if( rule_count == 0 ) { nkeynes@359: fprintf( f, "%*cUNDEF(ir);\n", depth*8, ' ' ); nkeynes@359: } else if( rule_count == 1 ) { nkeynes@359: fprint_action( rules->rules[ruleidx[0]], actions->actions[ruleidx[0]], depth, f ); nkeynes@359: } else { nkeynes@359: nkeynes@359: mask = find_mask(rules, ruleidx, rule_count, input_mask); nkeynes@359: if( mask == 0 ) { /* No matching mask? */ nkeynes@359: fprintf( stderr, "Error: unable to find a valid bitmask (%d rules, %08X input mask)\n", rule_count, input_mask ); nkeynes@359: dump_rulesubset( rules, ruleidx, rule_count, stderr ); nkeynes@420: return; nkeynes@359: } nkeynes@359: nkeynes@359: /* break up the rules into sub-sets, and process each sub-set. nkeynes@359: * NB: We could do this in one pass at the cost of more complex nkeynes@359: * data structures. For now though, this keeps it simple nkeynes@359: */ nkeynes@359: int option_count = get_option_count_for_mask( mask ); nkeynes@359: uint32_t options[option_count]; nkeynes@359: int subruleidx[rule_count]; nkeynes@359: int subrule_count; nkeynes@359: int mask_shift = get_bitshift_for_mask( mask ); nkeynes@359: int has_empty_options = 0; nkeynes@359: get_option_values_for_mask( options, mask ); nkeynes@359: nkeynes@359: if( mask_shift == 0 ) { nkeynes@359: fprintf( f, "%*cswitch( ir&0x%X ) {\n", depth*8, ' ', mask ); nkeynes@359: } else { nkeynes@359: fprintf( f, "%*cswitch( (ir&0x%X) >> %d ) {\n", depth*8, ' ', nkeynes@359: mask, mask_shift); nkeynes@359: } nkeynes@359: for( i=0; irules[ruleidx[j]]->bits & mask; nkeynes@359: if( match == options[i] ) { nkeynes@359: subruleidx[subrule_count++] = ruleidx[j]; nkeynes@359: } nkeynes@359: } nkeynes@359: if( subrule_count == 0 ) { nkeynes@359: has_empty_options = 1; nkeynes@359: } else { nkeynes@359: fprintf( f, "%*ccase 0x%X:\n", depth*8+4, ' ', options[i]>>mask_shift ); nkeynes@359: split_and_generate( rules, actions, subruleidx, subrule_count, nkeynes@359: mask|input_mask, depth+1, f ); nkeynes@359: fprintf( f, "%*cbreak;\n", depth*8+8, ' ' ); nkeynes@359: } nkeynes@359: } nkeynes@359: if( has_empty_options ) { nkeynes@359: fprintf( f, "%*cdefault:\n%*cUNDEF();\n%*cbreak;\n", nkeynes@359: depth*8+4, ' ', depth*8+8, ' ', depth*8 + 8, ' ' ); nkeynes@359: } nkeynes@359: fprintf( f, "%*c}\n", depth*8, ' ' ); nkeynes@359: } nkeynes@359: } nkeynes@359: nkeynes@359: int generate_decoder( struct ruleset *rules, struct actionset *actions, FILE *f ) nkeynes@359: { nkeynes@359: int ruleidx[rules->rule_count]; nkeynes@359: int i; nkeynes@359: nkeynes@359: for( i=0; irule_count; i++ ) { nkeynes@359: ruleidx[i] = i; nkeynes@359: } nkeynes@359: nkeynes@359: fputs( actions->pretext, f ); nkeynes@359: nkeynes@359: split_and_generate( rules, actions, ruleidx, rules->rule_count, 0, 1, f ); nkeynes@359: nkeynes@359: fputs( actions->posttext, f ); nkeynes@359: nkeynes@359: return 0; nkeynes@359: } nkeynes@359: nkeynes@359: int generate_template( struct ruleset *rules, struct actionset *actions, FILE *f ) nkeynes@359: { nkeynes@359: int i; nkeynes@359: fputs( actions->pretext, f ); nkeynes@359: fputs( "%%\n", f ); nkeynes@359: nkeynes@359: for( i=0; irule_count; i++ ) { nkeynes@359: fprintf( f, "%s {: %s :}\n", rules->rules[i]->format, nkeynes@359: actions->actions[i] == NULL ? "" : actions->actions[i] ); nkeynes@359: } nkeynes@359: fputs( "%%\n", f ); nkeynes@359: fputs( actions->posttext, f ); nkeynes@359: nkeynes@359: return 0; nkeynes@359: } nkeynes@420: nkeynes@420: nkeynes@420: int main( int argc, char *argv[] ) nkeynes@420: { nkeynes@420: int opt; nkeynes@420: nkeynes@420: /* Parse the command line */ nkeynes@420: while( (opt = getopt_long( argc, argv, option_list, longopts, NULL )) != -1 ) { nkeynes@420: switch( opt ) { nkeynes@420: case 't': nkeynes@420: gen_mode = GEN_TEMPLATE; nkeynes@420: break; nkeynes@420: case 'o': nkeynes@420: out_filename = optarg; nkeynes@420: break; nkeynes@420: case 'h': nkeynes@420: usage(); nkeynes@420: exit(0); nkeynes@420: } nkeynes@420: } nkeynes@420: if( optind < argc ) { nkeynes@420: ins_filename = argv[optind++]; nkeynes@420: } nkeynes@420: if( optind < argc ) { nkeynes@420: act_filename = argv[optind++]; nkeynes@420: } nkeynes@420: nkeynes@420: if( optind < argc || ins_filename == NULL || act_filename == NULL ) { nkeynes@420: usage(); nkeynes@420: exit(1); nkeynes@420: } nkeynes@420: nkeynes@420: if( out_filename == NULL ) { nkeynes@420: if( gen_mode == GEN_TEMPLATE ) { nkeynes@420: out_filename = act_filename; nkeynes@420: } else { nkeynes@420: char tmp[strlen(act_filename)+1]; nkeynes@420: strcpy( tmp, act_filename); nkeynes@420: char *c = strrchr( tmp, '.' ); nkeynes@420: if( c != NULL ) { nkeynes@420: *c = '\0'; nkeynes@420: } nkeynes@420: out_filename = g_strconcat( tmp, DEFAULT_OUT_EXT, NULL ); nkeynes@420: } nkeynes@420: } nkeynes@420: nkeynes@420: /* Open the files */ nkeynes@420: ins_file = fopen( ins_filename, "ro" ); nkeynes@420: if( ins_file == NULL ) { nkeynes@420: fprintf( stderr, "Unable to open '%s' for reading (%s)\n", ins_filename, strerror(errno) ); nkeynes@420: exit(2); nkeynes@420: } nkeynes@420: nkeynes@420: act_file = fopen( act_filename, "ro" ); nkeynes@420: if( act_file == NULL ) { nkeynes@420: fprintf( stderr, "Unable to open '%s' for reading (%s)\n", act_filename, strerror(errno) ); nkeynes@420: exit(3); nkeynes@420: } nkeynes@420: nkeynes@420: /* Parse the input */ nkeynes@420: struct ruleset *rules = parse_ruleset_file( ins_file ); nkeynes@420: fclose( ins_file ); nkeynes@420: if( rules == NULL ) { nkeynes@420: exit(5); nkeynes@420: } nkeynes@420: nkeynes@420: struct actionset *actions = parse_action_file( rules, act_file ); nkeynes@420: fclose( act_file ); nkeynes@420: if( actions == NULL ) { nkeynes@420: exit(6); nkeynes@420: } nkeynes@420: nkeynes@420: /* Finally write out the results */ nkeynes@420: out_file = fopen( out_filename, "wo" ); nkeynes@420: if( out_file == NULL ) { nkeynes@420: fprintf( stderr, "Unable to open '%s' for writing (%s)\n", out_filename, strerror(errno) ); nkeynes@420: exit(4); nkeynes@420: } nkeynes@420: nkeynes@420: switch( gen_mode ) { nkeynes@420: case GEN_SOURCE: nkeynes@420: if( generate_decoder( rules, actions, out_file ) != 0 ) { nkeynes@420: exit(7); nkeynes@420: } nkeynes@420: break; nkeynes@420: case GEN_TEMPLATE: nkeynes@420: if( generate_template( rules, actions, out_file ) != 0 ) { nkeynes@420: exit(7); nkeynes@420: } nkeynes@420: break; nkeynes@420: } nkeynes@420: fclose( out_file ); nkeynes@420: return 0; nkeynes@420: }