kulp / tenyr
1
#define _BSD_SOURCE 1
2
#include <stdint.h>
3
#include <stdlib.h>
4
#include <stdio.h>
5

6
#include <sys/time.h>
7

8
#include "plugin.h"
9

10
#include "SDL.h"
11
#include "SDL_image.h"
12

13
#include "os_common.h"
14
#include "common.h"
15
#include "param.h"
16
#include "device.h"
17
#include "sim.h"
18

19
#define SDLLED_UPDATE_HZ 30
20
#define SDLLED_BASE (0x100)
21

22
#define DIGIT_COUNT     4
23
#define DIGIT_WIDTH     38
24
#define DOT_WIDTH       6
25
#define DIGIT_HEIGHT    64
26
#define RESOURCE_DIR    "rsrc"
27

28
#define PUMP_CYCLES 2048
29

30
library_init tenyr_plugin_init;
31
device_adder sdlled_add_device;
32

33
struct sdlled_state {
34
    struct plugin_cookie *pcookie;
35
    int32_t data[2];
36
    SDL_Window *window;
37
    SDL_Renderer *renderer;
38
    SDL_Texture *digits[16];
39
    SDL_Texture *dots[2];
40
    struct timeval last_update, deadline;
41
    enum { RUNNING, STOPPING, STOPPED } status;
42
    int cycles;
43
};
44

45
static int handle_update(struct sdlled_state *state);
46

47 1
static int put_digit(struct sdlled_state *state, int index, int digit)
48
{
49 1
    SDL_Rect src = { .w = DIGIT_WIDTH, .h = DIGIT_HEIGHT },
50 1
             dst = {
51 1
                 .x = index * (DIGIT_WIDTH + DOT_WIDTH),
52
                 .w = DIGIT_WIDTH,
53
                 .h = DIGIT_HEIGHT
54
             };
55

56 1
    if (digit > 15)
57 0
        return -1;
58

59 1
    SDL_Texture *num = state->digits[digit];
60 1
    if (!num) {
61 1
        const char *share_path = ".";
62
        // If the param_get fails, we'll check the current directory
63 1
        state->pcookie->gops.param_get(state->pcookie, "paths.share", 1, (void*)&share_path);
64 1
        char *filename = build_path(share_path, RESOURCE_DIR "/%d/%c.png",
65 1
                DIGIT_HEIGHT, "0123456789ABCDEF"[digit]);
66

67 1
        SDL_Surface *surf = IMG_Load(filename);
68 1
        if (!surf)
69 0
            fatal(0, "sdlled failed to load sprite `%s' : %s", filename, IMG_GetError());
70

71 1
        free(filename);
72

73 1
        num = state->digits[digit] = SDL_CreateTextureFromSurface(state->renderer, surf);
74 1
        SDL_FreeSurface(surf);
75
    }
76

77 1
    SDL_RenderCopy(state->renderer, num, &src, &dst);
78

79 1
    return 0;
80
}
81

82 1
static int put_dot(struct sdlled_state *state, int index, int on)
83
{
84 1
    SDL_Rect src = { .w = DOT_WIDTH, .h = DIGIT_HEIGHT },
85 1
             dst = {
86 1
                 .x = (index + 1) * DIGIT_WIDTH + index * DOT_WIDTH,
87
                 .w = DOT_WIDTH,
88
                 .h = DIGIT_HEIGHT
89
             };
90

91 1
    SDL_Texture *dot = state->dots[on];
92 1
    if (!dot) {
93 1
        const char *share_path = ".";
94
        // If the param_get fails, we'll check the current directory
95 1
        state->pcookie->gops.param_get(state->pcookie, "paths.share", 1, (void*)&share_path);
96 1
        char *filename = build_path(share_path, RESOURCE_DIR "/%d/dot_%s.png",
97
                DIGIT_HEIGHT, on ? "on" : "off");
98

99 1
        SDL_Surface *surf = IMG_Load(filename);
100 1
        if (!surf)
101 0
            fatal(0, "sdlled failed to load sprite `%s' : %s", filename, IMG_GetError());
102

103 1
        free(filename);
104

105 1
        dot = state->dots[on] = SDL_CreateTextureFromSurface(state->renderer, surf);
106 1
        SDL_FreeSurface(surf);
107
    }
108

109 1
    SDL_RenderCopy(state->renderer, dot, &src, &dst);
110

111 1
    return 0;
112
}
113

114 1
static void decode_led(int32_t data, int digits[4])
115
{
116 1
    for (unsigned i = 0; i < 4; i++)
117 1
        digits[i] = (data >> (i * 4)) & 0xf;
118
}
119

120 1
static void decode_dots(int32_t data, int dots[4])
121
{
122 1
    for (unsigned i = 0; i < 4; i++)
123 1
        dots[i] = (data >> i) & 1;
124
}
125

