Search
lxdream.org :: lxdream/src/tools/mdparse.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/mdparse.c
changeset 1104:700e16c321e5
next1105:73da8fd129fb
author nkeynes
date Mon Mar 15 22:10:24 2010 +1000 (12 years ago)
permissions -rw-r--r--
last change Commit genmach work-in-progress
file annotate diff log raw
nkeynes@1104
     1
/**
nkeynes@1104
     2
 * $Id$
nkeynes@1104
     3
 *
nkeynes@1104
     4
 * genmmio I/O register definition parser.
nkeynes@1104
     5
 *
nkeynes@1104
     6
 * Copyright (c) 2010 Nathan Keynes.
nkeynes@1104
     7
 *
nkeynes@1104
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@1104
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@1104
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@1104
    11
 * (at your option) any later version.
nkeynes@1104
    12
 *
nkeynes@1104
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@1104
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@1104
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@1104
    16
 * GNU General Public License for more details.
nkeynes@1104
    17
 */
nkeynes@1104
    18
nkeynes@1104
    19
#include <assert.h>
nkeynes@1104
    20
#include <stdlib.h>
nkeynes@1104
    21
#include <stdio.h>
nkeynes@1104
    22
#include <string.h>
nkeynes@1104
    23
#include <unistd.h>
nkeynes@1104
    24
#include <fcntl.h>
nkeynes@1104
    25
#include <ctype.h>
nkeynes@1104
    26
#include <errno.h>
nkeynes@1104
    27
#include <sys/stat.h>
nkeynes@1104
    28
#include <glib/gmem.h>
nkeynes@1104
    29
nkeynes@1104
    30
#include "genmach.h"
nkeynes@1104
    31
/*
nkeynes@1104
    32
 * Grammar:
nkeynes@1104
    33
 *
nkeynes@1104
    34
 * start: start register_block |  ;
nkeynes@1104
    35
 * register_block: 'registers' IDENTIFIER 'at' INTEGER opt_paramlist '{' register_list '}';
nkeynes@1104
    36
 * opt_paramlist: '(' param_list ')' | '(' ')' |  ;
nkeynes@1104
    37
 * param_list: param_list ',' param | param ;
nkeynes@1104
    38
 * param: access_param | fill_param | endian_param | mask_param ;
nkeynes@1104
    39
 * access_param: 'access' '=' 'any'  // Behaves as if it were memory
nkeynes@1104
    40
 *        | 'access' '=' 'nooffset' // Recognize any size access (zero-ext or trunc) as long as the address is exactly the same
nkeynes@1104
    41
 *        | 'access' '=' 'exact'    // Recognize only the exact access type at the exact address
nkeynes@1104
    42
 *        ;
nkeynes@1104
    43
 * fill_param: 'fill' '=' literal;
nkeynes@1104
    44
 * endian_param: 'endian' '=' 'little'
nkeynes@1104
    45
 *        | 'endian' '=' 'big'
nkeynes@1104
    46
 *        ;
nkeynes@1104
    47
 * mask_param: 'mask' '=' literal;
nkeynes@1104
    48
 *
nkeynes@1104
    49
 * register_list: register_list register_desc | ;
nkeynes@1104
    50
 * register_desc: INTEGER ':' mode_spec type_spec IDENTIFIER opt_paramlist [ '=' literal ] [ ACTION ]
nkeynes@1104
    51
 * mode_spec: 'const' | 'rw' | 'ro' | 'wo';
nkeynes@1104
    52
 * type_spec: 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64' | 'string' | 'cstring';
nkeynes@1104
    53
 * literal: INTEGER | STRING | 'undefined' ;
nkeynes@1104
    54
 *
nkeynes@1104
    55
 * C-style comments are recognized as such.
nkeynes@1104
    56
 */
nkeynes@1104
    57
nkeynes@1104
    58
#define TOK_IDENTIFIER 1
nkeynes@1104
    59
#define TOK_INTEGER    2
nkeynes@1104
    60
#define TOK_STRING     3
nkeynes@1104
    61
#define TOK_ACTION     4
nkeynes@1104
    62
#define TOK_EOF        5
nkeynes@1104
    63
#define TOK_ERROR      6
nkeynes@1104
    64
#define TOK_LPAREN     7
nkeynes@1104
    65
#define TOK_RPAREN     8
nkeynes@1104
    66
#define TOK_LBRACE     9
nkeynes@1104
    67
#define TOK_RBRACE    10
nkeynes@1104
    68
#define TOK_COMMA     11
nkeynes@1104
    69
#define TOK_COLON     12
nkeynes@1104
    70
#define TOK_SEMI      13
nkeynes@1104
    71
#define TOK_PERIOD    14
nkeynes@1104
    72
#define TOK_RANGE     15
nkeynes@1104
    73
#define TOK_LSQUARE   16
nkeynes@1104
    74
#define TOK_RSQUARE   17
nkeynes@1104
    75
#define TOK_EQUALS    18
nkeynes@1104
    76
#define TOK_ACCESS    19
nkeynes@1104
    77
#define TOK_AT        20
nkeynes@1104
    78
#define TOK_ENDIAN    21
nkeynes@1104
    79
#define TOK_FILL      22
nkeynes@1104
    80
#define TOK_INCLUDE   23
nkeynes@1104
    81
#define TOK_MASK      24
nkeynes@1104
    82
#define TOK_REGISTERS 25
nkeynes@1104
    83
#define TOK_SPACE     26
nkeynes@1104
    84
#define TOK_STRIDE    27
nkeynes@1104
    85
