wok view emu2/stuff/video.c @ rev 25624

Add opendkim
author Pascal Bellard <pascal.bellard@slitaz.org>
date Wed Nov 08 16:45:57 2023 +0000 (6 months ago)
parents
children
line source
1 #include "video.h"
2 #include "codepage.h"
3 #include "dbg.h"
4 #include "emu.h"
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12 #include <termios.h>
13 #include <unistd.h>
15 // Simulated character screen.
16 // This is a copy of the currently displayed output in the terminal window.
17 // We have a max of 128 rows of up to 256 columns, total of 32KB.
18 static uint16_t term_screen[64][256];
19 // Current line in output, lines bellow this are not currently displayed.
20 // This allows using only part of the terminal.
21 static int output_row;
22 // Current cursor row/column position in the terminal.
23 static unsigned term_posx, term_posy, term_color, term_cursor;
24 // Current terminal sizes
25 static unsigned term_sx, term_sy;
26 // Current emulated video sizes and cursor position
27 static unsigned vid_posx[8], vid_posy[8], vid_cursor;
28 static unsigned vid_sx, vid_sy, vid_color, vid_page;
29 static unsigned vid_font_lines, vid_no_blank;
30 // Signals that the terminal size needs updating
31 static volatile int term_needs_update;
32 // Terminal FD, allows video output even with redirection.
33 static FILE *tty_file;
34 // Video is already initialized
35 static int video_initialized;
37 // Forward
38 static void term_goto_xy(unsigned x, unsigned y);
40 // Signal handler - terminal size changed
41 // TODO: not used yet.
42 #if 0
43 static void sigwinch_handler(int sig)
44 {
45 // term_needs_upodate = 1;
46 }
47 #endif
49 static void term_get_size(void)
50 {
51 struct winsize ws;
52 if(ioctl(fileno(tty_file), TIOCGWINSZ, &ws) != -1)
53 {
54 // TODO: perhaps restrict to "known" values
55 term_sx = ws.ws_col;
56 term_sy = ws.ws_row;
57 if(term_sx < 40)
58 term_sx = 40;
59 else if(term_sx > 240)
60 term_sx = 240;
61 if(term_sy < 25)
62 term_sy = 25;
63 else if(term_sy > 64)
64 term_sy = 64;
65 }
66 else
67 {
68 term_sx = 80;
69 term_sy = 25;
70 }
71 }
73 // Update posx/posy in BIOS memory
74 static void update_posxy(void)
75 {
76 int vid_size = vid_sy > 25 ? 0x20 : 0x10;
77 memory[0x44C] = 0x00;
78 memory[0x44D] = vid_size;
79 memory[0x44E] = 0x00;
80 memory[0x44F] = (vid_size * vid_page) & 0x7F;
81 for(int i = 0; i < 8; i++)
82 {
83 memory[0x450 + i * 2] = vid_posx[i];
84 memory[0x451 + i * 2] = vid_posy[i];
85 }
86 memory[0x462] = vid_page;
87 }
89 // Clears the terminal data - not the actual terminal screen
90 static void clear_terminal(void)
91 {
92 debug(debug_video, "clear terminal shadow\n");
93 // Clear screen terminal:
94 for(int y = 0; y < 64; y++)
95 for(int x = 0; x < 256; x++)
96 term_screen[y][x] = 0x0720;
97 output_row = -1;
98 term_posx = 0;
99 term_posy = 0;
100 // Get current terminal size
101 term_get_size();
102 putc('\r', tty_file); // Go to column 0
103 }
105 static void set_text_mode(int clear, int sx, int sy)
106 {
107 debug(debug_video, "set text mode%s\n", clear ? " and clear" : "");
108 // Clear video screen
109 if(clear)
110 {
111 uint16_t *vm = (uint16_t *)(memory + 0xB8000);
112 for(int i = 0; i < 16384; i++)
113 vm[i] = 0x0720;
114 }
115 for(int i = 0; i < 8; i++)
116 {
117 vid_posx[i] = 0;
118 vid_posy[i] = 0;
119 }
120 vid_page = 0;
121 vid_color = 0x07;
122 vid_cursor = 1;
123 // TODO: support other video modes
124 vid_sx = sx;
125 vid_sy = sy;
126 vid_font_lines = 16;
127 memory[0x449] = 0x03; // video mode
128 memory[0x44A] = vid_sx;
129 memory[0x484] = vid_sy - 1;
130 update_posxy();
131 }
133 static unsigned get_last_used_row(void)
134 {
135 unsigned max = 0;
136 for(unsigned y = 0; y < vid_sy; y++)
137 for(unsigned x = 0; x < vid_sx; x++)
138 if(term_screen[y][x] != 0x700 && term_screen[y][x] != 0x720)
139 max = y + 1;
140 return max;
141 }
143 static void exit_video(void)
144 {
145 vid_cursor = 1;
146 check_screen();
147 unsigned max = get_last_used_row();
148 term_goto_xy(0, max);
149 fputs("\x1b[m", tty_file);
150 fclose(tty_file);
151 debug(debug_video, "exit video - row %d\n", max);
152 }
154 static void init_video(void)
155 {
156 debug(debug_video, "starting video emulation.\n");
157 int tty_fd = open("/dev/tty", O_NOCTTY | O_WRONLY);
158 if(tty_fd < 0)
159 {
160 print_error("error at open TTY, %s\n", strerror(errno));
161 exit(1);
162 }
163 tty_file = fdopen(tty_fd, "w");
164 atexit(exit_video);
165 video_initialized = 1;
167 // Fill the functionality table
168 memory[0xC0100] = 0x08; // Only mode 3 supported
169 memory[0xC0101] = 0x00;
170 memory[0xC0102] = 0x00;
171 memory[0xC0107] = 0x07; // Support 300, 350 and 400 scanlines
172 memory[0xC0108] = 0x00; // Active character blocks?
173 memory[0xC0109] = 0x00; // MAximum character blocks?
174 memory[0xC0108] = 0xFF; // Support functions
176 // Set video mode
177 set_text_mode(1, 80, 25);
178 clear_terminal();
179 term_needs_update = 0;
180 term_cursor = 1;
181 term_color = 0x07;
182 }
184 int video_active(void)
185 {
186 return video_initialized;
187 }
189 static void set_color(uint8_t c)
190 {
191 if(term_color != c)
192 {
193 static char cn[8] = "04261537";
194 fprintf(tty_file, (c & 0x80) ? "\x1b[%c;9%c;10%cm" : "\x1b[%c;3%c;4%cm", (c & 0x08) ? '1' : '0', cn[c & 7],
195 cn[(c >> 4) & 7]);
196 term_color = c;
197 }
198 }
200 // Writes a DOS character to the current terminal position
201 static void put_vc(uint8_t c)
202 {
203 uint16_t uc = get_unicode(c);
204 if(uc < 128)
205 putc(uc, tty_file);
206 else if(uc < 0x800)
207 {
208 putc(0xC0 | (uc >> 6), tty_file);
209 putc(0x80 | (uc & 0x3F), tty_file);
210 }
211 else
212 {
213 putc(0xE0 | (uc >> 12), tty_file);
214 putc(0x80 | ((uc >> 6) & 0x3F), tty_file);
215 putc(0x80 | (uc & 0x3F), tty_file);
216 }
217 }
219 // Move terminal cursor to the position
220 static void term_goto_xy(unsigned x, unsigned y)
221 {
222 if(term_posy < y && (int)term_posy < output_row)
223 {
224 int inc = (int)y < output_row ? y - term_posy : output_row - term_posy;
225 fprintf(tty_file, "\x1b[%dB", inc);
226 term_posy += inc;
227 }
228 if(term_posy < y)
229 {
230 putc('\r', tty_file);
231 // Set background color to black, as some terminals insert lines with
232 // the current background color.
233 set_color(term_color & 0x0F);
234 // TODO: Draw new line with background color from video screen
235 for(unsigned i = term_posy; i < y; i++)
236 putc('\n', tty_file);
237 term_posx = 0;
238 term_posy = y;
239 }
240 if(term_posy > y)
241 {
242 fprintf(tty_file, "\x1b[%dA", term_posy - y);
243 term_posy = y;
244 }
245 if(x == 0 && term_posx != 0)
246 {
247 putc('\r', tty_file);
248 term_posx = 0;
249 }
250 if(term_posx < x)
251 {
252 fprintf(tty_file, "\x1b[%dC", x - term_posx);
253 term_posx = x;
254 }
255 if(term_posx > x)
256 {
257 fprintf(tty_file, "\x1b[%dD", term_posx - x);
258 term_posx = x;
259 }
260 }
262 // Outputs a character with the given attributes at the given position
263 static void put_vc_xy(uint8_t vc, uint8_t color, unsigned x, unsigned y)
264 {
265 term_goto_xy(x, y);
266 set_color(color);
268 put_vc(vc);
269 term_posx++;
270 if(term_posx >= term_sx)
271 {
272 term_posx = 0;
273 term_posy++;
274 }
276 if(output_row < (int)term_posy)
277 output_row = term_posy;
278 }
280 // Compares current screen with memory data
281 void check_screen(void)
282 {
283 // Exit if not in video mode
284 if(!video_initialized)
285 return;
287 uint16_t memp = (vid_page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000);
288 uint16_t *vm = (uint16_t *)(memory + 0xB8000 + memp);
289 unsigned max = output_row + 1;
290 for(unsigned y = output_row + 1; y < vid_sy; y++)
291 for(unsigned x = 0; x < vid_sx; x++)
292 if(vm[x + y * vid_sx] != term_screen[y][x])
293 max = y + 1;
295 for(unsigned y = 0; y < max; y++)
296 for(unsigned x = 0; x < vid_sx; x++)
297 {
298 int16_t vc = vm[x + y * vid_sx];
299 if(vc != term_screen[y][x])
300 {
301 // Output character
302 term_screen[y][x] = vc;
303 put_vc_xy(vc & 0xFF, vc >> 8, x, y);
304 }
305 }
306 if(term_cursor != vid_cursor)
307 {
308 term_cursor = vid_cursor;
309 if(vid_cursor)
310 fputs("\x1b[?25h", tty_file);
311 else
312 fputs("\x1b[?25l", tty_file);
313 }
314 if(term_cursor)
315 // Move cursor
316 term_goto_xy(vid_posx[vid_page], vid_posy[vid_page]);
317 fflush(tty_file);
318 }
320 static void vid_scroll_up(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, int n,
321 int page)
322 {
323 debug(debug_video, "scroll up %d: (%d, %d) - (%d, %d)\n", n, x0, y0, x1, y1);
325 // Check parameters
326 if(x1 >= vid_sx)
327 x1 = vid_sx - 1;
328 if(y1 >= vid_sy)
329 y1 = vid_sy - 1;
330 if(y0 > y1 || x0 > x1)
331 return;
332 if(n > y1 - y0 + 1 || !n)
333 n = y1 + 1 - y0;
335 // Scroll TERMINAL if we are scrolling (almost) the entire screen
336 if(y0 == 0 && y1 >= vid_sy - 2 && x0 < 2 && x1 >= vid_sx - 2)
337 {
338 // Update screen before
339 check_screen();
340 int m = n > output_row + 1 ? output_row + 1 : n;
341 if(term_posy < m)
342 term_goto_xy(0, m);
343 output_row -= m;
344 term_posy -= m;
345 for(unsigned y = 0; y + m < term_sy; y++)
346 for(unsigned x = 0; x < term_sx; x++)
347 term_screen[y][x] = term_screen[y + m][x];
348 for(unsigned y = term_sy - m; y < term_sy; y++)
349 for(unsigned x = 0; x < term_sx; x++)
350 term_screen[y][x] = 0x0720;
351 }
353 // Scroll VIDEO
354 uint16_t memp = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000);
355 uint16_t *vm = (uint16_t *)(memory + 0xB8000 + memp);
356 for(unsigned y = y0; y + n <= y1; y++)
357 for(unsigned x = x0; x <= x1; x++)
358 vm[x + y * vid_sx] = vm[x + y * vid_sx + n * vid_sx];
359 // Set last rows
360 for(unsigned y = y1 - (n - 1); y <= y1; y++)
361 for(unsigned x = x0; x <= x1; x++)
362 vm[x + y * vid_sx] = (vid_color << 8) + 0x20;
363 }
365 static void vid_scroll_dwn(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, unsigned n,
366 int page)
367 {
368 debug(debug_video, "scroll down %d: (%d, %d) - (%d, %d)\n", n, x0, y0, x1, y1);
370 // Check parameters
371 if(x1 >= vid_sx)
372 x1 = vid_sx - 1;
373 if(y1 >= vid_sy)
374 y1 = vid_sy - 1;
375 if(y0 > y1 || x0 > x1)
376 return;
377 if(n > y1 - y0 + 1 || !n)
378 n = y1 + 1 - y0;
380 // TODO: try to scroll TERMINAL
382 // Scroll VIDEO
383 uint16_t memp = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000);
384 uint16_t *vm = (uint16_t *)(memory + 0xB8000 + memp);
385 for(unsigned y = y1; y >= y0 + n; y--)
386 for(unsigned x = x0; x <= x1; x++)
387 vm[x + y * vid_sx] = vm[x + y * vid_sx - n * vid_sx];
388 // Set first rows
389 for(unsigned y = y0; y < y0 + n; y++)
390 for(unsigned x = x0; x <= x1; x++)
391 vm[x + y * vid_sx] = (vid_color << 8) + 0x20;
392 }
394 static void set_xy(unsigned x, unsigned y, uint16_t c, uint16_t mask, int page)
395 {
396 uint16_t mem = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000);
397 uint16_t *vm = (uint16_t *)(memory + 0xB8000 + mem);
398 vm[x + y * vid_sx] = (vm[x + y * vid_sx] & mask) | c;
399 }
401 static uint16_t get_xy(unsigned x, unsigned y, int page)
402 {
403 uint16_t mem = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000);
404 uint16_t *vm = (uint16_t *)(memory + 0xB8000 + mem);
405 return vm[x + y * vid_sx];
406 }
408 static void video_putchar(uint8_t ch, uint16_t at, int page)
409 {
410 page = page & 7;
411 if(ch == 0x0A)
412 {
413 vid_posy[page]++;
414 while(vid_posy[page] >= vid_sy)
415 {
416 vid_posy[page] = vid_sy - 1;
417 vid_scroll_up(0, 0, vid_sx - 1, vid_sy - 1, 1, page);
418 }
419 }
420 else if(ch == 0x0D)
421 vid_posx[page] = 0;
422 else if(ch == 0x08)
423 {
424 if(vid_posx[page] > 0)
425 vid_posx[page]--;
426 }
427 else
428 {
429 if(at & 0xFFFF)
430 set_xy(vid_posx[page], vid_posy[page], ch, 0xFF00, page);
431 else
432 set_xy(vid_posx[page], vid_posy[page], ch + (at << 8), 0, page);
433 vid_posx[page]++;
434 if(vid_posx[page] >= vid_sx)
435 {
436 vid_posx[page] = 0;
437 vid_posy[page]++;
438 while(vid_posy[page] >= vid_sy)
439 {
440 vid_posy[page] = vid_sy - 1;
441 vid_scroll_up(0, 0, vid_sx - 1, vid_sy - 1, 1, page);
442 }
443 }
444 }
445 update_posxy();
446 }
448 void video_putch(char ch)
449 {
450 debug(debug_video, "putchar %02x at (%d,%d)\n", ch & 0xFF, vid_posx[vid_page],
451 vid_posy[vid_page]);
452 video_putchar(ch, 0xFF00, vid_page);
453 }
455 // VIDEO int
456 void int10()
457 {
458 debug(debug_int, "V-10%04X: BX=%04X\n", cpuGetAX(), cpuGetBX());
459 debug(debug_video, "V-10%04X: BX=%04X\n", cpuGetAX(), cpuGetBX());
460 if(!video_initialized)
461 init_video();
462 unsigned ax = cpuGetAX();
463 switch(ax >> 8)
464 {
465 case 0x00: // SET VIDEO MODE
466 if((ax & 0x7F) > 3)
467 debug(debug_video, "-> SET GRAPHICS MODE %x<-\n", ax & 0xFF);
468 else
469 {
470 set_text_mode((ax & 0x80) == 0, (ax < 2) ? 40 : 80, 25);
471 vid_no_blank = ax & 0x80;
472 }
473 break;
474 case 0x01: // SET CURSOR SHAPE
475 if((cpuGetCX() & 0x6000) == 0x2000) // Hide cursor
476 vid_cursor = 0;
477 else
478 vid_cursor = 1;
479 break;
480 case 0x02: // SET CURSOR POS
481 {
482 int page = (cpuGetBX() >> 8) & 7;
483 vid_posx[page] = cpuGetDX() & 0xFF;
484 vid_posy[page] = cpuGetDX() >> 8;
485 if(vid_posx[page] >= vid_sx)
486 vid_posx[page] = vid_sx - 1;
487 if(vid_posy[page] >= vid_sy)
488 vid_posy[page] = vid_sy - 1;
489 update_posxy();
490 break;
491 }
492 case 0x03: // GET CURSOR POS
493 {
494 int page = (cpuGetBX() >> 8) & 7;
495 cpuSetDX(vid_posx[page] + (vid_posy[page] << 8));
496 cpuSetCX(0x0010);
497 break;
498 }
499 case 0x05: // SELECT DISPLAY PAGE
500 if((ax & 0xFF) > 7)
501 debug(debug_video, "WARN: Select display page > 7!\n");
502 else
503 {
504 vid_page = ax & 7;
505 update_posxy();
506 }
507 break;
508 case 0x06: // SCROLL UP WINDOW
509 {
510 uint16_t cx = cpuGetCX(), dx = cpuGetDX();
511 vid_color = cpuGetBX() >> 8;
512 vid_scroll_up(cx, cx >> 8, dx, dx >> 8, ax & 0xFF, vid_page);
513 break;
514 }
515 case 0x07: // SCROLL DOWN WINDOW
516 {
517 uint16_t cx = cpuGetCX(), dx = cpuGetDX();
518 vid_color = cpuGetBX() >> 8;
519 vid_scroll_dwn(cx, cx >> 8, dx, dx >> 8, ax & 0xFF, vid_page);
520 break;
521 }
522 case 0x08: // READ CHAR AT CURSOR
523 {
524 int page = (cpuGetBX() >> 8) & 7;
525 cpuSetAX(get_xy(vid_posx[page], vid_posy[page], page));
526 break;
527 }
528 case 0x09: // WRITE CHAR AT CURSOR
529 case 0x0A: // WRITE CHAR ONLY AT CURSOR
530 {
531 int page = (cpuGetBX() >> 8) & 7;
532 uint16_t px = vid_posx[page];
533 uint16_t py = vid_posy[page];
534 uint16_t mask = (ax & 0x0100) ? 0 : 0xFF00;
535 uint16_t ch = ((ax & 0xFF) | (cpuGetBX() << 8)) & ~mask;
536 for(int i = cpuGetCX(); i > 0; i--)
537 {
538 set_xy(px, py, ch, mask, page);
539 px++;
540 if(px >= vid_sx)
541 {
542 px = 0;
543 py++;
544 if(py >= vid_sy)
545 py = 0;
546 }
547 }
548 break;
549 }
550 case 0x0E: // TELETYPE OUTPUT
551 video_putchar(ax, 0xFF00, (cpuGetBX() >> 8) & 7);
552 break;
553 case 0x0F: // GET CURRENT VIDEO MODE
554 cpuSetAX((vid_sx << 8) | 0x0003 | vid_no_blank); // 80x25 mode
555 cpuSetBX((vid_page << 8) | (0xFF & cpuGetBX()));
556 break;
557 case 0x10:
558 if(ax == 0x1002) // TODO: Set pallete registers - ignore
559 break;
560 else if(ax == 0x1003) // TODO: Set blinking state
561 break;
562 debug(debug_video, "UNHANDLED INT 10, AX=%04x\n", ax);
563 break;
564 case 0x11:
565 if(ax == 0x1130)
566 {
567 cpuSetDX((vid_sy - 1) & 0xFF);
568 cpuSetCX(vid_font_lines);
569 }
570 else if(ax == 0x1104 || ax == 0x1111 || ax == 0x1114)
571 {
572 // Clear end-of-screen
573 unsigned max = get_last_used_row();
574 debug(debug_video, "set 25 lines mode %d\n", max);
575 if(max > 25)
576 {
577 term_goto_xy(0, 24);
578 set_color(0x07);
579 fputs("\x1b[J", tty_file);
580 for(int y = 25; y < 64; y++)
581 for(int x = 0; x < 256; x++)
582 term_screen[y][x] = 0x0720;
583 if(output_row > 24)
584 output_row = 24;
585 }
586 // Set 8x16 font - 80x25 mode:
587 vid_sy = 25;
588 vid_font_lines = 16;
589 memory[0x484] = vid_sy - 1;
590 update_posxy();
591 }
592 else if(ax == 0x1102 || ax == 0x1112)
593 {
594 // Set 8x8 font - 80x43 or 80x50 mode:
595 debug(debug_video, "set 43/50 lines mode\n");
596 // Hack - QBASIC.EXE assumes that the mode is always 50 lines on VGA,
597 // and *sets* the height into the BIOS area!
598 if(memory[0x484] > 42)
599 vid_sy = 50;
600 else
601 vid_sy = 43;
602 vid_font_lines = 8;
603 memory[0x484] = vid_sy - 1;
604 update_posxy();
605 }
606 break;
607 case 0x12: // ALT FUNCTION SELECT
608 {
609 int bl = cpuGetBX() & 0xFF;
610 if(bl == 0x10) // GET EGA INFO
611 {
612 cpuSetBX(0x0003);
613 cpuSetCX(0x0000);
614 cpuSetAX(0);
615 }
616 else if(bl == 0x30) // SET VERTICAL RESOLUTION
617 {
618 // TODO: select 25/28 lines
619 cpuSetAX(0x1212);
620 }
621 else
622 debug(debug_video, "UNHANDLED INT 10, AH=12 BL=%02x\n", bl);
623 }
624 break;
625 case 0x13: // WRITE STRING
626 {
627 int page = (cpuGetBX() >> 8) & 7;
628 vid_posx[page] = cpuGetDX() & 0xFF;
629 vid_posy[page] = cpuGetDX() >> 8;
630 if(vid_posx[page] >= vid_sx)
631 vid_posx[page] = vid_sx - 1;
632 if(vid_posy[page] >= vid_sy)
633 vid_posy[page] = vid_sy - 1;
634 int save_posx = vid_posx[page];
635 int save_posy = vid_posy[page];
636 int addr = cpuGetAddrES(cpuGetBP());
637 int cnt = cpuGetCX();
638 if(ax & 2)
639 {
640 while(cnt && addr < 0xFFFFF)
641 {
642 video_putchar(memory[addr], memory[addr + 1], page);
643 addr += 2;
644 cnt--;
645 }
646 }
647 else
648 {
649 uint8_t at = cpuGetBX() >> 8;
650 while(cnt && addr <= 0xFFFFF)
651 {
652 video_putchar(memory[addr], at, page);
653 addr++;
654 cnt--;
655 }
656 }
657 if(!(ax & 1))
658 {
659 vid_posx[page] = save_posx;
660 vid_posy[page] = save_posy;
661 }
662 update_posxy();
663 }
664 break;
665 case 0x1A: // GET/SET DISPLAY COMBINATION CODE
666 cpuSetAX(0x001A);
667 cpuSetBX(0x0008); // VGA + analog color display
668 break;
669 case 0x1B: // STATE INFO
670 if(cpuGetBX() == 0x0000)
671 {
672 int addr = cpuGetAddrES(cpuGetDI());
673 if(addr < 0xFFF00)
674 {
675 // Store state information
676 memset(memory + addr, 0, 64);
677 memory[addr + 0] = 0x00;
678 memory[addr + 1] = 0x01;
679 memory[addr + 2] = 0x00;
680 memory[addr + 3] = 0xC0; // static-func table at C000:0000
681 memory[addr + 4] = 0x03; // Video mode
682 memory[addr + 5] = vid_sx;
683 memory[addr + 6] = vid_sx >> 8;
684 for(int i = 0; i < 8; i++)
685 {
686 memory[addr + 11 + i * 2] = vid_posx[i];
687 memory[addr + 12 + i * 2] = vid_posy[i];
688 }
689 memory[addr + 27] = vid_cursor * 6; // cursor start scanline
690 memory[addr + 28] = vid_cursor * 7; // cursor end scanline
691 memory[addr + 29] = 0; // current page
692 memory[addr + 30] = 0xD4;
693 memory[addr + 31] = 0x03; // CRTC port: 03D4
694 memory[addr + 34] = vid_sy;
695 memory[addr + 35] = vid_font_lines;
696 memory[addr + 36] = 0x00; // font lines: 0010
697 memory[addr + 39] = 0x10;
698 memory[addr + 40] = 0x00; // # of colors: 0010
699 memory[addr + 41] = vid_sy > 25 ? 4 : 8; // # of pages
700 memory[addr + 42] = 2; // # of scan-lines - get from vid_sy
701 memory[addr + 49] = 3; // 256k memory
702 cpuSetAX(0x1B1B);
703 }
704 }
705 break;
706 case 0xEF: // TEST MSHERC.COM DISPLAY TYPE
707 // Ignored
708 break;
709 default:
710 debug(debug_video, "UNHANDLED INT 10, AX=%04x\n", ax);
711 }
712 }
714 // CRTC port emulation, some software use it to fix "snow" in CGA modes.
715 static uint8_t crtc_port;
716 static uint16_t crtc_cursor_loc;
718 uint8_t video_crtc_read(int port)
719 {
720 if(port & 1)
721 {
722 if(crtc_port == 0x0E)
723 return crtc_cursor_loc >> 8;
724 if(crtc_port == 0x0F)
725 return crtc_cursor_loc;
726 else
727 return 0;
728 }
729 else
730 return crtc_port;
731 }
733 void video_crtc_write(int port, uint8_t value)
734 {
735 if(port & 1)
736 {
737 if(crtc_port == 0x0E)
738 crtc_cursor_loc = (crtc_cursor_loc & 0xFF) | (value << 8);
739 if(crtc_port == 0x0F)
740 crtc_cursor_loc = (crtc_cursor_loc & 0xFF00) | (value);
741 else
742 debug(debug_video, "CRTC port write [%02x] <- %02x\n", crtc_port, value);
743 }
744 else
745 crtc_port = value;
746 }
748 int video_get_col(void)
749 {
750 return vid_posx[vid_page];
751 }