nkeynes@359 | 1 | /**
|
nkeynes@359 | 2 | * $Id: gendec.c,v 1.1 2007-08-23 12:33:27 nkeynes Exp $
|
nkeynes@359 | 3 | *
|
nkeynes@359 | 4 | * Parse the instruction and action files and generate an appropriate
|
nkeynes@359 | 5 | * instruction decoder.
|
nkeynes@359 | 6 | *
|
nkeynes@359 | 7 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@359 | 8 | *
|
nkeynes@359 | 9 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@359 | 10 | * it under the terms of the GNU General Public License as published by
|
nkeynes@359 | 11 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@359 | 12 | * (at your option) any later version.
|
nkeynes@359 | 13 | *
|
nkeynes@359 | 14 | * This program is distributed in the hope that it will be useful,
|
nkeynes@359 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@359 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@359 | 17 | * GNU General Public License for more details.
|
nkeynes@359 | 18 | */
|
nkeynes@359 | 19 |
|
nkeynes@359 | 20 | #include <stdio.h>
|
nkeynes@359 | 21 | #include <stdlib.h>
|
nkeynes@359 | 22 | #include <string.h>
|
nkeynes@359 | 23 | #include <getopt.h>
|
nkeynes@359 | 24 | #include <errno.h>
|
nkeynes@359 | 25 | #include <ctype.h>
|
nkeynes@359 | 26 | #include <glib/gstrfuncs.h>
|
nkeynes@359 | 27 | #include <assert.h>
|
nkeynes@359 | 28 | #include "tools/gendec.h"
|
nkeynes@359 | 29 |
|
nkeynes@359 | 30 | #define DEFAULT_OUT_EXT ".c"
|
nkeynes@359 | 31 |
|
nkeynes@359 | 32 | const char *ins_filename = NULL;
|
nkeynes@359 | 33 | const char *act_filename = NULL;
|
nkeynes@359 | 34 | const char *out_filename = NULL;
|
nkeynes@359 | 35 |
|
nkeynes@359 | 36 | #define GEN_SOURCE 1
|
nkeynes@359 | 37 | #define GEN_TEMPLATE 2
|
nkeynes@359 | 38 |
|
nkeynes@359 | 39 | FILE *ins_file, *act_file, *out_file;
|
nkeynes@359 | 40 |
|
nkeynes@359 | 41 | char *option_list = "tmho:";
|
nkeynes@359 | 42 | int gen_mode = GEN_SOURCE;
|
nkeynes@359 | 43 | struct option longopts[1] = { { NULL, 0, 0, 0 } };
|
nkeynes@359 | 44 |
|
nkeynes@359 | 45 | void usage() {
|
nkeynes@359 | 46 | printf( "gendec <instruction-file> <action-file> [ -o <output-file> ]\n" );
|
nkeynes@359 | 47 | }
|
nkeynes@359 | 48 |
|
nkeynes@359 | 49 | int main( int argc, char *argv[] )
|
nkeynes@359 | 50 | {
|
nkeynes@359 | 51 | int opt, i;
|
nkeynes@359 | 52 |
|
nkeynes@359 | 53 | /* Parse the command line */
|
nkeynes@359 | 54 | while( (opt = getopt_long( argc, argv, option_list, longopts, NULL )) != -1 ) {
|
nkeynes@359 | 55 | switch( opt ) {
|
nkeynes@359 | 56 | case 't':
|
nkeynes@359 | 57 | gen_mode = GEN_TEMPLATE;
|
nkeynes@359 | 58 | break;
|
nkeynes@359 | 59 | case 'o':
|
nkeynes@359 | 60 | out_filename = optarg;
|
nkeynes@359 | 61 | break;
|
nkeynes@359 | 62 | case 'h':
|
nkeynes@359 | 63 | usage();
|
nkeynes@359 | 64 | exit(0);
|
nkeynes@359 | 65 | }
|
nkeynes@359 | 66 | }
|
nkeynes@359 | 67 | if( optind < argc ) {
|
nkeynes@359 | 68 | ins_filename = argv[optind++];
|
nkeynes@359 | 69 | }
|
nkeynes@359 | 70 | if( optind < argc ) {
|
nkeynes@359 | 71 | act_filename = argv[optind++];
|
nkeynes@359 | 72 | }
|
nkeynes@359 | 73 |
|
nkeynes@359 | 74 | if( optind < argc || ins_filename == NULL || act_filename == NULL ) {
|
nkeynes@359 | 75 | usage();
|
nkeynes@359 | 76 | exit(1);
|
nkeynes@359 | 77 | }
|
nkeynes@359 | 78 |
|
nkeynes@359 | 79 | if( out_filename == NULL ) {
|
nkeynes@359 | 80 | if( gen_mode == GEN_TEMPLATE ) {
|
nkeynes@359 | 81 | out_filename = act_filename;
|
nkeynes@359 | 82 | } else {
|
nkeynes@359 | 83 | char tmp[strlen(act_filename)+1];
|
nkeynes@359 | 84 | strcpy( tmp, act_filename);
|
nkeynes@359 | 85 | char *c = strrchr( tmp, '.' );
|
nkeynes@359 | 86 | if( c != NULL ) {
|
nkeynes@359 | 87 | *c = '\0';
|
nkeynes@359 | 88 | }
|
nkeynes@359 | 89 | out_filename = g_strconcat( tmp, DEFAULT_OUT_EXT );
|
nkeynes@359 | 90 | }
|
nkeynes@359 | 91 | }
|
nkeynes@359 | 92 |
|
nkeynes@359 | 93 | /* Open the files */
|
nkeynes@359 | 94 | ins_file = fopen( ins_filename, "ro" );
|
nkeynes@359 | 95 | if( ins_file == NULL ) {
|
nkeynes@359 | 96 | fprintf( stderr, "Unable to open '%s' for reading (%s)\n", ins_filename, strerror(errno) );
|
nkeynes@359 | 97 | exit(2);
|
nkeynes@359 | 98 | }
|
nkeynes@359 | 99 |
|
nkeynes@359 | 100 | act_file = fopen( act_filename, "ro" );
|
nkeynes@359 | 101 | if( act_file == NULL ) {
|
nkeynes@359 | 102 | fprintf( stderr, "Unable to open '%s' for reading (%s)\n", act_filename, strerror(errno) );
|
nkeynes@359 | 103 | exit(3);
|
nkeynes@359 | 104 | }
|
nkeynes@359 | 105 |
|
nkeynes@359 | 106 | /* Parse the input */
|
nkeynes@359 | 107 | struct ruleset *rules = parse_ruleset_file( ins_file );
|
nkeynes@359 | 108 | fclose( ins_file );
|
nkeynes@359 | 109 | if( rules == NULL ) {
|
nkeynes@359 | 110 | exit(5);
|
nkeynes@359 | 111 | }
|
nkeynes@359 | 112 |
|
nkeynes@359 | 113 | struct actionset *actions = parse_action_file( rules, act_file );
|
nkeynes@359 | 114 | fclose( act_file );
|
nkeynes@359 | 115 | if( actions == NULL ) {
|
nkeynes@359 | 116 | exit(6);
|
nkeynes@359 | 117 | }
|
nkeynes@359 | 118 |
|
nkeynes@359 | 119 | /* Finally write out the results */
|
nkeynes@359 | 120 | out_file = fopen( out_filename, "wo" );
|
nkeynes@359 | 121 | if( out_file == NULL ) {
|
nkeynes@359 | 122 | fprintf( stderr, "Unable to open '%s' for writing (%s)\n", out_filename, strerror(errno) );
|
nkeynes@359 | 123 | exit(4);
|
nkeynes@359 | 124 | }
|
nkeynes@359 | 125 |
|
nkeynes@359 | 126 | switch( gen_mode ) {
|
nkeynes@359 | 127 | case GEN_SOURCE:
|
nkeynes@359 | 128 | if( generate_decoder( rules, actions, out_file ) != 0 ) {
|
nkeynes@359 | 129 | exit(7);
|
nkeynes@359 | 130 | }
|
nkeynes@359 | 131 | break;
|
nkeynes@359 | 132 | case GEN_TEMPLATE:
|
nkeynes@359 | 133 | if( generate_template( rules, actions, out_file ) != 0 ) {
|
nkeynes@359 | 134 | exit(7);
|
nkeynes@359 | 135 | }
|
nkeynes@359 | 136 | break;
|
nkeynes@359 | 137 | }
|
nkeynes@359 | 138 | fclose( out_file );
|
nkeynes@359 | 139 | return 0;
|
nkeynes@359 | 140 | }
|
nkeynes@359 | 141 |
|
nkeynes@359 | 142 | /**
|
nkeynes@359 | 143 | * Find a mask that can be used to split up the given rules
|
nkeynes@359 | 144 | */
|
nkeynes@359 | 145 | uint32_t find_mask( struct ruleset *rules, int ruleidx[], int rule_count,
|
nkeynes@359 | 146 | uint32_t input_mask )
|
nkeynes@359 | 147 | {
|
nkeynes@359 | 148 | int i;
|
nkeynes@359 | 149 | uint32_t mask = rules->rules[ruleidx[0]]->mask;
|
nkeynes@359 | 150 |
|
nkeynes@359 | 151 | for( i=1; i<rule_count; i++ ) {
|
nkeynes@359 | 152 | mask = mask & rules->rules[ruleidx[i]]->mask;
|
nkeynes@359 | 153 | }
|
nkeynes@359 | 154 |
|
nkeynes@359 | 155 | assert( (mask & input_mask) == input_mask ); /* input_mask should always be included in the mask */
|
nkeynes@359 | 156 |
|
nkeynes@359 | 157 | return mask & (~input_mask); /* but we don't want to see the input mask again */
|
nkeynes@359 | 158 | }
|
nkeynes@359 | 159 |
|
nkeynes@359 | 160 | int get_option_count_for_mask( uint32_t mask ) {
|
nkeynes@359 | 161 | int count = 0;
|
nkeynes@359 | 162 |
|
nkeynes@359 | 163 | while( mask ) {
|
nkeynes@359 | 164 | if( mask&1 )
|
nkeynes@359 | 165 | count++;
|
nkeynes@359 | 166 | mask >>= 1;
|
nkeynes@359 | 167 | }
|
nkeynes@359 | 168 | return 1<<count;
|
nkeynes@359 | 169 | }
|
nkeynes@359 | 170 |
|
nkeynes@359 | 171 | int get_bitshift_for_mask( uint32_t mask ) {
|
nkeynes@359 | 172 | int shift = 0;
|
nkeynes@359 | 173 | while( mask && !(mask&1) ) {
|
nkeynes@359 | 174 | shift++;
|
nkeynes@359 | 175 | mask >>= 1;
|
nkeynes@359 | 176 | }
|
nkeynes@359 | 177 | return shift;
|
nkeynes@359 | 178 | }
|
nkeynes@359 | 179 |
|
nkeynes@359 | 180 | void get_option_values_for_mask( uint32_t *options,
|
nkeynes@359 | 181 | uint32_t mask )
|
nkeynes@359 | 182 | {
|
nkeynes@359 | 183 | /* This could be a lot smarter. But it's not */
|
nkeynes@359 | 184 | int i;
|
nkeynes@359 | 185 | *options = 0;
|
nkeynes@359 | 186 | for( i=1; i<=mask; i++ ) {
|
nkeynes@359 | 187 | if( (i & mask) > *options ) {
|
nkeynes@359 | 188 | options++;
|
nkeynes@359 | 189 | *options = (i&mask);
|
nkeynes@359 | 190 | }
|
nkeynes@359 | 191 | }
|
nkeynes@359 | 192 | }
|
nkeynes@359 | 193 |
|
nkeynes@359 | 194 | fprint_indent( char *action, int depth, FILE *f )
|
nkeynes@359 | 195 | {
|
nkeynes@359 | 196 | int spaces = 0, needed = depth*8, i;
|
nkeynes@359 | 197 | char *text = action;
|
nkeynes@359 | 198 |
|
nkeynes@359 | 199 | /* Determine number of spaces in first line of input */
|
nkeynes@359 | 200 | for( i=0; isspace(action[i]); i++ ) {
|
nkeynes@359 | 201 | if( action[i] == '\n' ) {
|
nkeynes@359 | 202 | spaces = 0;
|
nkeynes@359 | 203 | text = &action[i+1];
|
nkeynes@359 | 204 | } else {
|
nkeynes@359 | 205 | spaces++;
|
nkeynes@359 | 206 | }
|
nkeynes@359 | 207 | }
|
nkeynes@359 | 208 |
|
nkeynes@359 | 209 | needed -= spaces;
|
nkeynes@359 | 210 | fprintf( f, "%*c", needed, ' ' );
|
nkeynes@359 | 211 | for( i=0; text[i] != '\0'; i++ ) {
|
nkeynes@359 | 212 | fputc( text[i], f );
|
nkeynes@359 | 213 | if( text[i] == '\n' && text[i+1] != '\0' ) {
|
nkeynes@359 | 214 | fprintf( f, "%*c", needed, ' ' );
|
nkeynes@359 | 215 | }
|
nkeynes@359 | 216 | }
|
nkeynes@359 | 217 | if( text[i-1] != '\n' ) {
|
nkeynes@359 | 218 | fprintf( f, "\n" );
|
nkeynes@359 | 219 | }
|
nkeynes@359 | 220 | }
|
nkeynes@359 | 221 |
|
nkeynes@359 | 222 | fprint_action( struct rule *rule, char *action, int depth, FILE *f )
|
nkeynes@359 | 223 | {
|
nkeynes@359 | 224 | int i;
|
nkeynes@359 | 225 | char tmp[64];
|
nkeynes@359 | 226 | if( action == NULL ) {
|
nkeynes@359 | 227 | fprintf( f, "%*cUNIMP(ir); /* %s */\n", depth*8, ' ', rule->format );
|
nkeynes@359 | 228 | } else {
|
nkeynes@359 | 229 | fprintf( f, "%*c{ /* %s */", depth*8, ' ', rule->format );
|
nkeynes@359 | 230 | if( rule->operand_count != 0 ) {
|
nkeynes@359 | 231 | fprintf( f, "\n%*c", depth*8, ' ' );
|
nkeynes@359 | 232 | for( i=0; i<rule->operand_count; i++ ) {
|
nkeynes@359 | 233 | if( rule->operands[i].is_signed ) {
|
nkeynes@359 | 234 | fprintf( f, "int32_t %s = SIGNEXT%d", rule->operands[i].name, rule->operands[i].bit_count );
|
nkeynes@359 | 235 | } else {
|
nkeynes@359 | 236 | fprintf( f, "uint32_t %s = ", rule->operands[i].name );
|
nkeynes@359 | 237 | }
|
nkeynes@359 | 238 | if( rule->operands[i].bit_shift == 0 ) {
|
nkeynes@359 | 239 | fprintf( f, "(ir&0x%X)", (1<<(rule->operands[i].bit_count))-1 );
|
nkeynes@359 | 240 | } else {
|
nkeynes@359 | 241 | fprintf( f, "((ir>>%d)&0x%X)", rule->operands[i].bit_shift,
|
nkeynes@359 | 242 | (1<<(rule->operands[i].bit_count))-1 );
|
nkeynes@359 | 243 | }
|
nkeynes@359 | 244 | if( rule->operands[i].left_shift != 0 ) {
|
nkeynes@359 | 245 | fprintf( f, "<<%d", rule->operands[i].left_shift );
|
nkeynes@359 | 246 | }
|
nkeynes@359 | 247 | fprintf( f, "; " );
|
nkeynes@359 | 248 | }
|
nkeynes@359 | 249 | }
|
nkeynes@359 | 250 | fputs( "\n", f );
|
nkeynes@359 | 251 | if( action[0] != '\0' ) {
|
nkeynes@359 | 252 | fprint_indent( action, depth, f );
|
nkeynes@359 | 253 | }
|
nkeynes@359 | 254 | fprintf( f, "%*c}\n", depth*8, ' ' );
|
nkeynes@359 | 255 | }
|
nkeynes@359 | 256 | }
|
nkeynes@359 | 257 |
|
nkeynes@359 | 258 | int split_and_generate( struct ruleset *rules, struct actionset *actions,
|
nkeynes@359 | 259 | int ruleidx[], int rule_count, int input_mask,
|
nkeynes@359 | 260 | int depth, FILE *f ) {
|
nkeynes@359 | 261 | char tmp[64];
|
nkeynes@359 | 262 | uint32_t mask;
|
nkeynes@359 | 263 | int i,j;
|
nkeynes@359 | 264 |
|
nkeynes@359 | 265 | if( rule_count == 0 ) {
|
nkeynes@359 | 266 | fprintf( f, "%*cUNDEF(ir);\n", depth*8, ' ' );
|
nkeynes@359 | 267 | } else if( rule_count == 1 ) {
|
nkeynes@359 | 268 | fprint_action( rules->rules[ruleidx[0]], actions->actions[ruleidx[0]], depth, f );
|
nkeynes@359 | 269 | } else {
|
nkeynes@359 | 270 |
|
nkeynes@359 | 271 | mask = find_mask(rules, ruleidx, rule_count, input_mask);
|
nkeynes@359 | 272 | if( mask == 0 ) { /* No matching mask? */
|
nkeynes@359 | 273 | fprintf( stderr, "Error: unable to find a valid bitmask (%d rules, %08X input mask)\n", rule_count, input_mask );
|
nkeynes@359 | 274 | dump_rulesubset( rules, ruleidx, rule_count, stderr );
|
nkeynes@359 | 275 | return -1;
|
nkeynes@359 | 276 | }
|
nkeynes@359 | 277 |
|
nkeynes@359 | 278 | /* break up the rules into sub-sets, and process each sub-set.
|
nkeynes@359 | 279 | * NB: We could do this in one pass at the cost of more complex
|
nkeynes@359 | 280 | * data structures. For now though, this keeps it simple
|
nkeynes@359 | 281 | */
|
nkeynes@359 | 282 | int option_count = get_option_count_for_mask( mask );
|
nkeynes@359 | 283 | uint32_t options[option_count];
|
nkeynes@359 | 284 | int subruleidx[rule_count];
|
nkeynes@359 | 285 | int subrule_count;
|
nkeynes@359 | 286 | int mask_shift = get_bitshift_for_mask( mask );
|
nkeynes@359 | 287 | int has_empty_options = 0;
|
nkeynes@359 | 288 | get_option_values_for_mask( options, mask );
|
nkeynes@359 | 289 |
|
nkeynes@359 | 290 | if( mask_shift == 0 ) {
|
nkeynes@359 | 291 | fprintf( f, "%*cswitch( ir&0x%X ) {\n", depth*8, ' ', mask );
|
nkeynes@359 | 292 | } else {
|
nkeynes@359 | 293 | fprintf( f, "%*cswitch( (ir&0x%X) >> %d ) {\n", depth*8, ' ',
|
nkeynes@359 | 294 | mask, mask_shift);
|
nkeynes@359 | 295 | }
|
nkeynes@359 | 296 | for( i=0; i<option_count; i++ ) {
|
nkeynes@359 | 297 | subrule_count = 0;
|
nkeynes@359 | 298 | for( j=0; j<rule_count; j++ ) {
|
nkeynes@359 | 299 | int match = rules->rules[ruleidx[j]]->bits & mask;
|
nkeynes@359 | 300 | if( match == options[i] ) {
|
nkeynes@359 | 301 | subruleidx[subrule_count++] = ruleidx[j];
|
nkeynes@359 | 302 | }
|
nkeynes@359 | 303 | }
|
nkeynes@359 | 304 | if( subrule_count == 0 ) {
|
nkeynes@359 | 305 | has_empty_options = 1;
|
nkeynes@359 | 306 | } else {
|
nkeynes@359 | 307 | fprintf( f, "%*ccase 0x%X:\n", depth*8+4, ' ', options[i]>>mask_shift );
|
nkeynes@359 | 308 | split_and_generate( rules, actions, subruleidx, subrule_count,
|
nkeynes@359 | 309 | mask|input_mask, depth+1, f );
|
nkeynes@359 | 310 | fprintf( f, "%*cbreak;\n", depth*8+8, ' ' );
|
nkeynes@359 | 311 | }
|
nkeynes@359 | 312 | }
|
nkeynes@359 | 313 | if( has_empty_options ) {
|
nkeynes@359 | 314 | fprintf( f, "%*cdefault:\n%*cUNDEF();\n%*cbreak;\n",
|
nkeynes@359 | 315 | depth*8+4, ' ', depth*8+8, ' ', depth*8 + 8, ' ' );
|
nkeynes@359 | 316 | }
|
nkeynes@359 | 317 | fprintf( f, "%*c}\n", depth*8, ' ' );
|
nkeynes@359 | 318 | }
|
nkeynes@359 | 319 | }
|
nkeynes@359 | 320 |
|
nkeynes@359 | 321 | int generate_decoder( struct ruleset *rules, struct actionset *actions, FILE *f )
|
nkeynes@359 | 322 | {
|
nkeynes@359 | 323 | int ruleidx[rules->rule_count];
|
nkeynes@359 | 324 | int i;
|
nkeynes@359 | 325 |
|
nkeynes@359 | 326 | for( i=0; i<rules->rule_count; i++ ) {
|
nkeynes@359 | 327 | ruleidx[i] = i;
|
nkeynes@359 | 328 | }
|
nkeynes@359 | 329 |
|
nkeynes@359 | 330 | fputs( actions->pretext, f );
|
nkeynes@359 | 331 |
|
nkeynes@359 | 332 | split_and_generate( rules, actions, ruleidx, rules->rule_count, 0, 1, f );
|
nkeynes@359 | 333 |
|
nkeynes@359 | 334 | fputs( actions->posttext, f );
|
nkeynes@359 | 335 |
|
nkeynes@359 | 336 | return 0;
|
nkeynes@359 | 337 | }
|
nkeynes@359 | 338 |
|
nkeynes@359 | 339 | int generate_template( struct ruleset *rules, struct actionset *actions, FILE *f )
|
nkeynes@359 | 340 | {
|
nkeynes@359 | 341 | int i;
|
nkeynes@359 | 342 | fputs( actions->pretext, f );
|
nkeynes@359 | 343 | fputs( "%%\n", f );
|
nkeynes@359 | 344 |
|
nkeynes@359 | 345 | for( i=0; i<rules->rule_count; i++ ) {
|
nkeynes@359 | 346 | fprintf( f, "%s {: %s :}\n", rules->rules[i]->format,
|
nkeynes@359 | 347 | actions->actions[i] == NULL ? "" : actions->actions[i] );
|
nkeynes@359 | 348 | }
|
nkeynes@359 | 349 | fputs( "%%\n", f );
|
nkeynes@359 | 350 | fputs( actions->posttext, f );
|
nkeynes@359 | 351 |
|
nkeynes@359 | 352 | return 0;
|
nkeynes@359 | 353 | }
|