#define TOK_TEST      28
nkeynes@1104
    86
#define TOK_TRACE     29
nkeynes@1104
    87
#define TOK_UNDEFINED 30
nkeynes@1104
    88
nkeynes@1104
    89
nkeynes@1104
    90
#define FIRST_KEYWORD TOK_ACCESS
nkeynes@1104
    91
#define LAST_KEYWORD  TOK_UNDEFINED
nkeynes@1104
    92
static const char *TOKEN_NAMES[] = {"NULL", "IDENTIFIER", "INTEGER", "STRING", "ACTION", "EOF", "ERROR",
nkeynes@1104
    93
        "(", ")", "{", "}", ",", ":", ";", ".", "..", "[", "]", "=",
nkeynes@1104
    94
        "ACCESS", "AT", "ENDIAN", "FILL", "INCLUDE", "MASK", "REGISTERS", "SPACE", "STRIDE", "TEST", "TRACE", "UNDEFINED" };
nkeynes@1104
    95
static const char *MODE_NAMES[] = {"const", "rw", "ro", "wo", "mirror"};
nkeynes@1104
    96
static const char *TYPE_NAMES[] = {"i8", "i16", "i32", "i64", "f32", "f64", "string"};
nkeynes@1104
    97
static int TYPE_SIZES[] = {1,2,4,8,4,8,0};
nkeynes@1104
    98
static const char *ENDIAN_NAMES[] = {"default", "little", "big"};
nkeynes@1104
    99
static const char *ACCESS_NAMES[] = {"default", "any", "nooffset", "exact"};
nkeynes@1104
   100
static const char *TRACE_NAMES[] = {"default", "off", "on"};
nkeynes@1104
   101
nkeynes@1104
   102
#define elementsof(x) (sizeof(x)/sizeof(x[0]))
nkeynes@1104
   103
nkeynes@1104
   104
typedef struct token_data {
nkeynes@1104
   105
    char *yytext;
nkeynes@1104
   106
    int yylength;
nkeynes@1104
   107
    int yyline;
nkeynes@1104
   108
    int yycol;
nkeynes@1104
   109
    union {
nkeynes@1104
   110
        long i;
nkeynes@1104
   111
        char *s;
nkeynes@1104
   112
    } v;
nkeynes@1104
   113
    int slen;
nkeynes@1104
   114
} token_data;
nkeynes@1104
   115
nkeynes@1104
   116
static char *yybuffer;
nkeynes@1104
   117
static char *yyposn, *yylineposn;
nkeynes@1104
   118
static char *yyend;
nkeynes@1104
   119
static int yyline;
nkeynes@1104
   120
static struct token_data yytok;
nkeynes@1104
   121
nkeynes@1104
   122
#define YYPARSE_ERROR( msg, ... ) \
nkeynes@1104
   123
    do { \
nkeynes@1104
   124
        fprintf( stderr, "Parse error at %d:%d: " msg "\n", yytok.yyline, yytok.yycol, __VA_ARGS__ ); \
nkeynes@1104
   125
        exit(2); \
nkeynes@1104
   126
    } while(0)
nkeynes@1104
   127
nkeynes@1104
   128
#define READ(x) \
nkeynes@1104
   129
{   int _tok = iolex(x); \
nkeynes@1104
   130
    if( _tok != (x) ) { \
nkeynes@1104
   131
        YYPARSE_ERROR( "Expected %s but got %s", TOKEN_NAMES[x], TOKEN_NAMES[_tok] ); \
nkeynes@1104
   132
    } \
nkeynes@1104
   133
}
nkeynes@1104
   134
nkeynes@1104
   135
static int iolex( int expectToken );
nkeynes@1104
   136
static int iolex_open( const char *filename );
nkeynes@1104
   137
static void iolex_close();
nkeynes@1104
   138
nkeynes@1104
   139
static inline char *yystrdup()
nkeynes@1104
   140
{
nkeynes@1104
   141
    char *str = g_malloc0(yytok.yylength+1);
nkeynes@1104
   142
    memcpy( str, yytok.yytext, yytok.yylength);
nkeynes@1104
   143
    return str;
nkeynes@1104
   144
}
nkeynes@1104
   145
nkeynes@1104
   146
static inline int yystrcasecmp(const char *cmp)
nkeynes@1104
   147
{
nkeynes@1104
   148
    int len = strlen(cmp);
nkeynes@1104
   149
    if( len != yytok.yylength ) {
nkeynes@1104
   150
        return yytok.yylength - len;
nkeynes@1104
   151
    }
nkeynes@1104
   152
    return strncasecmp(yytok.yytext, cmp, yytok.yylength);
nkeynes@1104
   153
}
nkeynes@1104
   154
nkeynes@1104
   155
static int yymatch( const char *arr[], unsigned numOptions )
nkeynes@1104
   156
{
nkeynes@1104
   157
    for( unsigned i=0; i<numOptions; i++ ) {
nkeynes@1104
   158
        if( yystrcasecmp( arr[i] ) == 0 ) {
nkeynes@1104
   159
            return i;
nkeynes@1104
   160
        }
nkeynes@1104
   161
    }
nkeynes@1104
   162
    return -1;
nkeynes@1104
   163
}
nkeynes@1104
   164
nkeynes@1104
   165
static gint register_block_sort_cb( gconstpointer a, gconstpointer b )
nkeynes@1104
   166
{
nkeynes@1104
   167
    struct regblock *blocka = (struct regblock *)a;
nkeynes@1104
   168
    struct regblock *blockb = (struct regblock *)b;
nkeynes@1104
   169
    return blocka->address - blockb->address;
nkeynes@1104
   170
}
nkeynes@1104
   171
