summaryrefslogtreecommitdiff
path: root/apps/plugins/greyscale.c
blob: 73dd98b21684d78057228fc95e77e36cecdac346 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/***************************************************************************
*             __________               __   ___.
*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
*                     \/            \/     \/    \/            \/
* $Id$
*
* Greyscale demo plugin
*
* Copyright (C) 2004-2008 Jens Arnold
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/

#include "plugin.h"
#include "lib/helper.h" 

#if defined(HAVE_LCD_BITMAP) && (LCD_DEPTH < 4)
#include "lib/grey.h"

PLUGIN_HEADER

/* variable button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
#define GREYSCALE_SHIFT BUTTON_ON
#define GREYSCALE_UP BUTTON_UP
#define GREYSCALE_DOWN BUTTON_DOWN
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_OFF

#elif CONFIG_KEYPAD == ONDIO_PAD
#define GREYSCALE_SHIFT BUTTON_MENU
#define GREYSCALE_UP BUTTON_UP
#define GREYSCALE_DOWN BUTTON_DOWN
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_OFF

#elif CONFIG_KEYPAD == IRIVER_H100_PAD
#define GREYSCALE_SHIFT BUTTON_ON
#define GREYSCALE_UP BUTTON_UP
#define GREYSCALE_DOWN BUTTON_DOWN
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_OFF

#define GREYSCALE_RC_OFF BUTTON_RC_STOP

#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) || (CONFIG_KEYPAD == MROBE100_PAD)
#define GREYSCALE_SHIFT BUTTON_PLAY /* won't work, but define it anyway */
#define GREYSCALE_UP BUTTON_UP
#define GREYSCALE_DOWN BUTTON_DOWN
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_POWER

