source: buchla-emu/emu/ser.c@ a2b296e

Last change on this file since a2b296e was a2b296e, checked in by Thomas Lopatic <thomas@…>, 6 years ago

Smaller LCD and console windows. Mouse scaling.

  • Property mode set to 100644
File size: 10.8 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(ser_verbose, 0, __VA_ARGS__)
21#define ver2(...) _ver(ser_verbose, 1, __VA_ARGS__)
22#define ver3(...) _ver(ser_verbose, 2, __VA_ARGS__)
23
24int32_t ser_verbose = 0;
25
26#define WIN_W (1520 / 2)
27#define WIN_H (950 / 2)
28
29#define BEL_CYC 10000
30#define MOU_CYC 10000
31
32#if defined EMU_WIN
33#define MOU_DIV 64
34#else
35#define MOU_DIV 4
36#endif
37
38#define CON_W 80
39#define CON_H 25
40
41#define CON_BGR 0x00000000
42#define CON_BEL 0x00808080
43#define CON_CUR 0x00e87000
44#define CON_FGR ((SDL_Color){ .r = 255, .b = 255, .g = 255, .a = 255 })
45
46#define BUF_SZ 16
47
48#define REG_IER_ISR 0
49#define REG_CFR_SR 1
50#define REG_CDR_TBR 2
51#define REG_TDR_RDR 3
52
53typedef struct {
54 int32_t buf_hd;
55 int32_t buf_tl;
56 uint8_t buf[BUF_SZ];
57 bool irq_r;
58 bool irq_t;
59 bool rdr_ok;
60 uint8_t rdr;
61} state_t;
62
63static state_t state[] = {
64 { .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 },
65 { .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 }
66};
67
68static uint8_t mem[CON_H][CON_W];
69
70static SDL_Window *win;
71uint32_t ser_win;
72
73static SDL_Renderer *ren;
74static SDL_atomic_t frame;
75
76static TTF_Font *fon;
77static int32_t fon_w, fon_h;
78
79static int32_t sur_w, sur_h;
80static SDL_Surface *sur;
81
82static int32_t cur_x = 0, cur_y = 0;
83static int32_t bel = 0;
84
85static int32_t mou;
86static int32_t mou_dx, mou_dy;
87static bool mou_l, mou_r;
88
89static void scroll(void)
90{
91 memmove(mem, mem + 1, (CON_H - 1) * CON_W);
92 memset(mem + (CON_H - 1), ' ', CON_W);
93}
94
95static void forw(void)
96{
97 if (cur_x < CON_W - 1) {
98 ++cur_x;
99 return;
100 }
101
102 if (cur_y == CON_H - 1) {
103 cur_x = 0;
104 scroll();
105 return;
106 }
107
108 cur_x = 0;
109 ++cur_y;
110}
111
112static void back(void)
113{
114 if (cur_x > 0) {
115 --cur_x;
116 return;
117 }
118
119 if (cur_y == 0) {
120 return;
121 }
122
123 cur_x = CON_W - 1;
124 --cur_y;
125}
126
127static void down(void)
128{
129 if (cur_y < CON_H - 1) {
130 ++cur_y;
131 return;
132 }
133
134 scroll();
135}
136
137static void echo(uint8_t c)
138{
139 if (c < 32) {
140 switch (c) {
141 case '\r':
142 cur_x = 0;
143 break;
144
145 case '\n':
146 down();
147 break;
148
149 case '\b':
150 back();
151 break;
152
153 case '\a':
154 bel = BEL_CYC;
155 break;
156
157 default:
158 echo('^');
159 echo((uint8_t)(c + '@'));
160 return;
161 }
162 }
163 else {
164 mem[cur_y][cur_x] = c;
165 forw();
166 }
167
168 SDL_AtomicAdd(&frame, 1);
169}
170
171static void xmit(int32_t un)
172{
173 int32_t i = state[un].buf_tl;
174 ver2("ser xmit %d %d", i, state[un].buf_hd);
175
176 if (i >= state[un].buf_hd) {
177 return;
178 }
179
180 uint8_t byte = state[un].buf[i % BUF_SZ];
181 ver2("ser xmit 0x%02x", byte);
182
183 state[un].rdr = byte;
184 state[un].rdr_ok = true;
185 state[un].irq_r = true;
186
187 state[un].buf_tl = i + 1;
188
189 if (state[un].buf_tl >= BUF_SZ) {
190 state[un].buf_hd -= BUF_SZ;
191 state[un].buf_tl -= BUF_SZ;
192 ver2("ser adj %d %d", state[un].buf_tl, state[un].buf_hd);
193 }
194}
195
196static void out_lk(int32_t un, uint8_t c)
197{
198 int32_t i = state[un].buf_hd;
199 ver2("ser out %d %d 0x%02x", state[un].buf_tl, i, c);
200
201 if (i >= state[un].buf_tl + BUF_SZ) {
202 err("serial port %d losing data", un);
203 return;
204 }
205
206 state[un].buf[i % BUF_SZ] = c;
207 state[un].buf_hd = i + 1;
208
209 if (!state[un].irq_r && !state[un].rdr_ok) {
210 xmit(un);
211 }
212}
213
214static void out(int32_t un, uint8_t c)
215{
216 if (SDL_LockMutex(cpu_mutex) < 0) {
217 fail("SDL_LockMutex() failed: %s", SDL_GetError());
218 }
219
220 out_lk(un, c);
221
222 if (SDL_UnlockMutex(cpu_mutex) < 0) {
223 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
224 }
225}
226
227static void mouse(uint8_t c)
228{
229 if (c == 't') {
230 ver2("ser mou init");
231 }
232
233 out_lk(0, 'V');
234 out_lk(0, 'O');
235}
236
237void ser_sdl(void)
238{
239 ver3("ser_sdl()");
240
241 static int32_t last = 0;
242 int32_t now = SDL_AtomicGet(&frame);
243
244 if (last == now) {
245 ver3("no update");
246 return;
247 }
248
249 last = now;
250
251 if (SDL_FillRect(sur, NULL, bel == 0 ? CON_BGR : CON_BEL) < 0) {
252 fail("SDL_FillRect() failed: %s", SDL_GetError());
253 }
254
255 if (SDL_FillRect(sur, &(SDL_Rect){
256 .x = cur_x * fon_w,
257 .y = cur_y * fon_h,
258 .w = fon_w,
259 .h = fon_h
260 }, CON_CUR) < 0) {
261 fail("SDL_FillRect() failed: %s", SDL_GetError());
262 }
263
264 for (int32_t y = 0; y < CON_H; ++y) {
265 char line[CON_W + 1];
266 line[CON_W] = 0;
267
268 if (SDL_LockMutex(cpu_mutex) < 0) {
269 fail("SDL_LockMutex() failed: %s", SDL_GetError());
270 }
271
272 memcpy(line, mem[y], CON_W);
273
274 if (SDL_UnlockMutex(cpu_mutex) < 0) {
275 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
276 }
277
278 SDL_Surface *lin = TTF_RenderText_Blended(fon, line, CON_FGR);
279
280 if (lin == NULL) {
281 fail("TTF_RenderText_Blended() failed: %s", TTF_GetError());
282 }
283
284 if (SDL_BlitSurface(lin, NULL, sur, &(SDL_Rect){
285 .x = 0,
286 .y = y * fon_h,
287 .w = CON_W * fon_w,
288 .h = fon_h
289 })) {
290 fail("SDL_BlitSurface() failed: %s", SDL_GetError());
291 }
292
293 SDL_FreeSurface(lin);
294 }
295
296 SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, sur);
297
298 if (tex == NULL) {
299 fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
300 }
301
302 if (SDL_RenderCopy(ren, tex, NULL, NULL) < 0) {
303 fail("SDL_RenderCopy() failed: %s", SDL_GetError());
304 }
305
306 SDL_DestroyTexture(tex);
307 SDL_RenderPresent(ren);
308}
309
310void ser_key(SDL_KeyboardEvent *ev)
311{
312 switch (ev->keysym.sym) {
313 case SDLK_BACKSPACE:
314 out(1, '\b');
315 break;
316
317 case SDLK_RETURN:
318 out(1, '\r');
319 break;
320
321 default:
322 if ((ev->keysym.mod & KMOD_CTRL) != 0 &&
323 ev->keysym.sym >= SDLK_a && ev->keysym.sym <= SDLK_z) {
324 out(1, (uint8_t)(ev->keysym.sym - SDLK_a + 1));
325 }
326
327 break;
328 }
329}
330
331void ser_text(SDL_TextInputEvent *ev)
332{
333 for (int32_t i = 0; ev->text[i] != 0; ++i) {
334 out(1, (uint8_t)ev->text[i]);
335 }
336}
337
338static void mou_ev(void)
339{
340 ver2("ser mou ev (%d, %d) %c %c",
341 mou_dx, mou_dy, mou_l ? 'l' : '-', mou_r ? 'r' : '-');
342
343 int32_t dx = mou_dx / MOU_DIV;
344 int32_t dy = mou_dy / MOU_DIV;
345
346 if (dx < -128) {
347 dx = -128;
348 }
349 else if (dx > 127) {
350 dx = 127;
351 }
352
353 if (dy < -128) {
354 dy = -128;
355 }
356 else if (dy > 127) {
357 dy = 127;
358 }
359
360 dx = dx & 0xff;
361 dy = dy & 0xff;
362
363 int32_t b1 = 0x40;
364
365 if (mou_l) {
366 b1 |= 0x20;
367 }
368
369 if (mou_r) {
370 b1 |= 0x10;
371 }
372
373 b1 |= (dy & 0xc0) >> 4;
374 b1 |= (dx & 0xc0) >> 6;
375
376 int32_t b2 = dx & 0x3f;
377 int32_t b3 = dy & 0x3f;
378
379 out_lk(0, (uint8_t)b1);
380 out_lk(0, (uint8_t)b2);
381 out_lk(0, (uint8_t)b3);
382
383 mou_dx = mou_dy = 0;
384}
385
386static void mou_ev_chk(void)
387{
388 if (--mou > 0) {
389 return;
390 }
391
392 mou = MOU_CYC;
393
394 if (mou_dx == 0 && mou_dy == 0) {
395 return;
396 }
397
398 mou_ev();
399}
400
401void ser_mou_res(void)
402{
403 if (SDL_LockMutex(cpu_mutex) < 0) {
404 fail("SDL_LockMutex() failed: %s", SDL_GetError());
405 }
406
407 mou = MOU_CYC;
408 mou_dx = mou_dy = 0;
409 mou_l = mou_r = false;
410
411 if (SDL_UnlockMutex(cpu_mutex) < 0) {
412 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
413 }
414}
415
416void ser_mou_mov(SDL_MouseMotionEvent *ev)
417{
418 if (SDL_LockMutex(cpu_mutex) < 0) {
419 fail("SDL_LockMutex() failed: %s", SDL_GetError());
420 }
421
422 mou_dx += ev->xrel;
423 mou_dy += ev->yrel;
424
425 if (SDL_UnlockMutex(cpu_mutex) < 0) {
426 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
427 }
428}
429
430void ser_mou_dn(SDL_MouseButtonEvent *ev)
431{
432 if (SDL_LockMutex(cpu_mutex) < 0) {
433 fail("SDL_LockMutex() failed: %s", SDL_GetError());
434 }
435
436 if (ev->button == SDL_BUTTON_LEFT) {
437 mou_l = true;
438 }
439 else if (ev->button == SDL_BUTTON_RIGHT) {
440 mou_r = true;
441 }
442 else {
443 return;
444 }
445
446 mou_ev();
447
448 if (SDL_UnlockMutex(cpu_mutex) < 0) {
449 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
450 }
451}
452
453void ser_mou_up(SDL_MouseButtonEvent *ev)
454{
455 if (SDL_LockMutex(cpu_mutex) < 0) {
456 fail("SDL_LockMutex() failed: %s", SDL_GetError());
457 }
458
459 if (ev->button == SDL_BUTTON_LEFT) {
460 mou_l = false;
461 }
462 else if (ev->button == SDL_BUTTON_RIGHT) {
463 mou_r = false;
464 }
465 else {
466 return;
467 }
468
469 mou_ev();
470
471 if (SDL_UnlockMutex(cpu_mutex) < 0) {
472 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
473 }
474}
475
476void ser_init(void)
477{
478 ver("ser init");
479
480 win = SDL_CreateWindow("Serial Console", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
481 WIN_W, WIN_H, 0);
482
483 if (win == NULL) {
484 fail("SDL_CreateWindow() failed: %s", SDL_GetError());
485 }
486
487 ser_win = SDL_GetWindowID(win);
488
489 if (ser_win == 0) {
490 fail("SDL_GetWindowID() failed: %s", SDL_GetError());
491 }
492
493 ren = SDL_CreateRenderer(win, -1, 0);
494
495 if (ren == NULL) {
496 fail("SDL_CreateRenderer() failed: %s", SDL_GetError());
497 }
498
499 SDL_AtomicSet(&frame, 1);
500
501 SDL_RWops *ops = SDL_RWFromFile(font, "rb");
502
503 if (ops == NULL) {
504 fail("error while opening font file %s: %s", font, SDL_GetError());
505 }
506
507 fon = TTF_OpenFontRW(ops, 1, 32);
508
509 if (fon == NULL) {
510 fail("error while loading font file %s: %s", font, TTF_GetError());
511 }
512
513 fon_h = TTF_FontLineSkip(fon);
514
515 if (TTF_GlyphMetrics(fon, 'X', NULL, NULL, NULL, NULL, &fon_w) < 0) {
516 fail("error while measuring font width: %s", TTF_GetError());
517 }
518
519 sur_w = CON_W * fon_w;
520 sur_h = CON_H * fon_h;
521
522 sur = SDL_CreateRGBSurface(0, sur_w, sur_h, 32, 0, 0, 0, 0);
523
524 if (sur == NULL) {
525 fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
526 }
527
528 for (int32_t y = 0; y < CON_H; ++y) {
529 for (int32_t x = 0; x < CON_W; ++x) {
530 mem[y][x] = ' ';
531 }
532 }
533}
534
535void ser_quit(void)
536{
537 ver("ser quit");
538
539 SDL_FreeSurface(sur);
540 TTF_CloseFont(fon);
541
542 SDL_DestroyRenderer(ren);
543 SDL_DestroyWindow(win);
544}
545
546bool ser_exec(void)
547{
548 ver3("ser exec");
549
550 if (bel > 0) {
551 --bel;
552
553 if (bel == BEL_CYC - 1 || bel == 0) {
554 SDL_AtomicAdd(&frame, 1);
555 }
556 }
557
558 mou_ev_chk();
559
560 return state[0].irq_r || state[0].irq_t || state[1].irq_r || state[1].irq_t;
561}
562
563uint32_t ser_read(uint32_t off, int32_t sz)
564{
565 ver2("ser rd %u:%d", off, sz * 8);
566
567 if (sz != 1 || off > 7) {
568 fail("invalid ser rd %u:%d", off, sz * 8);
569 }
570
571 int32_t rg = (int32_t)(off % 4);
572 int32_t un = (int32_t)(off / 4);
573
574 uint32_t rv;
575
576 switch (rg) {
577 case REG_IER_ISR:
578 rv = (uint32_t)(0xc0 | (state[un].rdr_ok ? 0x01 : 0x00));
579 state[un].irq_r = false;
580 state[un].irq_t = false;
581 ver2("ISR[%d] 0x%02x", un, rv);
582 break;
583
584 case REG_TDR_RDR:
585 rv = state[un].rdr;
586 state[un].rdr_ok = false;
587 ver2("RDR[%d] 0x%02x", un, rv);
588 break;
589
590 default:
591 rv = 0x00;
592 break;
593 }
594
595 if (!state[un].irq_r && !state[un].rdr_ok) {
596 xmit(un);
597 }
598
599 return rv;
600}
601
602void ser_write(uint32_t off, int32_t sz, uint32_t val)
603{
604 ver2("ser wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
605
606 if (sz != 1 || off > 7) {
607 fail("invalid ser wr %u:%d", off, sz * 8);
608 }
609
610 int32_t rg = (int32_t)(off % 4);
611 int32_t un = (int32_t)(off / 4);
612
613 switch (rg) {
614 case REG_TDR_RDR:
615 ver2("TDR[%d] 0x%02x", un, val);
616
617 if (un == 1) {
618 echo((uint8_t)val);
619 }
620 else {
621 mouse((uint8_t)val);
622 }
623
624 state[un].irq_t = true;
625 break;
626
627 default:
628 break;
629 }
630}
Note: See TracBrowser for help on using the repository browser.