nkeynes@1104
   172
static int register_ptr_sort_cb( const void *a, const void *b )
nkeynes@1104
   173
{
nkeynes@1104
   174
    regdef_t *ptra = (regdef_t *)a;
nkeynes@1104
   175
    regdef_t *ptrb = (regdef_t *)b;
nkeynes@1104
   176
    if( (*ptra)->offset != (*ptrb)->offset )
nkeynes@1104
   177
        return (*ptra)->offset - (*ptrb)->offset;
nkeynes@1104
   178
    return (*ptra)->type - (*ptrb)->type;
nkeynes@1104
   179
}
nkeynes@1104
   180
nkeynes@1104
   181
static void ioparse_apval(int tok, union apval *apv, unsigned *numBytes)
nkeynes@1104
   182
{
nkeynes@1104
   183
    if( tok == TOK_INTEGER ) {
nkeynes@1104
   184
        apv->i = yytok.v.i;
nkeynes@1104
   185
        if( *numBytes == 0 ) {
nkeynes@1104
   186
            YYPARSE_ERROR( "Expected string initializer but was an integer (0x%x)", yytok.v.i );
nkeynes@1104
   187
        }
nkeynes@1104
   188
    } else if( tok = TOK_STRING ) {
nkeynes@1104
   189
        if( *numBytes == 0 ) {
nkeynes@1104
   190
            *numBytes = yytok.slen;
nkeynes@1104
   191
            apv->s = yytok.v.s;
nkeynes@1104
   192
        } else {
nkeynes@1104
   193
            if( *numBytes != yytok.slen ) {
nkeynes@1104
   194
                YYPARSE_ERROR( "Expected %d byte initializer but was %d", *numBytes, yytok.slen );
nkeynes@1104
   195
            }
nkeynes@1104
   196
            assert( *numBytes < sizeof(uint64_t) );
nkeynes@1104
   197
            apv->i = 0;
nkeynes@1104
   198
            /* FIXME: handle endian mismatches */
nkeynes@1104
   199
            memcpy( &apv->i, yytok.v.s, yytok.slen );
nkeynes@1104
   200
        }
nkeynes@1104
   201
    } else {
nkeynes@1104
   202
        YYPARSE_ERROR( "Expected literal (integer or string), but got %s", TOKEN_NAMES[tok] );
nkeynes@1104
   203
    }
nkeynes@1104
   204
}
nkeynes@1104
   205
nkeynes@1104
   206
static void ioparse_regflags( regflags_t flags, unsigned *numBytes )
nkeynes@1104
   207
{
nkeynes@1104
   208
    do {
nkeynes@1104
   209
        int tok = iolex(TOK_RPAREN);
nkeynes@1104
   210
        switch(tok) {
nkeynes@1104
   211
        case TOK_FILL:
nkeynes@1104
   212
            READ(TOK_EQUALS);
nkeynes@1104
   213
            tok = iolex(TOK_INTEGER);
nkeynes@1104
   214
            flags->fillSizeBytes = 4;
nkeynes@1104
   215
            ioparse_apval( tok, &flags->fillValue, &flags->fillSizeBytes );
nkeynes@1104
   216
            break;
nkeynes@1104
   217
        case TOK_ACCESS:
nkeynes@1104
   218
            READ(TOK_EQUALS);
nkeynes@1104
   219
            READ(TOK_IDENTIFIER);
nkeynes@1104
   220
            flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES));
nkeynes@1104
   221
            if( flags->access == -1 ) {
nkeynes@1104
   222
                YYPARSE_ERROR("Unknown access mode '%s'", yystrdup());
nkeynes@1104
   223
            }
nkeynes@1104
   224
            break;
nkeynes@1104
   225
        case TOK_MASK:
nkeynes@1104
   226
            if( numBytes ) {
nkeynes@1104
   227
                READ(TOK_EQUALS);
nkeynes@1104
   228
                tok = iolex(TOK_INTEGER);
nkeynes@1104
   229
                ioparse_apval( tok, &flags->maskValue, numBytes );
nkeynes@1104
   230
            } else {
nkeynes@1104
   231
                YYPARSE_ERROR("mask is not valid on a register block",0);
nkeynes@1104
   232
            }
nkeynes@1104
   233
            break;
nkeynes@1104
   234
        case TOK_ENDIAN:
nkeynes@1104
   235
            READ(TOK_EQUALS);
nkeynes@1104
   236
            READ(TOK_IDENTIFIER);
nkeynes@1104
   237
            flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES));
nkeynes@1104
   238
            if( flags->endian == -1 ) {
nkeynes@1104
   239
                YYPARSE_ERROR("Unknown endianness '%s'", yystrdup());
nkeynes@1104
   240
            }
nkeynes@1104
   241
            break;
nkeynes@1104
   242
        case TOK_TRACE:
nkeynes@1104
   243
            READ(TOK_EQUALS);
nkeynes@1104
   244
            READ(TOK_IDENTIFIER);
nkeynes@1104
   245
            flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
nkeynes@1104
   246
            if( flags->traceFlag == -1 ) {
nkeynes@1104
   247
                YYPARSE_ERROR("Unknown trace flag '%s'", yystrdup());
nkeynes@1104
   248
            }
nkeynes@1104
   249
            break;
nkeynes@1104
   250
        case TOK_TEST:
nkeynes@1104
   251
            READ(TOK_EQUALS);
nkeynes@1104
   252
            READ(TOK_IDENTIFIER);