#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
      (CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define GREYSCALE_SHIFT BUTTON_SELECT
#define GREYSCALE_UP BUTTON_SCROLL_BACK
#define GREYSCALE_DOWN BUTTON_SCROLL_FWD
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_MENU

#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
#define GREYSCALE_SHIFT BUTTON_PLAY
#define GREYSCALE_UP BUTTON_UP
#define GREYSCALE_DOWN BUTTON_DOWN
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_EQ

#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define GREYSCALE_SHIFT BUTTON_RC_PLAY /* somewhat dangerous... */
#define GREYSCALE_UP BUTTON_RC_VOL_UP
#define GREYSCALE_DOWN BUTTON_RC_VOL_DOWN
#define GREYSCALE_LEFT BUTTON_RC_REW
#define GREYSCALE_RIGHT BUTTON_RC_FF
#define GREYSCALE_OFF BUTTON_RC_REC

#define GREYSCALE_RC_OFF BUTTON_REC

#elif CONFIG_KEYPAD == SANSA_CLIP_PAD
#define GREYSCALE_SHIFT BUTTON_SELECT
#define GREYSCALE_UP BUTTON_UP
#define GREYSCALE_DOWN BUTTON_DOWN
#define GREYSCALE_LEFT BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF BUTTON_POWER

#elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
#define GREYSCALE_SHIFT BUTTON_FFWD
#define GREYSCALE_UP    BUTTON_UP
#define GREYSCALE_DOWN  BUTTON_DOWN
#define GREYSCALE_LEFT  BUTTON_LEFT
#define GREYSCALE_RIGHT BUTTON_RIGHT
#define GREYSCALE_OFF   BUTTON_PLAY

#endif

#define GFX_HEIGHT (LCD_HEIGHT-8)
#if LCD_WIDTH < 160
#define GFX_GREYTONE_WIDTH 86
#define GFX_GREYTONE_STEP 3
#else
#define GFX_GREYTONE_WIDTH 128
#define GFX_GREYTONE_STEP 2
#endif
/******************************* Globals ***********************************/

GREY_INFO_STRUCT
static char pbuf[32];         /* global printf buffer */
static unsigned char *gbuf;
static size_t gbuf_size = 0;

/**************************** main function ********************************/

void cleanup(void *parameter)
{
    (void)parameter;

    grey_release(); /* switch off overlay and deinitialize */
    /* Turn on backlight timeout (revert to settings) */
    backlight_use_settings(); /* backlight control in lib/helper.c */
}

/* this is only a demo of what the framework can do */
int main(void)
{
    int time;
    int x, y, i;
    int button, scroll_amount;
    bool black_border = false;

    static const unsigned char rockbox[] = {
    /* ...........................................
     * .####...###...###..#...#.####...###..#...#.
     * .#...#.#...#.#...#.#..#..#...#.#...#..#.#..
     * .####..#...#.#.....###...####..#...#...#...
     * .#..#..#...#.#...#.#..#..#...#.#...#..#.#..
     * .#...#..###...###..#...#.####...###..#...#.
     * ...........................................
     * 43 x 7 pixel, 1 bpp
     */
       0x00, 0x3E, 0x0A, 0x0A, 0x1A, 0x24, 0x00, 0x1C, 0x22, 0x22,
       0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x14, 0x00, 0x3E,
       0x08, 0x08, 0x14, 0x22, 0x00, 0x3E, 0x2A, 0x2A, 0x2A, 0x14,
       0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x22, 0x14, 0x08,
       0x14, 0x22, 0x00
    };
    
    static const unsigned char showing[] = {
    /* .......................................
     * ..####.#...#..###..#...#.#.#...#..####.
     * .#.....#...#.#...#.#...#.#.##..#.#.....
     * ..###..#####.#...#.#.#.#.#.#.#.#.#..##.
     * .....#.#...#.#...#.#.#.#.#.#..##.#...#.
     * .####..#...#..###...#.#..#.#...#..####.
     * .......................................
     * 39 x 7 pixel, 1 bpp
     */
       0x00, 0x24, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x3E, 0x08, 0x08,
       0x08, 0x3E, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x1E,
       0x20, 0x18, 0x20, 0x1E, 0x00, 0x3E, 0x00, 0x3E, 0x04, 0x08,
       0x10, 0x3E, 0x00, 0x1C, 0x22, 0x22, 0x2A, 0x3A, 0x00
    };
    
    static const unsigned char grayscale_grey[] = {
    /* .......................................................
     * ..####.####...###..#...#..####..###...###..#.....#####.
     * .#.....#...#.#...#.#...#.#.....#...#.#...#.#.....#.....
     * .#..##.####..#####..#.#...###..#.....#####.#.....####..
     * .#...#.#..#..#...#...#.......#.#...#.#...#.#.....#.....
     * ..####.#...#.#...#...#...####...###..#...#.#####.#####.
     * .......................................................
     * 55 x 7 pixel, 8 bpp
     */
       110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
       110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
       110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
       110,110,110,110,110,110,110,
       120,120, 20, 20, 20, 20,120,222,222,222,222,120,120,120, 24, 24,
        24,120,120,226,120,120,120,226,120,120, 28, 28, 28, 28,120,120,
       230,230,230,120,120,120, 32, 32, 32,120,120,234,120,120,120,120,
       120, 36, 36, 36, 36, 36,120,
       130, 20,130,130,130,130,130,222,130,130,130,222,130, 24,130,130,
       130, 24,130,226,130,130,130,226,130, 28,130,130,130,130,130,230,
       130,130,130,230,130, 32,130,130,130, 32,130,234,130,130,130,130,
       130, 36,130,130,130,130,130,
       140, 20,140,140, 20, 20,140,222,222,222,222,140,140, 24, 24, 24,
        24, 24,140,140,226,140,226,140,140,140, 28, 28, 28,140,140,230,
       140,140,140,140,140, 32, 32, 32, 32, 32,140,234,140,140,140,140,
       140, 36, 36, 36, 36,140,140,
       130, 20,130,130,130, 20,130,222,130,130,222,130,130, 24,130,130,
       130, 24,130,130,130,226,130,130,130,130,130,130,130, 28,130,230,
       130,130,130,230,130, 32,130,130,130, 32,130,234,130,130,130,130,
       130, 36,130,130,130,130,130,
       120,120, 20, 20, 20, 20,120,222,120,120,120,222,120, 24,120,120,
       120, 24,120,120,120,226,120,120,120, 28, 28, 28, 28,120,120,120,
       230,230,230,120,120, 32,120,120,120, 32,120,234,234,234,234,234,
       120, 36, 36, 36, 36, 36,120,
       110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
       110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
       110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
       110,110,110,110,110,110,110
    };

    /* Turn off backlight timeout */
    backlight_force_on(); /* backlight control in lib/helper.c */

    rb->lcd_setfont(FONT_SYSFIXED);   /* select default font */

    /* get the remainder of the plugin buffer */
    gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size);

    /* initialize the greyscale buffer:
       Archos: 112 pixels wide, 7 rows (56 pixels) high.
       H1x0: 160 pixels wide, 30 rows (120 pixels) high. */
    if (!grey_init(gbuf, gbuf_size, GREY_BUFFERED|GREY_ON_COP,
                   LCD_WIDTH, GFX_HEIGHT, NULL))
    {
        rb->splash(HZ, "Not enough memory.");
        return PLUGIN_ERROR;
    }

    /* place greyscale overlay 1 row down */
    grey_set_position(0, 8);

    rb->lcd_puts(0, 0, "Shades: 129");
    rb->lcd_update();

#ifdef HAVE_ADJUSTABLE_CPU_FREQ
    rb->cpu_boost(true);
#endif
    grey_show(true);          /* switch on greyscale overlay */

    time = *rb->current_tick; /* start time measurement */

    grey_set_background(150);
    grey_clear_display();     /* fill everything with grey 150 */

    /* draw a dark grey line star background */
    grey_set_foreground(80);
    for (y = 0; y < GFX_HEIGHT; y += 8)                   /* horizontal part */
    {
        grey_drawline(0, y, (LCD_WIDTH-1), (GFX_HEIGHT-1) - y); /*grey lines */
    }
    for (x = 10; x <= LCD_WIDTH; x += 10)                    /* vertical part */
    {
        grey_drawline(x, 0, (LCD_WIDTH-1) - x, (GFX_HEIGHT-1)); /*grey lines */
    }

    grey_set_foreground(0);
    grey_drawrect(0, 0, LCD_WIDTH, GFX_HEIGHT);   /* black border */

    /* draw grey tones */
    for (i = 0; i < GFX_GREYTONE_WIDTH; i++)
    {
        x = ((LCD_WIDTH-GFX_GREYTONE_WIDTH)/2) + i;
        grey_set_foreground(GFX_GREYTONE_STEP * i);
        /* vertical lines */
        grey_vline(x, (GFX_HEIGHT/8), (GFX_HEIGHT-GFX_HEIGHT/8-1));
    }

    grey_set_drawmode(DRMODE_COMPLEMENT);
    /* invert rectangle (lower half) */
    grey_fillrect((LCD_WIDTH-GFX_GREYTONE_WIDTH)/2, (GFX_HEIGHT/2+1),
                  GFX_GREYTONE_WIDTH, (GFX_HEIGHT/2-GFX_HEIGHT/8-1));
    /* invert a line */
    grey_hline((LCD_WIDTH-GFX_GREYTONE_WIDTH)/2,
               (LCD_WIDTH+GFX_GREYTONE_WIDTH)/2, (GFX_HEIGHT/2-1));

    /* show bitmaps (1 bit and 8 bit) */
    /* opaque */
    grey_set_drawinfo(DRMODE_SOLID, 255, 100);
    grey_mono_bitmap(rockbox,
                     MAX((LCD_WIDTH/2-47), ((LCD_WIDTH-GFX_GREYTONE_WIDTH)/2)),
                     (5*GFX_HEIGHT/16-4), 43, 7);
    /* transparent */
    grey_set_drawinfo(DRMODE_FG, 0, 100);
    grey_mono_bitmap(showing, (LCD_WIDTH/2+4) , (5*GFX_HEIGHT/16-4), 39, 7);
    /* greyscale */
    grey_gray_bitmap(grayscale_grey, ((LCD_WIDTH-55)/2), (11*GFX_HEIGHT/16-4),
                     55, 7);

    grey_update();

    time = *rb->current_tick - time;  /* end time measurement */

    rb->snprintf(pbuf, sizeof(pbuf), "Shades: 129, %d.%02ds", 
                 time / 100, time % 100);
    rb->lcd_puts(0, 0, pbuf);
    grey_deferred_lcd_update();       /* schedule an lcd_update() */
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
    rb->cpu_boost(false);
#endif

    /* drawing is now finished, play around with scrolling 
     * until you press OFF or connect USB
     */
    grey_set_background(255);
    while (true)
    {
        scroll_amount = 1;

        button = rb->button_get(true);

        if (rb->default_event_handler_ex(button, cleanup, NULL) 
            == SYS_USB_CONNECTED)
            return PLUGIN_USB_CONNECTED;

        if (button & GREYSCALE_SHIFT)
        {
            if (!black_border)
            {
                grey_set_background(0);
                black_border = true;
            }
        }
        else
        {
            if (black_border)
            {
                grey_set_background(255);
                black_border = false;
            }
        }

        if (button & BUTTON_REPEAT)
            scroll_amount = 4;

        switch (button & ~(GREYSCALE_SHIFT | BUTTON_REPEAT))
        {
            case GREYSCALE_LEFT:

                grey_scroll_left(scroll_amount);  /* scroll left */
                grey_update();
                break;

            case GREYSCALE_RIGHT:

                grey_scroll_right(scroll_amount); /* scroll right */
                grey_update();
                break;

            case GREYSCALE_UP:

                grey_scroll_up(scroll_amount);    /* scroll up */
                grey_update();
                break;

            case GREYSCALE_DOWN:

                grey_scroll_down(scroll_amount);  /* scroll down */
                grey_update();
                break;
#ifdef GREYSCALE_RC_OFF
            case GREYSCALE_RC_OFF:
#endif
            case GREYSCALE_OFF:

                cleanup(NULL);
                return PLUGIN_OK;
        }
    }
}

