source: buchla-emu/emu/lcd.c@ c8a92ef

Last change on this file since c8a92ef was c8a92ef, checked in by Alexander Heinrich <alex.heinrich@…>, 7 years ago

Add graphic output to lcd window

  • Property mode set to 100644
File size: 8.5 KB
Line 
1/*
2 * Copyright (C) 2017 The Contributors
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * A copy of the GNU General Public License can be found in the file
15 * "gpl.txt" in the top directory of this repository.
16 */
17
18#include <all.h>
19
20#define ver(...) _ver(lcd_verbose, 0, __VA_ARGS__)
21#define ver2(...) _ver(lcd_verbose, 1, __VA_ARGS__)
22#define ver3(...) _ver(lcd_verbose, 2, __VA_ARGS__)
23
24#define WIN_W (510 * 2)
25#define WIN_H (64 * 2)
26
27#define CON_BGR 0x00000000
28#define CON_DRW 0xFFFFFFFF
29#define CON_FGR ((SDL_Color){ .r = 255, .b = 255, .g = 255, .a = 255 })
30
31#define G_INIT 0x40
32#define G_MWRITE 0x42
33#define G_MREAD 0x43
34#define G_SETSAD 0x44
35#define G_CRSWR 0x46
36#define G_CRSRD 0x47
37#define G_CRSMRT 0x4C
38#define G_CRSMLT 0x4D
39#define G_CRSMUP 0x4E
40#define G_CRSMDN 0x4F
41#define G_ERASE 0x52
42#define G_SLEEP 0x53
43#define G_DSPOFF 0x58
44#define G_DSPON 0x59
45#define G_HSCRL 0x5A
46#define G_OVRLAY 0x5B
47#define G_CGRAM 0x5C
48#define G_CRSFRM 0x5D
49
50#define TXT_W 85
51#define TXT_H 8
52
53#define GFX_W 85
54#define GFX_H 64
55
56#define BASE_TXT 0x0000
57#define BASE_GFX 0x2000
58
59static uint8_t mem_txt[TXT_H * TXT_W];
60static uint8_t mem_gfx[GFX_H * GFX_W];
61
62static SDL_Window *win;
63static SDL_Renderer *ren;
64static SDL_atomic_t frame;
65static SDL_atomic_t clear;
66
67static uint32_t render = 0;
68
69static TTF_Font *fon;
70static int32_t fon_w, fon_h;
71
72static int32_t sur_w, sur_h;
73static SDL_Surface *sur;
74
75static SDL_Surface *gfx_sur;
76
77static int32_t cur = 0;
78static int32_t cur_c = 0;
79static int32_t dir = 1;
80
81static int32_t current_op = 0x00;
82
83static uint32_t last_val = 0x00;
84static uint32_t last_off = 0;
85static int32_t last_sz = -1;
86static int32_t mult_val_c = 0;
87
88int32_t lcd_verbose = 0;
89
90
91void lcd_sdl(void)
92{
93 ver3("lcd_sdl()");
94
95 if (SDL_AtomicGet(&clear)) {
96 if (SDL_SetRenderDrawColor(ren, 0x00, 0x00, 0x00, 0xff) < 0) {
97 fail("SDL_SetRenderDrawColor() failed: %s", SDL_GetError());
98 }
99
100 if (SDL_RenderClear(ren) < 0) {
101 fail("SDL_RenderClear() failed: %s", SDL_GetError());
102 }
103
104 SDL_RenderPresent(ren);
105 SDL_AtomicSet(&clear, 0);
106 return;
107 }
108
109 static int32_t last = 0;
110 int32_t now = SDL_AtomicGet(&frame);
111
112 if (last == now) {
113 ver3("no update");
114 return;
115 }
116
117 last = now;
118
119 if (SDL_FillRect(sur, NULL, CON_BGR) < 0) {
120 fail("SDL_FillRect() failed: %s", SDL_GetError());
121 }
122
123 for (int32_t y = 0; y < TXT_H; ++y) {
124 char line[TXT_W + 1];
125
126 if (SDL_LockMutex(cpu_mutex) < 0) {
127 fail("SDL_LockMutex() failed: %s", SDL_GetError());
128 }
129
130 memcpy(line, mem_txt + y * TXT_W, TXT_W);
131 line[TXT_W] = 0;
132
133 if (SDL_UnlockMutex(cpu_mutex) < 0) {
134 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
135 }
136
137 SDL_Surface *lin = TTF_RenderText_Blended(fon, line, CON_FGR);
138
139 if (lin == NULL) {
140 fail("TTF_RenderText_Blended() failed: %s", TTF_GetError());
141 }
142
143 if (SDL_BlitSurface(lin, NULL, sur, &(SDL_Rect){
144 .x = 0,
145 .y = y * fon_h,
146 .w = TXT_W * fon_w,
147 .h = fon_h
148 })) {
149 fail("SDL_BlitSurface() failed: %s", SDL_GetError());
150 }
151
152 SDL_FreeSurface(lin);
153 }
154
155 SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, sur);
156
157 if (tex == NULL) {
158 fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
159 }
160
161 if (SDL_RenderCopy(ren, tex, NULL, NULL) < 0) {
162 fail("SDL_RenderCopy() failed: %s", SDL_GetError());
163 }
164
165 for (int32_t y = 0; y < GFX_H * GFX_W; ++y) {
166 for (int32_t p = 7; p > 1; --p) {
167 uint32_t col = CON_BGR;
168 if ((mem_gfx[y] & (1 << p)) > 0) {
169 col = CON_DRW;
170 }
171
172 if (SDL_FillRect(gfx_sur, &(SDL_Rect){
173 .x = y % 85 * fon_w + (8 - p) * fon_w / 6,
174 .y = y / 85 * fon_h / 8,
175 .w = fon_w / 6 + 1,
176 .h = fon_h / 8 + 1
177 }, col) < 0) {
178 fail("SDL_FillRect() failed: %s", SDL_GetError());
179 }
180 }
181 }
182
183 SDL_Texture *gfx_tex = SDL_CreateTextureFromSurface(ren, gfx_sur);
184
185 if (gfx_tex == NULL) {
186 fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
187 }
188
189 if (SDL_SetTextureBlendMode(gfx_tex, SDL_BLENDMODE_ADD) < 0) {
190 fail("SDL_SetTextureBlendMode() failed: %s", SDL_GetError());
191 }
192
193 if (SDL_RenderCopy(ren, gfx_tex, NULL, NULL) < 0) {
194 fail("SDL_RenderCopy() failed: %s", SDL_GetError());
195 }
196
197 SDL_DestroyTexture(tex);
198 SDL_DestroyTexture(gfx_tex);
199 SDL_RenderPresent(ren);
200}
201
202
203void lcd_init(void)
204{
205 ver("lcd init");
206
207 win = SDL_CreateWindow("Front LCD", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
208 WIN_W, WIN_H, 0);
209
210 if (win == NULL) {
211 fail("SDL_CreateWindow() failed: %s", SDL_GetError());
212 }
213
214 ren = SDL_CreateRenderer(win, -1, 0);
215
216 if (ren == NULL) {
217 fail("SDL_CreateRenderer() failed: %s", SDL_GetError());
218 }
219
220 SDL_AtomicSet(&clear, 0);
221
222 SDL_AtomicSet(&frame, 1);
223
224 SDL_RWops *ops = SDL_RWFromFile(font, "rb");
225
226 if (ops == NULL) {
227 fail("error while opening font file %s: %s", font, SDL_GetError());
228 }
229
230 fon = TTF_OpenFontRW(ops, 1, 32);
231
232 if (fon == NULL) {
233 fail("error while loading font file %s: %s", font, TTF_GetError());
234 }
235
236 fon_h = TTF_FontLineSkip(fon);
237
238 if (TTF_GlyphMetrics(fon, 'X', NULL, NULL, NULL, NULL, &fon_w) < 0) {
239 fail("error while measuring font width: %s", TTF_GetError());
240 }
241
242 sur_w = TXT_W * fon_w;
243 sur_h = TXT_H * fon_h;
244
245 sur = SDL_CreateRGBSurface(0, sur_w, sur_h, 32, 0, 0, 0, 0);
246
247 if (sur == NULL) {
248 fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
249 }
250
251 gfx_sur = SDL_CreateRGBSurface(0, sur_w, sur_h, 32, 0, 0, 0, 0);
252
253 if (gfx_sur == NULL) {
254 fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
255 }
256
257 for (int32_t y = 0; y < TXT_W * TXT_H; ++y) {
258 mem_txt[y] = ' ';
259 }
260}
261
262void lcd_quit(void)
263{
264 ver("lcd quit");
265
266 SDL_FreeSurface(sur);
267 SDL_FreeSurface(gfx_sur);
268 TTF_CloseFont(fon);
269
270 SDL_DestroyRenderer(ren);
271 SDL_DestroyWindow(win);
272}
273
274bool lcd_exec(void)
275{
276 ver3("lcd exec");
277 return false;
278}
279
280uint32_t lcd_read(uint32_t off, int32_t sz)
281{
282 ver2("lcd rd %u:%d", off, sz * 8);
283
284 uint32_t rv;
285
286 switch (current_op) {
287 case G_MREAD:
288 if (cur >= BASE_TXT && cur < BASE_TXT + TXT_W * TXT_H) {
289 rv = mem_txt[cur - BASE_TXT];
290 }
291 else if (cur >= BASE_GFX && cur < BASE_TXT + GFX_W * GFX_H) {
292 rv = mem_gfx[cur - BASE_GFX];
293 }
294 else {
295 rv = 0x00;
296 }
297 break;
298
299 default:
300 rv = 0x00;
301 break;
302 }
303 return rv;
304}
305
306void lcd_write(uint32_t off, int32_t sz, uint32_t val)
307{
308 if (last_val != val && mult_val_c > 0) {
309 if (mult_val_c > 1) {
310 ver2("lcd wr %u:%d 0x%0*x was called %u more times", last_off, last_sz * 8, last_sz * 2, last_val, mult_val_c);
311 }
312 else {
313 ver2("lcd wr %u:%d 0x%0*x", last_off, last_sz * 8, last_sz * 2, last_val);
314 }
315
316 ver2("lcd wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
317 mult_val_c = 0;
318 }
319 else if (last_val == val && mult_val_c >= 0) {
320 ++mult_val_c;
321 }
322 else {
323 ver2("lcd wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
324 }
325
326 last_val = val;
327 last_off = off;
328 last_sz = sz;
329
330 if (off == 0) {
331 switch (current_op) {
332 case G_MWRITE:
333 if (cur >= BASE_TXT && cur < BASE_TXT + TXT_W * TXT_H) {
334 if (val == 0) {
335 mem_txt[cur - BASE_TXT] = ' ';
336 }
337 else {
338 mem_txt[cur - BASE_TXT] = (uint8_t) val;
339 }
340
341 cur += dir;
342
343 if (render) {
344 SDL_AtomicAdd(&frame, 1);
345 }
346 }
347 else if (cur >= BASE_GFX && cur < BASE_GFX + GFX_W * GFX_H) {
348 mem_gfx[cur - BASE_GFX] = (uint8_t) val;
349 cur += dir;
350
351 if (render) {
352 SDL_AtomicAdd(&frame, 1);
353 }
354 }
355 else {
356 err("Invalid cur value %d", cur);
357 }
358 break;
359
360 case G_CRSWR:
361 if (cur_c == 0) {
362 cur = (int32_t) val;
363 cur_c++;
364 }
365 else if (cur_c == 1) {
366 cur = cur | ((int32_t) val << 8);
367 cur_c = 0;
368 }
369 break;
370
371 default:
372 break;
373 }
374 }
375 else {
376 switch (val) {
377 case G_MWRITE:
378 current_op = G_MWRITE;
379 break;
380
381 case G_CRSMRT:
382 dir = 1;
383 current_op = G_CRSMRT;
384 break;
385
386 case G_CRSMUP:
387 dir = -85;
388 current_op = G_CRSMUP;
389 break;
390
391 case G_CRSMDN:
392 dir = 85;
393 current_op = G_CRSMDN;
394 break;
395
396 case G_CRSWR:
397 current_op = G_CRSWR;
398 break;
399
400 case G_MREAD:
401 current_op = G_MREAD;
402 break;
403
404 case G_DSPOFF:
405 SDL_AtomicSet(&clear, 1);
406 render = 0;
407 current_op = G_DSPOFF;
408 break;
409
410 case G_DSPON:
411 render = 1;
412 current_op = G_DSPON;
413 break;
414
415 default:
416 current_op = 0x00;
417 break;
418 }
419 }
420}
Note: See TracBrowser for help on using the repository browser.