nkeynes@1104
   253
            flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
nkeynes@1104
   254
            if( flags->testFlag == -1 ) {
nkeynes@1104
   255
                YYPARSE_ERROR("Unknown test flag '%s'", yystrdup());
nkeynes@1104
   256
            }
nkeynes@1104
   257
            break;
nkeynes@1104
   258
        case TOK_COMMA:
nkeynes@1104
   259
            break;
nkeynes@1104
   260
        case TOK_RPAREN:
nkeynes@1104
   261
            return;
nkeynes@1104
   262
        default:
nkeynes@1104
   263
            YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]);
nkeynes@1104
   264
        }
nkeynes@1104
   265
    } while(1);
nkeynes@1104
   266
}
nkeynes@1104
   267
nkeynes@1104
   268
static void ioparse_regdef( struct regdef *reg )
nkeynes@1104
   269
{
nkeynes@1104
   270
    reg->offset = yytok.v.i;
nkeynes@1104
   271
    reg->numElements = 1;
nkeynes@1104
   272
    reg->numBytes = 0;
nkeynes@1104
   273
    reg->initValue.i = 0;
nkeynes@1104
   274
    reg->flags.maskValue.i = -1;
nkeynes@1104
   275
    unsigned rangeOffset = reg->offset;
nkeynes@1104
   276
nkeynes@1104
   277
    int tok = iolex(TOK_COLON);
nkeynes@1104
   278
    if( tok == TOK_RANGE ) {
nkeynes@1104
   279
        READ(TOK_INTEGER);
nkeynes@1104
   280
        rangeOffset = yytok.v.i;
nkeynes@1104
   281
        if( rangeOffset < reg->offset ) {
nkeynes@1104
   282
            YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset );
nkeynes@1104
   283
        }
nkeynes@1104
   284
        READ(TOK_COLON);
nkeynes@1104
   285
    } else if( tok != TOK_COLON ) {
nkeynes@1104
   286
        YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   287
    }
nkeynes@1104
   288
    READ(TOK_IDENTIFIER);
nkeynes@1104
   289
    reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES));
nkeynes@1104
   290
    if( reg->mode == -1 ) {
nkeynes@1104
   291
        YYPARSE_ERROR( "Unknown register mode '%s'", yystrdup() );
nkeynes@1104
   292
    }
nkeynes@1104
   293
    if( reg->mode == REG_MIRROR ) {
nkeynes@1104
   294
        /* Mirror regions have a target range only */
nkeynes@1104
   295
        READ(TOK_INTEGER);
nkeynes@1104
   296
        reg->initValue.i = yytok.v.i;
nkeynes@1104
   297
        reg->type = REG_I8;
nkeynes@1104
   298
        tok = iolex(TOK_RANGE);
nkeynes@1104
   299
        if( tok == TOK_RANGE ) {
nkeynes@1104
   300
            READ(TOK_INTEGER);
nkeynes@1104
   301
            if( yytok.v.i < reg->initValue.i ) {
nkeynes@1104
   302
                YYPARSE_ERROR( "Invalid mirror target range 0x%x..0x%x", reg->initValue.i, yytok.v.i );
nkeynes@1104
   303
            }
nkeynes@1104
   304
            reg->numBytes = yytok.v.i - reg->initValue.i + 1;
nkeynes@1104
   305
            tok = iolex(TOK_STRIDE);
nkeynes@1104
   306
        }
nkeynes@1104
   307
        if( tok == TOK_STRIDE ) {
nkeynes@1104
   308
            READ(TOK_INTEGER);
nkeynes@1104
   309
            reg->stride = yytok.v.i;
nkeynes@1104
   310
            tok = iolex(TOK_ACTION);
nkeynes@1104
   311
        } else {
nkeynes@1104
   312
            reg->stride = reg->numBytes;
nkeynes@1104
   313
        }
nkeynes@1104
   314
nkeynes@1104
   315
        if( tok != TOK_SEMI ) {
nkeynes@1104
   316
            YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] );
nkeynes@1104
   317
        }
nkeynes@1104
   318
nkeynes@1104
   319
        if( reg->stride < reg->numBytes ) {
nkeynes@1104
   320
            YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes );
nkeynes@1104
   321
        }
nkeynes@1104
   322
nkeynes@1104
   323
        if( rangeOffset != reg->offset ) {
nkeynes@1104
   324
            reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride;
nkeynes@1104
   325
        }
nkeynes@1104
   326
nkeynes@1104
   327
    } else {
nkeynes@1104
   328
        READ(TOK_IDENTIFIER);
nkeynes@1104
   329
        reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES));
nkeynes@1104
   330
        if( reg->type == -1 ) {
nkeynes@1104
   331
            YYPARSE_ERROR( "Unknown register type '%s'", yystrdup() );
nkeynes@1104
   332
        }
nkeynes@1104
   333
        reg->numBytes = TYPE_SIZES[reg->type];
nkeynes@1104
   334
        tok = iolex(TOK_IDENTIFIER);
nkeynes@1104
   335
        if( tok == TOK_IDENTIFIER ) {
nkeynes@1104
   336
            reg->name = yystrdup();
nkeynes@1104
   337
            tok = iolex(TOK_ACTION);
nkeynes@1104
   338
        }
nkeynes@1104
   339
        if( tok == TOK_STRING ) {
nkeynes@1104
   340
            reg->description = yytok.v.s;
nkeynes@1104
   341
            tok = iolex(TOK_ACTION);
nkeynes@1104
   342
        }