/*************************** Plugin entry point ****************************/

enum plugin_status plugin_start(const void* parameter)
{
    (void)parameter;

    return main();
}

#endif /* #ifdef HAVE_LCD_BITMAP */

pan class="hl opt">) return parse_ascii_number(ctx, lexem); if(isdigit(c)) return parse_number(ctx, lexem); if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem); parse_error(ctx, "Unexpected character '%c'\n", c); #undef ret_simple } #if 0 static void log_lexem(struct lexem_t *lexem) { switch(lexem->type) { case LEX_EOF: printf("<eof>"); break; case LEX_EQUAL: printf("="); break; case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break; case LEX_LPAREN: printf("("); break; case LEX_RPAREN: printf(")"); break; case LEX_LBRACE: printf("{"); break; case LEX_RBRACE: printf("}"); break; case LEX_SEMICOLON: printf(";"); break; case LEX_NUMBER: printf("num(%d)", lexem->num); break; case LEX_STRING: printf("str(%s)", lexem->str); break; case LEX_OR: printf("|"); break; case LEX_LSHIFT: printf("<<"); break; default: printf("<unk>"); } } #endif struct cmd_option_t *db_add_opt(struct cmd_option_t **opt, const char *identifier, bool is_str) { while(*opt) opt = &(*opt)->next; *opt = xmalloc(sizeof(struct cmd_option_t)); memset(*opt, 0, sizeof(struct cmd_option_t)); (*opt)->name = strdup(identifier); (*opt)->is_string = is_str; return *opt; } void db_add_str_opt(struct cmd_option_t **opt, const char *name, const char *value) { db_add_opt(opt, name, true)->str = strdup(value); } void db_add_int_opt(struct cmd_option_t **opt, const char *name, uint32_t value) { db_add_opt(opt, name, false)->val = value; } static struct cmd_source_t *db_add_src(struct cmd_source_t **src, const char *identifier, bool is_extern) { while(*src) src = &(*src)->next; *src = xmalloc(sizeof(struct cmd_source_t)); memset(*src, 0, sizeof(struct cmd_source_t)); (*src)->identifier = strdup(identifier); (*src)->is_extern = is_extern; return *src; } void db_add_source(struct cmd_file_t *cmd_file, const char *identifier, const char *filename) { db_add_src(&cmd_file->source_list, identifier, false)->filename = strdup(filename); } void db_add_extern_source(struct cmd_file_t *cmd_file, const char *identifier, int extern_nr) { db_add_src(&cmd_file->source_list, identifier, true)->extern_nr = extern_nr; } static struct cmd_inst_t *db_add_inst(struct cmd_inst_t **list, enum cmd_inst_type_t type, uint32_t argument) { while(*list) list = &(*list)->next; *list = xmalloc(sizeof(struct cmd_inst_t)); memset(*list, 0, sizeof(struct cmd_inst_t)); (*list)->type = type; (*list)->argument = argument; return *list; } void db_add_inst_id(struct cmd_section_t *cmd_section, enum cmd_inst_type_t type, const char *identifier, uint32_t argument) { db_add_inst(&cmd_section->inst_list, type, argument)->identifier = strdup(identifier); } void db_add_inst_addr(struct cmd_section_t *cmd_section, enum cmd_inst_type_t type, uint32_t addr, uint32_t argument) { db_add_inst(&cmd_section->inst_list, type, argument)->addr = addr; } struct cmd_section_t *db_add_section(struct cmd_file_t *cmd_file, uint32_t identifier, bool data) { struct cmd_section_t **prev = &cmd_file->section_list; while(*prev) prev = &(*prev)->next; *prev = xmalloc(sizeof(struct cmd_section_t)); memset(*prev, 0, sizeof(struct cmd_section_t)); (*prev)->identifier = identifier; (*prev)->is_data = data; return *prev; } struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id) { struct cmd_source_t *src = cmd_file->source_list; while(src) { if(strcmp(src->identifier, id) == 0) return src; src = src->next; } return NULL; } struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name) { while(opt) { if(strcmp(opt->name, name) == 0) return opt; opt = opt->next; } return NULL; } #define INVALID_SB_SUBVERSION 0xffff static const char *parse_sb_subversion(const char *str, uint16_t *v) { int len = 0; *v = 0; while(isdigit(str[len]) && len < 3) *v = (*v) << 4 | (str[len++] - '0'); if(len == 0) *v = INVALID_SB_SUBVERSION; return str + len; } bool db_parse_sb_version(struct sb_version_t *ver, const char *str) { str = parse_sb_subversion(str, &ver->major); if(ver->major == INVALID_SB_SUBVERSION || *str != '.') return false; str = parse_sb_subversion(str + 1, &ver->minor); if(ver->minor == INVALID_SB_SUBVERSION || *str != '.') return false; str = parse_sb_subversion(str + 1, &ver->revision); if(ver->revision == INVALID_SB_SUBVERSION || *str != 0) return false; return true; } static bool db_generate_sb_subversion(uint16_t subver, char *str) { str[0] = '0' + ((subver >> 8) & 0xf); str[1] = '0' + ((subver >> 4) & 0xf); str[2] = '0' + (subver & 0xf); return true; } bool db_generate_sb_version(struct sb_version_t *ver, char *str, int size) { if(size < 12) return false; str[3] = '.'; str[7] = '.'; str[11] = 0; return db_generate_sb_subversion(ver->major, str) && db_generate_sb_subversion(ver->minor, str + 4) && db_generate_sb_subversion(ver->revision, str + 8); } #undef parse_error #define parse_error(lexem, ...) \ do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \ fprintf(stderr, __VA_ARGS__); exit(2); } while(0) struct lex_ctx_t { struct context_t ctx; struct lexem_t lexem; }; /* When lexems hold strings (like identifier), it might be useful to steal * the pointer and don't clean the lexem but in other case, one don't want * to keep the pointer to the string and just want to release the memory. * Thus clean_lexem should be true except when one keeps a pointer */ static inline void next(struct lex_ctx_t *ctx, bool clean_lexem) { if(clean_lexem) free(ctx->lexem.str); memset(&ctx->lexem, 0, sizeof(struct lexem_t)); next_lexem(&ctx->ctx, &ctx->lexem); } static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { uint32_t ret = 0; if(ctx->lexem.type == LEX_NUMBER) ret = ctx->lexem.num; else if(ctx->lexem.type == LEX_IDENTIFIER) { struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str); if(c == NULL) parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str); if(c->is_string) parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str); ret = c->val; } else parse_error(ctx->lexem, "Number or constant identifier expected\n"); next(ctx, true); return ret; } static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { uint32_t v = parse_term_expr(ctx, const_list); while(ctx->lexem.type == LEX_LSHIFT) { next(ctx, true); v <<= parse_term_expr(ctx, const_list); } return v; } static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { uint32_t v = parse_shift_expr(ctx, const_list); while(ctx->lexem.type == LEX_OR) { next(ctx, true); v |= parse_shift_expr(ctx, const_list); } return v; } static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) { return parse_or_expr(ctx, const_list); } #define NR_INITIAL_CONSTANTS 4 static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"}; static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0}; struct cmd_file_t *db_parse_file(const char *file) { size_t size; FILE *f = fopen(file, "r"); if(f == NULL) { if(g_debug) perror("Cannot open db file"); return NULL; } fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); char *buf = xmalloc(size); if(fread(buf, size, 1, f) != 1) { if(g_debug) perror("Cannot read db file"); return NULL; } fclose(f); if(g_debug) printf("Parsing db file '%s'\n", file); struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); memset(cmd_file, 0, sizeof(struct cmd_file_t)); /* add initial constants */ for(int i = 0; i < NR_INITIAL_CONSTANTS; i++) db_add_int_opt(&cmd_file->constant_list, init_const_name[i], init_const_value[i]); struct lex_ctx_t lctx; lctx.ctx.file = file; lctx.ctx.line = 1; lctx.ctx.begin = buf; lctx.ctx.ptr = buf; lctx.ctx.end = buf + size; #define next(clean_lexem) next(&lctx, clean_lexem) #define lexem lctx.lexem /* init lexer */ next(false); /* don't clean init lexem because it doesn't exist */ /* constants ? */ if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants")) { next(true); if(lexem.type != LEX_LBRACE) parse_error(lexem, "'{' expected after 'constants'\n"); while(true) { next(true); if(lexem.type == LEX_RBRACE) break; if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected in constants\n"); const char *name = lexem.str; next(false); /* lexem string is kept as option name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after identifier\n"); next(true); db_add_int_opt(&cmd_file->constant_list, name, parse_intexpr(&lctx, cmd_file->constant_list)); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after string\n"); } next(true); } /* options ? */ if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) { next(true); if(lexem.type != LEX_LBRACE) parse_error(lexem, "'{' expected after 'options'\n"); while(true) { next(true); if(lexem.type == LEX_RBRACE) break; if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected in options\n"); const char *name = lexem.str; next(false); /* lexem string is kept as option name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after identifier\n"); next(true); if(lexem.type == LEX_STRING) { db_add_str_opt(&cmd_file->opt_list, name, lexem.str); next(true); } else db_add_int_opt(&cmd_file->opt_list, name, parse_intexpr(&lctx, cmd_file->constant_list)); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after string\n"); } next(true); } /* sources */ if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources")) parse_error(lexem, "'sources' expected\n"); next(true); if(lexem.type != LEX_LBRACE) parse_error(lexem, "'{' expected after 'sources'\n"); while(true) { next(true); if(lexem.type == LEX_RBRACE) break; if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "identifier expected in sources\n"); const char *srcid = lexem.str; if(db_find_source_by_id(cmd_file, srcid) != NULL) parse_error(lexem, "Duplicate source identifier\n"); next(false); /* lexem string is kept as source name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after identifier\n"); next(true); if(lexem.type == LEX_STRING) { db_add_source(cmd_file, srcid, lexem.str); next(true); } else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern")) { next(true); if(lexem.type != LEX_LPAREN) parse_error(lexem, "'(' expected after 'extern'\n"); next(true); db_add_extern_source(cmd_file, srcid, parse_intexpr(&lctx, cmd_file->constant_list)); if(lexem.type != LEX_RPAREN) parse_error(lexem, "')' expected\n"); next(true); } else parse_error(lexem, "String or 'extern' expected after '='\n"); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected\n"); } /* sections */ while(true) { next(true); if(lexem.type == LEX_EOF) break; if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0) parse_error(lexem, "'section' expected\n"); next(true); if(lexem.type != LEX_LPAREN) parse_error(lexem, "'(' expected after 'section'\n"); next(true); /* can be any number */ struct cmd_section_t *sec = db_add_section(cmd_file, parse_intexpr(&lctx, cmd_file->constant_list), false); /* options ? */ if(lexem.type == LEX_SEMICOLON) { do { next(true); if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected for section option\n"); const char *name = lexem.str; next(false); /* lexem string is kept as option name */ if(lexem.type != LEX_EQUAL) parse_error(lexem, "'=' expected after option identifier\n"); next(true); if(lexem.type == LEX_STRING) { db_add_str_opt(&sec->opt_list, name, lexem.str); next(true); } else db_add_int_opt(&sec->opt_list, name, parse_intexpr(&lctx, cmd_file->constant_list)); }while(lexem.type == LEX_COLON); } if(lexem.type != LEX_RPAREN) parse_error(lexem, "')' expected after section identifier\n"); next(true); if(lexem.type == LEX_LBRACE) { sec->is_data = false; /* commands */ while(true) { next(true); if(lexem.type == LEX_RBRACE) break; struct cmd_inst_t *inst = db_add_inst(&sec->inst_list, CMD_LOAD, 0); if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Instruction expected in section\n"); if(strcmp(lexem.str, "load") == 0) inst->type = CMD_LOAD; else if(strcmp(lexem.str, "call") == 0) inst->type = CMD_CALL; else if(strcmp(lexem.str, "jump") == 0) inst->type = CMD_JUMP; else if(strcmp(lexem.str, "mode") == 0) inst->type = CMD_MODE; else parse_error(lexem, "Instruction expected in section\n"); next(true); if(inst->type == CMD_LOAD) { if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected after instruction\n"); inst->identifier = lexem.str; if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); next(false); /* lexem string kept as identifier */ if(lexem.type == LEX_RANGLE) { // load at inst->type = CMD_LOAD_AT; next(true); inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); } if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after command\n"); } else if(inst->type == CMD_CALL || inst->type == CMD_JUMP) { if(lexem.type == LEX_IDENTIFIER) { inst->identifier = lexem.str; if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); next(false); /* lexem string kept as identifier */ } else { inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT; inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); } if(lexem.type == LEX_LPAREN) { next(true); inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); if(lexem.type != LEX_RPAREN) parse_error(lexem, "Expected closing brace\n"); next(true); } if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after command\n"); } else if(inst->type == CMD_MODE) { inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "Expected ';' after command\n"); } else parse_error(lexem, "Internal error"); } } else if(lexem.type == LEX_LE) { sec->is_data = true; next(true); if(lexem.type != LEX_IDENTIFIER) parse_error(lexem, "Identifier expected after '<='\n"); sec->source_id = lexem.str; next(false); /* lexem string is kept as source id */ if(lexem.type != LEX_SEMICOLON) parse_error(lexem, "';' expected after identifier\n"); } else parse_error(lexem, "'{' or '<=' expected after section directive\n"); } #undef lexem #undef next free(buf); return cmd_file; } void db_free_option_list(struct cmd_option_t *opt_list) { while(opt_list) { struct cmd_option_t *next = opt_list->next; fflush(stdout); free(opt_list->name); free(opt_list->str); free(opt_list); opt_list = next; } } static bool db_generate_options(FILE *f, const char *secname, struct cmd_option_t *list) { fprintf(f, "%s\n", secname); fprintf(f, "{\n"); while(list) { fprintf(f, " %s = ", list->name); if(list->is_string) fprintf(f, "\"%s\";\n", list->str); // FIXME handle escape else fprintf(f, "0x%x;\n", list->val); list = list->next; } fprintf(f, "}\n"); return true; } static bool db_generate_section_options(FILE *f, struct cmd_option_t *list) { bool first = true; while(list) { fprintf(f, "%c %s = ", first ? ';' : ',', list->name); if(list->is_string) fprintf(f, "\"%s\"", list->str); // FIXME handle escape else fprintf(f, "0x%x", list->val); first = false; list = list->next; } return true; } static bool db_generate_sources(FILE *f, struct cmd_source_t *list) { fprintf(f, "sources\n"), fprintf(f, "{\n"); while(list) { fprintf(f, " %s = ", list->identifier); if(list->is_extern) fprintf(f, "extern(%d);\n", list->extern_nr); else fprintf(f, "\"%s\";\n", list->filename); // FIXME handle escape list = list->next; } fprintf(f, "}\n"); return true; } static bool db_generate_section(FILE *f, struct cmd_section_t *section) { fprintf(f, "section(%#x", section->identifier); db_generate_section_options(f, section->opt_list); if(section->is_data) { fprintf(f, ") <= %s;\n", section->source_id); return true; } fprintf(f, ")\n{\n"); struct cmd_inst_t *inst = section->inst_list; while(inst) { fprintf(f, " "); switch(inst->type) { case CMD_LOAD: fprintf(f, "load %s;\n", inst->identifier); break; case CMD_LOAD_AT: fprintf(f, "load %s > %#x;\n", inst->identifier, inst->addr); break; case CMD_CALL: fprintf(f, "call %s(%#x);\n", inst->identifier, inst->argument); break; case CMD_CALL_AT: fprintf(f, "call %#x(%#x);\n", inst->addr, inst->argument); break; case CMD_JUMP: fprintf(f, "jump %s(%#x);\n", inst->identifier, inst->argument); break; case CMD_JUMP_AT: fprintf(f, "jump %#x(%#x);\n", inst->addr, inst->argument); break; case CMD_MODE: fprintf(f, "mode %#x;\n", inst->argument); break; default: bug("die"); } inst = inst->next; } fprintf(f, "}\n"); return true; } static bool db_generate_sections(FILE *f, struct cmd_section_t *section) { while(section) if(!db_generate_section(f, section)) return false; else section = section->next; return true; } bool db_generate_file(struct cmd_file_t *file, const char *filename, void *user, db_color_printf printf) { FILE *f = fopen(filename, "w"); if(f == NULL) return printf(user, true, GREY, "Cannot open '%s' for writing: %m\n", filename), false; if(!db_generate_options(f, "constants", file->constant_list)) goto Lerr; if(!db_generate_options(f, "options", file->opt_list)) goto Lerr; if(!db_generate_sources(f, file->source_list)) goto Lerr; if(!db_generate_sections(f, file->section_list)) goto Lerr; fclose(f); return true; Lerr: fclose(f); return false; } void db_free(struct cmd_file_t *file) { db_free_option_list(file->opt_list); db_free_option_list(file->constant_list); struct cmd_source_t *src = file->source_list; while(src) { struct cmd_source_t *next = src->next; free(src->identifier); fflush(stdout); free(src->filename); if(src->loaded) { if(src->type == CMD_SRC_BIN) free(src->bin.data); if(src->type == CMD_SRC_ELF) elf_release(&src->elf); } free(src); src = next; } struct cmd_section_t *sec = file->section_list; while(sec) { struct cmd_section_t *next = sec->next; db_free_option_list(sec->opt_list); free(sec->source_id); struct cmd_inst_t *inst = sec->inst_list; while(inst) { struct cmd_inst_t *next = inst->next; free(inst->identifier); free(inst); inst = next; } free(sec); sec = next; } free(file); }