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