nkeynes@1104
   343
        if( tok == TOK_LPAREN ) {
nkeynes@1104
   344
            ioparse_regflags(&reg->flags, &reg->numBytes);
nkeynes@1104
   345
            tok = iolex(TOK_ACTION);
nkeynes@1104
   346
        }
nkeynes@1104
   347
        if( tok == TOK_EQUALS ) {
nkeynes@1104
   348
            tok = iolex(TOK_INTEGER);
nkeynes@1104
   349
            if( tok == TOK_UNDEFINED ) {
nkeynes@1104
   350
                reg->initValue.i = 0xDEADBEEFDEADBEEF;
nkeynes@1104
   351
                reg->initUndefined = TRUE;
nkeynes@1104
   352
            } else {
nkeynes@1104
   353
                ioparse_apval(tok, &reg->initValue, &reg->numBytes);
nkeynes@1104
   354
            }
nkeynes@1104
   355
            tok = iolex(TOK_ACTION);
nkeynes@1104
   356
        } else if( reg->type == REG_STRING ) {
nkeynes@1104
   357
            YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')",0 );
nkeynes@1104
   358
        }
nkeynes@1104
   359
        if( tok == TOK_ACTION ) {
nkeynes@1104
   360
            // reg->action = yystrdup();
nkeynes@1104
   361
        } else if( tok != TOK_SEMI ) {
nkeynes@1104
   362
            YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] );
nkeynes@1104
   363
        }
nkeynes@1104
   364
nkeynes@1104
   365
        if( rangeOffset != reg->offset ) {
nkeynes@1104
   366
            reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes;
nkeynes@1104
   367
        }
nkeynes@1104
   368
    }
nkeynes@1104
   369
nkeynes@1104
   370
}
nkeynes@1104
   371
nkeynes@1104
   372
static struct regblock *ioparse_regblock( )
nkeynes@1104
   373
{
nkeynes@1104
   374
    unsigned regsAllocSize = 128;
nkeynes@1104
   375
    struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
nkeynes@1104
   376
    block->numRegs = 0;
nkeynes@1104
   377
nkeynes@1104
   378
    READ(TOK_IDENTIFIER);
nkeynes@1104
   379
    block->name = yystrdup();
nkeynes@1104
   380
    int tok = iolex(TOK_AT);
nkeynes@1104
   381
    if( tok == TOK_STRING ) {
nkeynes@1104
   382
        block->description = yytok.v.s;
nkeynes@1104
   383
        tok = iolex(TOK_AT);
nkeynes@1104
   384
    }
nkeynes@1104
   385
    if( tok != TOK_AT ) {
nkeynes@1104
   386
        YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   387
    }
nkeynes@1104
   388
    READ(TOK_INTEGER);
nkeynes@1104
   389
    block->address = yytok.v.i;
nkeynes@1104
   390
nkeynes@1104
   391
    tok = iolex(TOK_LBRACE);
nkeynes@1104
   392
    if( tok == TOK_LPAREN) {
nkeynes@1104
   393
        ioparse_regflags(&block->flags, NULL);
nkeynes@1104
   394
        READ(TOK_LBRACE);
nkeynes@1104
   395
    } else if( tok != TOK_LBRACE ) {
nkeynes@1104
   396
        YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   397
    }
nkeynes@1104
   398
nkeynes@1104
   399
    tok = iolex(TOK_INTEGER);
nkeynes@1104
   400
    while( tok != TOK_RBRACE ) {
nkeynes@1104
   401
        if( tok != TOK_INTEGER ) {
nkeynes@1104
   402
            YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]);
nkeynes@1104
   403
        }
nkeynes@1104
   404
        struct regdef *regdef = g_malloc0(sizeof(struct regdef));
nkeynes@1104
   405
        if( block->numRegs >= regsAllocSize ) {
nkeynes@1104
   406
            regsAllocSize *= 2;
nkeynes@1104
   407
            block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
nkeynes@1104
   408
        }
nkeynes@1104
   409
        block->regs[block->numRegs++] = regdef;
nkeynes@1104
   410
        ioparse_regdef(regdef);
nkeynes@1104
   411
nkeynes@1104
   412
        tok = iolex(TOK_INTEGER);
nkeynes@1104
   413
    }
nkeynes@1104
   414
nkeynes@1104
   415
    qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb );
nkeynes@1104
   416
nkeynes@1104
   417
    return block;
nkeynes@1104
   418
}
nkeynes@1104
   419
nkeynes@1104
   420
GList *ioparse( const char *filename, GList *list )
nkeynes@1104
   421
{
nkeynes@1104
   422
    GList *blocks = list;
nkeynes@1104
   423
    int count = 0;
nkeynes@1104
   424
nkeynes@1104
   425
    if( iolex_open(filename) != 0  )
nkeynes@1104
   426
        return blocks;
nkeynes@1104
   427
nkeynes@1104
   428
    int tok;
nkeynes@1104
   429
    do {
nkeynes@1104
   430
        tok = iolex(TOK_REGISTERS);
nkeynes@1104
   431
        if( tok == TOK_EOF ) {
nkeynes@1104
   432
            return blocks;
nkeynes@1104
   433
        } else if( tok == TOK_INCLUDE) {
nkeynes@1104
   434
            READ(TOK_STRING);
nkeynes@1104
   435
nkeynes@1104
   436
        } else if( tok == TOK_SPACE ) {
nkeynes@1104
   437
        } else if( tok == TOK_REGISTERS ) {
nkeynes@1104
   438
            struct regblock *block = ioparse_regblock(block);
nkeynes@1104
   439
            count++;
nkeynes@1104
   440
            blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb);
nkeynes@1104
   441
        } else {
nkeynes@1104
   442
            YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   443
        }
nkeynes@1104
   444
    } while( tok != TOK_EOF );