126 1
static int sdlled_init(struct plugin_cookie *pcookie, struct device *device, void *cookie)
127
{
128 1
    struct sdlled_state *state = *(void**)cookie;
129

130 1
    if (!state)
131 1
        state = *(void**)cookie = malloc(sizeof *state);
132

133 1
    *state = (struct sdlled_state){ .status = RUNNING };
134

135 1
    state->pcookie = pcookie;
136

137 1
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
138 0
        fatal(0, "Unable to init SDL: %s", SDL_GetError());
139

140 1
    state->window = SDL_CreateWindow("sdlled",
141
            0, 0, DIGIT_COUNT * (DIGIT_WIDTH + DOT_WIDTH), DIGIT_HEIGHT, SDL_WINDOW_SHOWN);
142

143 1
    if (!state->window)
144 0
        fatal(0, "Unable to set up LED surface : %s", SDL_GetError());
145

146 1
    state->renderer = SDL_CreateRenderer(state->window, -1, 0);
147

148 1
    int flags = IMG_INIT_PNG;
149 1
    if (IMG_Init(flags) != flags)
150 0
        fatal(0, "sdlled failed to initialise SDL_Image");
151

152 1
    gettimeofday(&state->last_update, NULL);
153 1
    state->deadline = state->last_update;
154 1
    handle_update(state);
155

156 1
    return 0;
157
}
158

159 1
static int sdlled_fini(void *cookie)
160
{
161 1
    struct sdlled_state *state = cookie;
162

163 1
    for (unsigned i = 0; i < 16; i++)
164 1
        SDL_DestroyTexture(state->digits[i]);
165

166 1
    SDL_DestroyTexture(state->dots[0]);
167 1
    SDL_DestroyTexture(state->dots[1]);
168

169 1
    SDL_DestroyRenderer(state->renderer);
170 1
    SDL_DestroyWindow(state->window);
171 1
    free(state);
172
    // Can't immediately call SDL_Quit() in case others are using it
173 1
    atexit(SDL_Quit);
174 1
    atexit(IMG_Quit);
175

176 1
    return 0;
177
}
178

179 1
static int handle_update(struct sdlled_state *state)
180
{
181 1
    int digits[4] = { 0 };
182 1
    int dots[4] = { 0 };
183

184 1
    decode_led(state->data[0], digits);
185 1
    decode_dots(state->data[1], dots);
186

187 1
    for (int i = 0; i < 4; i++) {
188 1
        put_digit(state, i, digits[3 - i]);
189 1
        put_dot(state, i, dots[3 - i]);
190
    }
191

192
    // do periodic updates only, not as fast as we write to the display
193
    // (both faster to render, and more like real life)
194 1
    struct timeval now, tick = { .tv_usec = 1000000 / SDLLED_UPDATE_HZ };
195 1
    gettimeofday(&now, NULL);
196
    // TODO this could get lagged behind
197 1
    if (timercmp(&now, &state->deadline, >)) {
198 1
        SDL_RenderPresent(state->renderer);
199 1
        state->last_update = state->deadline;
200 1
        timeradd(&state->deadline, &tick, &state->deadline);
201
    }
202

203 1
    return 0;
204
}
205

206 1
static int sdlled_op(void *cookie, int op, int32_t addr, int32_t *data)
207
{
208 1
    struct sdlled_state *state = cookie;
209

210 1
    if (op == OP_WRITE) {
211 1
        state->data[addr - SDLLED_BASE] = *data;
212 1
        handle_update(state);
213 1
    } else if (op == OP_DATA_READ) {
214 1
        int32_t off = addr - SDLLED_BASE;
215 1
        switch (off) {
216 1
            case 0: *data = state->data[off] & 0x0000ffff; break;
217 1
            case 1: *data = state->data[off] & 0x0000000f; break;
218 0
            default: return 1;
219
        }
220
    }
221

222 1
    return 0;
223
}
224

225 1
static int sdlled_pump(void *cookie)
226
{
227 1
    struct sdlled_state *state = cookie;
228

229
    SDL_Event event;
230 1
    if (state->cycles++ % PUMP_CYCLES == 0 && state->status == RUNNING) {
231 1
        if (SDL_PollEvent(&event)) {
232 1
            switch (event.type) {
233 0
                case SDL_QUIT:
234 0
                    state->status = STOPPED;
235 0
                    exit(0);
236 1
                default:
237 1
                    break;
238
            }
239
        }
240
    }
241

242 1
    return 0;
243
}
244

245 1
int EXPORT tenyr_plugin_init(struct guest_ops *ops)
246
{
247 1
    fatal_ = ops->fatal;
248 1
    debug_ = ops->debug;
249

250 1
    return 0;
251
}
252

253 1
int EXPORT sdlled_add_device(struct device *device)
254
{
255 1
    *device = (struct device){
256
        .bounds = { SDLLED_BASE, SDLLED_BASE + 1 },
257
        .ops = {
258
            .op = sdlled_op,
259
            .init = sdlled_init,
260
            .fini = sdlled_fini,
261
            .cycle = sdlled_pump,
262
        },
263
    };
264

265 1
    return 0;
266
}
267

268
/* vi: set ts=4 sw=4 et: */

Read our documentation on viewing source code .

Loading