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
RevLine 
[a06aa8b]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
[2147e53]15 * "gpl.txt" in the top directory of this repository.
[a06aa8b]16 */
17
18#include <all.h>
19
[4c71d39]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__)
[a06aa8b]23
[c8a92ef]24#define WIN_W (510 * 2)
25#define WIN_H (64 * 2)
[7ba68aa]26
27#define CON_BGR 0x00000000
[c8a92ef]28#define CON_DRW 0xFFFFFFFF
[7ba68aa]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
[43ea417]43#define G_DSPOFF 0x58
44#define G_DSPON 0x59
[7ba68aa]45#define G_HSCRL 0x5A
46#define G_OVRLAY 0x5B
47#define G_CGRAM 0x5C
48#define G_CRSFRM 0x5D
49
[c8a92ef]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];
[7ba68aa]61
62static SDL_Window *win;
63static SDL_Renderer *ren;
64static SDL_atomic_t frame;
[c8a92ef]65static SDL_atomic_t clear;
[7ba68aa]66
[43ea417]67static uint32_t render = 0;
68
[7ba68aa]69static TTF_Font *fon;
70static int32_t fon_w, fon_h;
71
72static int32_t sur_w, sur_h;
73static SDL_Surface *sur;
74
[c8a92ef]75static SDL_Surface *gfx_sur;
76
77static int32_t cur = 0;
78static int32_t cur_c = 0;
79static int32_t dir = 1;
[7ba68aa]80
81static int32_t current_op = 0x00;
82
[43ea417]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
[4c71d39]88int32_t lcd_verbose = 0;
[a06aa8b]89
[43ea417]90
[c8a92ef]91void lcd_sdl(void)
[7ba68aa]92{
[c8a92ef]93 ver3("lcd_sdl()");
[7ba68aa]94
[c8a92ef]95 if (SDL_AtomicGet(&clear)) {
96 if (SDL_SetRenderDrawColor(ren, 0x00, 0x00, 0x00, 0xff) < 0) {
97 fail("SDL_SetRenderDrawColor() failed: %s", SDL_GetError());
[7ba68aa]98 }
[43ea417]99
[c8a92ef]100 if (SDL_RenderClear(ren) < 0) {
101 fail("SDL_RenderClear() failed: %s", SDL_GetError());
[43ea417]102 }
103
[c8a92ef]104 SDL_RenderPresent(ren);
105 SDL_AtomicSet(&clear, 0);
106 return;
[43ea417]107 }
[7ba68aa]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
[c8a92ef]119 if (SDL_FillRect(sur, NULL, CON_BGR) < 0) {
[7ba68aa]120 fail("SDL_FillRect() failed: %s", SDL_GetError());
121 }
122
[c8a92ef]123 for (int32_t y = 0; y < TXT_H; ++y) {
124 char line[TXT_W + 1];
[7ba68aa]125
126 if (SDL_LockMutex(cpu_mutex) < 0) {
127 fail("SDL_LockMutex() failed: %s", SDL_GetError());
128 }
129
[c8a92ef]130 memcpy(line, mem_txt + y * TXT_W, TXT_W);
131 line[TXT_W] = 0;
[7ba68aa]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,
[c8a92ef]146 .w = TXT_W * fon_w,
[7ba68aa]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
[c8a92ef]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
[7ba68aa]197 SDL_DestroyTexture(tex);
[c8a92ef]198 SDL_DestroyTexture(gfx_tex);
[7ba68aa]199 SDL_RenderPresent(ren);
200}
201
202
[a06aa8b]203void lcd_init(void)
204{
205 ver("lcd init");
[7ba68aa]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
[c8a92ef]220 SDL_AtomicSet(&clear, 0);
221
[7ba68aa]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
[c8a92ef]242 sur_w = TXT_W * fon_w;
243 sur_h = TXT_H * fon_h;
[7ba68aa]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
[c8a92ef]251 gfx_sur = SDL_CreateRGBSurface(0, sur_w, sur_h, 32, 0, 0, 0, 0);
[7ba68aa]252
[c8a92ef]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 }
[a06aa8b]260}
261
262void lcd_quit(void)
263{
264 ver("lcd quit");
[7ba68aa]265
266 SDL_FreeSurface(sur);
[c8a92ef]267 SDL_FreeSurface(gfx_sur);
[7ba68aa]268 TTF_CloseFont(fon);
269
270 SDL_DestroyRenderer(ren);
271 SDL_DestroyWindow(win);
[a06aa8b]272}
273
[3c30832]274bool lcd_exec(void)
[a06aa8b]275{
[4c71d39]276 ver3("lcd exec");
[3c30832]277 return false;
[a06aa8b]278}
279
280uint32_t lcd_read(uint32_t off, int32_t sz)
281{
[4c71d39]282 ver2("lcd rd %u:%d", off, sz * 8);
[7ba68aa]283
284 uint32_t rv;
285
286 switch (current_op) {
287 case G_MREAD:
[c8a92ef]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 }
[7ba68aa]297 break;
[43ea417]298
[7ba68aa]299 default:
300 rv = 0x00;
301 break;
302 }
303 return rv;
[a06aa8b]304}
305
306void lcd_write(uint32_t off, int32_t sz, uint32_t val)
307{
[c8a92ef]308 if (last_val != val && mult_val_c > 0) {
309 if (mult_val_c > 1) {
[43ea417]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 }
[c8a92ef]319 else if (last_val == val && mult_val_c >= 0) {
[43ea417]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;
[7ba68aa]329
330 if (off == 0) {
331 switch (current_op) {
332 case G_MWRITE:
[c8a92ef]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 }
[7ba68aa]358 break;
[43ea417]359
[7ba68aa]360 case G_CRSWR:
[c8a92ef]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;
[7ba68aa]368 }
[43ea417]369 break;
370
[7ba68aa]371 default:
372 break;
373 }
374 }
375 else {
376 switch (val) {
377 case G_MWRITE:
378 current_op = G_MWRITE;
379 break;
[43ea417]380
[7ba68aa]381 case G_CRSMRT:
[c8a92ef]382 dir = 1;
[43ea417]383 current_op = G_CRSMRT;
384 break;
385
386 case G_CRSMUP:
[c8a92ef]387 dir = -85;
[43ea417]388 current_op = G_CRSMUP;
389 break;
390
391 case G_CRSMDN:
[c8a92ef]392 dir = 85;
[43ea417]393 current_op = G_CRSMDN;
[7ba68aa]394 break;
[43ea417]395
[7ba68aa]396 case G_CRSWR:
397 current_op = G_CRSWR;
398 break;
[43ea417]399
[7ba68aa]400 case G_MREAD:
401 current_op = G_MREAD;
402 break;
[43ea417]403
404 case G_DSPOFF:
[c8a92ef]405 SDL_AtomicSet(&clear, 1);
[43ea417]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
[7ba68aa]415 default:
416 current_op = 0x00;
417 break;
418 }
419 }
[a06aa8b]420}
Note: See TracBrowser for help on using the repository browser.