nkeynes@1104
   445
nkeynes@1104
   446
    iolex_close();
nkeynes@1104
   447
    if( count == 0 ) {
nkeynes@1104
   448
        fprintf( stderr, "Warning: input file '%s' did not contain any register definitions\n" );
nkeynes@1104
   449
    }
nkeynes@1104
   450
nkeynes@1104
   451
    return blocks;
nkeynes@1104
   452
}
nkeynes@1104
   453
nkeynes@1104
   454
/**************************** Lexical analyser ***************************/
nkeynes@1104
   455
nkeynes@1104
   456
static int iolex_open( const char *filename )
nkeynes@1104
   457
{
nkeynes@1104
   458
    struct stat st;
nkeynes@1104
   459
    int fd = open( filename, O_RDONLY );
nkeynes@1104
   460
    if( fd == -1 ) {
nkeynes@1104
   461
        fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) );
nkeynes@1104
   462
        return -1;
nkeynes@1104
   463
    }
nkeynes@1104
   464
nkeynes@1104
   465
    if( fstat( fd, &st ) != 0 ) {
nkeynes@1104
   466
        fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) );
nkeynes@1104
   467
        close(fd);
nkeynes@1104
   468
        return -1;
nkeynes@1104
   469
    }
nkeynes@1104
   470
nkeynes@1104
   471
    char *data = g_malloc( st.st_size + 1 );
nkeynes@1104
   472
    if( read(fd, data, st.st_size) != st.st_size ) {
nkeynes@1104
   473
        fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) );
nkeynes@1104
   474
        close(fd);
nkeynes@1104
   475
        return -1;
nkeynes@1104
   476
    }
nkeynes@1104
   477
    close(fd);
nkeynes@1104
   478
    data[st.st_size] = 0;
nkeynes@1104
   479
nkeynes@1104
   480
    yybuffer = yyposn = data;
nkeynes@1104
   481
    yyend = data + st.st_size;
nkeynes@1104
   482
    yyline = 1;
nkeynes@1104
   483
    yylineposn = yyposn;
nkeynes@1104
   484
    return 0;
nkeynes@1104
   485
}
nkeynes@1104
   486
nkeynes@1104
   487
static void iolex_close()
nkeynes@1104
   488
{
nkeynes@1104
   489
    g_free(yybuffer);
nkeynes@1104
   490
    yybuffer = yyend = NULL;
nkeynes@1104
   491
}
nkeynes@1104
   492
nkeynes@1104
   493
#define YYRETURN(x) do{ \
nkeynes@1104
   494
    yytok.yylength = yyposn - yystart; \
nkeynes@1104
   495
    return (x); \
nkeynes@1104
   496
} while(0)
nkeynes@1104
   497
nkeynes@1104
   498
static int iolex_readhex(char *p, int digits)
nkeynes@1104
   499
{
nkeynes@1104
   500
    int result = 0;
nkeynes@1104
   501
    for( int i=0; i < digits; i++ ) {
nkeynes@1104
   502
        result <<= 4;
nkeynes@1104
   503
        if( isdigit(p[i]) ) {
nkeynes@1104
   504
            result += p[i]-'0';
nkeynes@1104
   505
        } else if( p[i] >= 'a' && p[i] <= 'f' ) {
nkeynes@1104
   506
            result += p[i]-'a'+10;
nkeynes@1104
   507
        } else if( p[i] >= 'A' && p[i] <= 'F' ) {
nkeynes@1104
   508
            result += p[i]-'A'+10;
nkeynes@1104
   509
        } else {
nkeynes@1104
   510
            return (result >> 4);
nkeynes@1104
   511
        }
nkeynes@1104
   512
nkeynes@1104
   513
    }
nkeynes@1104
   514
    return result;
nkeynes@1104
   515
}
nkeynes@1104
   516
nkeynes@1104
   517
static int iolex_readoctal(char *p, int digits)
nkeynes@1104
   518
{
nkeynes@1104
   519
    int result = 0;
nkeynes@1104
   520
    for( int i=0; i < digits; i++ ) {
nkeynes@1104
   521
        result <<= 3;
nkeynes@1104
   522
        if( p[i] >= '0' && p[i] <= '7' ) {
nkeynes@1104
   523
            result += p[i]-'0';
nkeynes@1104
   524
        } else {
nkeynes@1104
   525
            return (result >> 4);
nkeynes@1104
   526
        }
nkeynes@1104
   527
    }
nkeynes@1104
   528
    return result;
nkeynes@1104
   529
}
nkeynes@1104
   530
nkeynes@1104
   531
/**
nkeynes@1104
   532
 * Copy and interpret the string segment as a C string, replacing escape
nkeynes@1104
   533
 * expressions with the corresponding value.
nkeynes@1104
   534
 */
nkeynes@1104
   535
