source: buchla-emu/emu/ser.c@ 4ed9bfe

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

Support data keys, X, E, and M.

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