static char *iolex_getcstring( char *start, char *end, int *len )
nkeynes@1104
   536
{
nkeynes@1104
   537
   char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */
nkeynes@1104
   538
   char *q = result;
nkeynes@1104
   539
nkeynes@1104
   540
   for( char *p = start; p != end; p++ ) {
nkeynes@1104
   541
       if( *p == '\\' ) {
nkeynes@1104
   542
           if( ++p == end ) {
nkeynes@1104
   543
               *q++ = '\\';
nkeynes@1104
   544
               break;
nkeynes@1104
   545
           }
nkeynes@1104
   546
           if( p[0] >= '0' && p[0] <= '3' && p+3 <= end &&
nkeynes@1104
   547
               p[1] >= '0' && p[1] <= '7' &&
nkeynes@1104
   548
               p[2] >= '0' && p[2] <= '7' ) {
nkeynes@1104
   549
               *q++ = (char)iolex_readoctal(p,3);
nkeynes@1104
   550
               p+=2;
nkeynes@1104
   551
           } else {
nkeynes@1104
   552
               switch( *p ) {
nkeynes@1104
   553
               case 'n':
nkeynes@1104
   554
                   *q++ = '\n';
nkeynes@1104
   555
                   break;
nkeynes@1104
   556
               case 'r':
nkeynes@1104
   557
                   *q++ = '\r';
nkeynes@1104
   558
                   break;
nkeynes@1104
   559
               case 't':
nkeynes@1104
   560
                   *q++ = '\t';
nkeynes@1104
   561
                   break;
nkeynes@1104
   562
               case 'x': /* hex */
nkeynes@1104
   563
                   if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) {
nkeynes@1104
   564
                       *q++ = '\\';
nkeynes@1104
   565
                       *q++ = *p;
nkeynes@1104
   566
                   } else {
nkeynes@1104
   567
                       *q++ = (char)iolex_readhex(p+1, 2);
nkeynes@1104
   568
                       p+=2;
nkeynes@1104
   569
                   }
nkeynes@1104
   570
                   break;
nkeynes@1104
   571
               default:
nkeynes@1104
   572
                   *q++ = '\\';
nkeynes@1104
   573
                   *q++ = *p;
nkeynes@1104
   574
               }
nkeynes@1104
   575
           }
nkeynes@1104
   576
       } else {
nkeynes@1104
   577
           *q++ = *p;
nkeynes@1104
   578
       }
nkeynes@1104
   579
   }
nkeynes@1104
   580
   *len = q - result;
nkeynes@1104
   581
   return result;
nkeynes@1104
   582
}
nkeynes@1104
   583
nkeynes@1104
   584
int iolex( int expectToken )
nkeynes@1104
   585
{
nkeynes@1104
   586
    int count = 0;
nkeynes@1104
   587
    while( yyposn < yyend ) {
nkeynes@1104
   588
        char *yystart = yytok.yytext = yyposn;
nkeynes@1104
   589
        yytok.yylength = 1;
nkeynes@1104
   590
        yytok.yyline = yyline;
nkeynes@1104
   591
        yytok.yycol = yyposn - yylineposn+1;
nkeynes@1104
   592
        int ch = *yyposn++;
nkeynes@1104
   593
        if( isdigit(ch) ) {
nkeynes@1104
   594
            /* INTEGER */
nkeynes@1104
   595
            if( ch == '0' ) {
nkeynes@1104
   596
                if( *yyposn == 'x' ) {
nkeynes@1104
   597
                    while( yyposn < yyend && isxdigit(*++yyposn) ) ;
nkeynes@1104
   598
                } else {
nkeynes@1104
   599
                    while( yyposn < yyend && *yyposn >= '0' && *yyposn <= '7' )
nkeynes@1104
   600
                        yyposn++;
nkeynes@1104
   601
                }
nkeynes@1104
   602
            } else {
nkeynes@1104
   603
                while( yyposn < yyend && isdigit(*yyposn) )
nkeynes@1104
   604
                    yyposn++;
nkeynes@1104
   605
            }
nkeynes@1104
   606
            yytok.v.i = strtol( yystart, NULL, 0 );
nkeynes@1104
   607
            YYRETURN(TOK_INTEGER);
nkeynes@1104
   608
        } else if( isalpha(ch) || ch == '_' ) {
nkeynes@1104
   609
            /* IDENTIFIER */
nkeynes@1104
   610
            while( yyposn < yyend && (isalnum(*yyposn) || *yyposn == '_') )
nkeynes@1104
   611
                yyposn++;
nkeynes@1104
   612
            yytok.yylength = yyposn - yystart;
nkeynes@1104
   613
            if( expectToken == TOK_IDENTIFIER ) {
nkeynes@1104
   614
                YYRETURN(TOK_IDENTIFIER);
nkeynes@1104
   615
            }
nkeynes@1104
   616
            /* Otherwise check for keywords */
nkeynes@1104
   617
            for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) {
nkeynes@1104
   618
                if( strlen(TOKEN_NAMES[i]) == yytok.yylength &&
nkeynes@1104
   619
                    strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) {
nkeynes@1104
   620
                    YYRETURN(i);
nkeynes@1104
   621
                }
nkeynes@1104
   622
            }
nkeynes@1104
   623
            YYRETURN(TOK_IDENTIFIER);
nkeynes@1104
   624
        } else if( isspace(ch) ) {
nkeynes@1104
   625
            if( ch == '\n' ) {
nkeynes@1104
   626
                yyline++;
nkeynes@1104
   627
                yylineposn = yyposn;
nkeynes@1104
   628
            }
nkeynes@1104
   629
            while( isspace(*yyposn) ) {
nkeynes@1104
   630
                if( *yyposn == '\n' ) {
nkeynes@1104
   631
                    yyline++;
nkeynes@1104
   632
                    yylineposn = yyposn+1;
nkeynes@1104
   633
                }
nkeynes@1104
   634
                yyposn++;
nkeynes@1104
   635
            }
nkeynes@1104
   636
        } else {
nkeynes@1104
   637
            switch( ch ) {
nkeynes@1104
   638
            case '(': YYRETURN(TOK_LPAREN);
nkeynes@1104
   639
            case ')': YYRETURN(TOK_RPAREN);
nkeynes@1104
   640
            case '[': YYRETURN(TOK_LSQUARE);
nkeynes@1104
   641
            case ']': YYRETURN(TOK_RSQUARE);
nkeynes@1104
   642
            case ',': YYRETURN(TOK_COMMA);
nkeynes@1104
   643
            case ':': YYRETURN(TOK_COLON);
nkeynes@1104
   644
            case ';': YYRETURN(TOK_SEMI);
nkeynes@1104
   645
            case '=': YYRETURN(TOK_EQUALS);
nkeynes@1104
   646
            case '/':
nkeynes@1104
   647
                if( *yyposn == '/' ) { /* Line comment */
nkeynes@1104
   648
                    while( yyposn < yyend && *++yyposn != '\n' ) ;
nkeynes@1104
   649
                } else if( *yyposn == '*' ) { /* C comment */
nkeynes@1104
   650
                    while( yyposn < yyend && (*++yyposn != '*' || *++yyposn != '/' ) ) {
nkeynes@1104
   651
                        if( *yyposn == '\n' ) {
nkeynes@1104
   652
                            yyline++;
nkeynes@1104
   653
                            yylineposn = yyposn+1;
nkeynes@1104
   654
                        }
nkeynes@1104
   655
                    }
nkeynes@1104
   656
                }
nkeynes@1104
   657
                break;
nkeynes@1104
   658
            case '\'': /* STRING */
nkeynes@1104
   659
                while( *yyposn != '\'' ) {
nkeynes@1104
   660
                    if( *yyposn == '\n' ) {
nkeynes@1104
   661
                        fprintf( stderr, "Unexpected newline in string constant!\n" );
nkeynes@1104
   662
                        YYRETURN(TOK_ERROR);
nkeynes@1104
   663
                    } else if( yyposn >= yyend ) {
nkeynes@1104
   664
                        fprintf( stderr, "Unexpected EOF in string constant!\n" );
nkeynes@1104
   665
                        YYRETURN(TOK_ERROR);
nkeynes@1104
   666
                    } else if( *yyposn == '\\' && yyposn[1] == '\'' ) {
nkeynes@1104
   667
                        yyposn++;
nkeynes@1104
   668
                    }
nkeynes@1104
   669
                    yyposn++;
nkeynes@1104
   670
                }
nkeynes@1104
   671
                yyposn++;
nkeynes@1104
   672
                yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen);
nkeynes@1104
   673
                YYRETURN(TOK_STRING);
nkeynes@1104
   674
            case '\"': /* STRING */
nkeynes@1104
   675
                while( *yyposn != '\"' ) {
nkeynes@1104
   676
                    if( *yyposn == '\n' ) {
nkeynes@1104
   677
                        fprintf( stderr, "Unexpected newline in string constant!\n" );
nkeynes@1104
   678
                        YYRETURN(TOK_ERROR);
nkeynes@1104
   679
                    } else if( yyposn >= yyend ) {
nkeynes@1104
   680
                        fprintf( stderr, "Unexpected EOF in string constant!\n" );
nkeynes@1104
   681
                        YYRETURN(TOK_ERROR);
nkeynes@1104
   682
                    } else if( *yyposn == '\\' && yyposn[1] == '\"' ) {
nkeynes@1104
   683
                        yyposn++;
nkeynes@1104
   684
                    }
nkeynes@1104
   685
                    yyposn++;
nkeynes@1104
   686
                }
nkeynes@1104
   687
                yyposn++;
nkeynes@1104
   688
                yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen);
nkeynes@1104
   689
                YYRETURN(TOK_STRING);
nkeynes@1104
   690
            case '}':
nkeynes@1104
   691
                YYRETURN(TOK_RBRACE);
nkeynes@1104
   692
            case '{': /* ACTION or LBRACE */
nkeynes@1104
   693
                if( expectToken == TOK_LBRACE ) {
nkeynes@1104
   694
                    YYRETURN(TOK_LBRACE);
nkeynes@1104
   695
                } else {
nkeynes@1104
   696
                    count++;
nkeynes@1104
   697
                    while( count > 0 && yyposn < yyend ) {
nkeynes@1104
   698
                        if( *yyposn == '{' )
nkeynes@1104
   699
                            count++;
nkeynes@1104
   700
                        if( *yyposn == '}' )
nkeynes@1104
   701
                            count--;
nkeynes@1104
   702
                        yyposn++;
nkeynes@1104
   703
                    }
nkeynes@1104
   704
                    YYRETURN(TOK_ACTION);
nkeynes@1104
   705
                }
nkeynes@1104
   706
            case '.':
nkeynes@1104
   707
                if( *yyposn == '.' ) {
nkeynes@1104
   708
                    yyposn++;
nkeynes@1104
   709
                    YYRETURN(TOK_RANGE);
nkeynes@1104
   710
                } else {
nkeynes@1104
   711
                    YYRETURN(TOK_PERIOD);
nkeynes@1104
   712
                }
nkeynes@1104
   713
            default:
nkeynes@1104
   714
                fprintf( stderr, "Illegal character: '%c'\n", ch );
nkeynes@1104
   715
                YYRETURN(TOK_ERROR);
nkeynes@1104
   716
            }
nkeynes@1104
   717
        }
nkeynes@1104
   718
nkeynes@1104
   719
    }
nkeynes@1104
   720
    return TOK_EOF;
nkeynes@1104
   721
}
.