wok diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/emu2/stuff/video.c Wed Nov 08 16:45:57 2023 +0000 1.3 @@ -0,0 +1,751 @@ 1.4 +#include "video.h" 1.5 +#include "codepage.h" 1.6 +#include "dbg.h" 1.7 +#include "emu.h" 1.8 + 1.9 +#include <errno.h> 1.10 +#include <fcntl.h> 1.11 +#include <stdio.h> 1.12 +#include <stdlib.h> 1.13 +#include <string.h> 1.14 +#include <sys/ioctl.h> 1.15 +#include <termios.h> 1.16 +#include <unistd.h> 1.17 + 1.18 +// Simulated character screen. 1.19 +// This is a copy of the currently displayed output in the terminal window. 1.20 +// We have a max of 128 rows of up to 256 columns, total of 32KB. 1.21 +static uint16_t term_screen[64][256]; 1.22 +// Current line in output, lines bellow this are not currently displayed. 1.23 +// This allows using only part of the terminal. 1.24 +static int output_row; 1.25 +// Current cursor row/column position in the terminal. 1.26 +static unsigned term_posx, term_posy, term_color, term_cursor; 1.27 +// Current terminal sizes 1.28 +static unsigned term_sx, term_sy; 1.29 +// Current emulated video sizes and cursor position 1.30 +static unsigned vid_posx[8], vid_posy[8], vid_cursor; 1.31 +static unsigned vid_sx, vid_sy, vid_color, vid_page; 1.32 +static unsigned vid_font_lines, vid_no_blank; 1.33 +// Signals that the terminal size needs updating 1.34 +static volatile int term_needs_update; 1.35 +// Terminal FD, allows video output even with redirection. 1.36 +static FILE *tty_file; 1.37 +// Video is already initialized 1.38 +static int video_initialized; 1.39 + 1.40 +// Forward 1.41 +static void term_goto_xy(unsigned x, unsigned y); 1.42 + 1.43 +// Signal handler - terminal size changed 1.44 +// TODO: not used yet. 1.45 +#if 0 1.46 +static void sigwinch_handler(int sig) 1.47 +{ 1.48 + // term_needs_upodate = 1; 1.49 +} 1.50 +#endif 1.51 + 1.52 +static void term_get_size(void) 1.53 +{ 1.54 + struct winsize ws; 1.55 + if(ioctl(fileno(tty_file), TIOCGWINSZ, &ws) != -1) 1.56 + { 1.57 + // TODO: perhaps restrict to "known" values 1.58 + term_sx = ws.ws_col; 1.59 + term_sy = ws.ws_row; 1.60 + if(term_sx < 40) 1.61 + term_sx = 40; 1.62 + else if(term_sx > 240) 1.63 + term_sx = 240; 1.64 + if(term_sy < 25) 1.65 + term_sy = 25; 1.66 + else if(term_sy > 64) 1.67 + term_sy = 64; 1.68 + } 1.69 + else 1.70 + { 1.71 + term_sx = 80; 1.72 + term_sy = 25; 1.73 + } 1.74 +} 1.75 + 1.76 +// Update posx/posy in BIOS memory 1.77 +static void update_posxy(void) 1.78 +{ 1.79 + int vid_size = vid_sy > 25 ? 0x20 : 0x10; 1.80 + memory[0x44C] = 0x00; 1.81 + memory[0x44D] = vid_size; 1.82 + memory[0x44E] = 0x00; 1.83 + memory[0x44F] = (vid_size * vid_page) & 0x7F; 1.84 + for(int i = 0; i < 8; i++) 1.85 + { 1.86 + memory[0x450 + i * 2] = vid_posx[i]; 1.87 + memory[0x451 + i * 2] = vid_posy[i]; 1.88 + } 1.89 + memory[0x462] = vid_page; 1.90 +} 1.91 + 1.92 +// Clears the terminal data - not the actual terminal screen 1.93 +static void clear_terminal(void) 1.94 +{ 1.95 + debug(debug_video, "clear terminal shadow\n"); 1.96 + // Clear screen terminal: 1.97 + for(int y = 0; y < 64; y++) 1.98 + for(int x = 0; x < 256; x++) 1.99 + term_screen[y][x] = 0x0720; 1.100 + output_row = -1; 1.101 + term_posx = 0; 1.102 + term_posy = 0; 1.103 + // Get current terminal size 1.104 + term_get_size(); 1.105 + putc('\r', tty_file); // Go to column 0 1.106 +} 1.107 + 1.108 +static void set_text_mode(int clear, int sx, int sy) 1.109 +{ 1.110 + debug(debug_video, "set text mode%s\n", clear ? " and clear" : ""); 1.111 + // Clear video screen 1.112 + if(clear) 1.113 + { 1.114 + uint16_t *vm = (uint16_t *)(memory + 0xB8000); 1.115 + for(int i = 0; i < 16384; i++) 1.116 + vm[i] = 0x0720; 1.117 + } 1.118 + for(int i = 0; i < 8; i++) 1.119 + { 1.120 + vid_posx[i] = 0; 1.121 + vid_posy[i] = 0; 1.122 + } 1.123 + vid_page = 0; 1.124 + vid_color = 0x07; 1.125 + vid_cursor = 1; 1.126 + // TODO: support other video modes 1.127 + vid_sx = sx; 1.128 + vid_sy = sy; 1.129 + vid_font_lines = 16; 1.130 + memory[0x449] = 0x03; // video mode 1.131 + memory[0x44A] = vid_sx; 1.132 + memory[0x484] = vid_sy - 1; 1.133 + update_posxy(); 1.134 +} 1.135 + 1.136 +static unsigned get_last_used_row(void) 1.137 +{ 1.138 + unsigned max = 0; 1.139 + for(unsigned y = 0; y < vid_sy; y++) 1.140 + for(unsigned x = 0; x < vid_sx; x++) 1.141 + if(term_screen[y][x] != 0x700 && term_screen[y][x] != 0x720) 1.142 + max = y + 1; 1.143 + return max; 1.144 +} 1.145 + 1.146 +static void exit_video(void) 1.147 +{ 1.148 + vid_cursor = 1; 1.149 + check_screen(); 1.150 + unsigned max = get_last_used_row(); 1.151 + term_goto_xy(0, max); 1.152 + fputs("\x1b[m", tty_file); 1.153 + fclose(tty_file); 1.154 + debug(debug_video, "exit video - row %d\n", max); 1.155 +} 1.156 + 1.157 +static void init_video(void) 1.158 +{ 1.159 + debug(debug_video, "starting video emulation.\n"); 1.160 + int tty_fd = open("/dev/tty", O_NOCTTY | O_WRONLY); 1.161 + if(tty_fd < 0) 1.162 + { 1.163 + print_error("error at open TTY, %s\n", strerror(errno)); 1.164 + exit(1); 1.165 + } 1.166 + tty_file = fdopen(tty_fd, "w"); 1.167 + atexit(exit_video); 1.168 + video_initialized = 1; 1.169 + 1.170 + // Fill the functionality table 1.171 + memory[0xC0100] = 0x08; // Only mode 3 supported 1.172 + memory[0xC0101] = 0x00; 1.173 + memory[0xC0102] = 0x00; 1.174 + memory[0xC0107] = 0x07; // Support 300, 350 and 400 scanlines 1.175 + memory[0xC0108] = 0x00; // Active character blocks? 1.176 + memory[0xC0109] = 0x00; // MAximum character blocks? 1.177 + memory[0xC0108] = 0xFF; // Support functions 1.178 + 1.179 + // Set video mode 1.180 + set_text_mode(1, 80, 25); 1.181 + clear_terminal(); 1.182 + term_needs_update = 0; 1.183 + term_cursor = 1; 1.184 + term_color = 0x07; 1.185 +} 1.186 + 1.187 +int video_active(void) 1.188 +{ 1.189 + return video_initialized; 1.190 +} 1.191 + 1.192 +static void set_color(uint8_t c) 1.193 +{ 1.194 + if(term_color != c) 1.195 + { 1.196 + static char cn[8] = "04261537"; 1.197 + fprintf(tty_file, (c & 0x80) ? "\x1b[%c;9%c;10%cm" : "\x1b[%c;3%c;4%cm", (c & 0x08) ? '1' : '0', cn[c & 7], 1.198 + cn[(c >> 4) & 7]); 1.199 + term_color = c; 1.200 + } 1.201 +} 1.202 + 1.203 +// Writes a DOS character to the current terminal position 1.204 +static void put_vc(uint8_t c) 1.205 +{ 1.206 + uint16_t uc = get_unicode(c); 1.207 + if(uc < 128) 1.208 + putc(uc, tty_file); 1.209 + else if(uc < 0x800) 1.210 + { 1.211 + putc(0xC0 | (uc >> 6), tty_file); 1.212 + putc(0x80 | (uc & 0x3F), tty_file); 1.213 + } 1.214 + else 1.215 + { 1.216 + putc(0xE0 | (uc >> 12), tty_file); 1.217 + putc(0x80 | ((uc >> 6) & 0x3F), tty_file); 1.218 + putc(0x80 | (uc & 0x3F), tty_file); 1.219 + } 1.220 +} 1.221 + 1.222 +// Move terminal cursor to the position 1.223 +static void term_goto_xy(unsigned x, unsigned y) 1.224 +{ 1.225 + if(term_posy < y && (int)term_posy < output_row) 1.226 + { 1.227 + int inc = (int)y < output_row ? y - term_posy : output_row - term_posy; 1.228 + fprintf(tty_file, "\x1b[%dB", inc); 1.229 + term_posy += inc; 1.230 + } 1.231 + if(term_posy < y) 1.232 + { 1.233 + putc('\r', tty_file); 1.234 + // Set background color to black, as some terminals insert lines with 1.235 + // the current background color. 1.236 + set_color(term_color & 0x0F); 1.237 + // TODO: Draw new line with background color from video screen 1.238 + for(unsigned i = term_posy; i < y; i++) 1.239 + putc('\n', tty_file); 1.240 + term_posx = 0; 1.241 + term_posy = y; 1.242 + } 1.243 + if(term_posy > y) 1.244 + { 1.245 + fprintf(tty_file, "\x1b[%dA", term_posy - y); 1.246 + term_posy = y; 1.247 + } 1.248 + if(x == 0 && term_posx != 0) 1.249 + { 1.250 + putc('\r', tty_file); 1.251 + term_posx = 0; 1.252 + } 1.253 + if(term_posx < x) 1.254 + { 1.255 + fprintf(tty_file, "\x1b[%dC", x - term_posx); 1.256 + term_posx = x; 1.257 + } 1.258 + if(term_posx > x) 1.259 + { 1.260 + fprintf(tty_file, "\x1b[%dD", term_posx - x); 1.261 + term_posx = x; 1.262 + } 1.263 +} 1.264 + 1.265 +// Outputs a character with the given attributes at the given position 1.266 +static void put_vc_xy(uint8_t vc, uint8_t color, unsigned x, unsigned y) 1.267 +{ 1.268 + term_goto_xy(x, y); 1.269 + set_color(color); 1.270 + 1.271 + put_vc(vc); 1.272 + term_posx++; 1.273 + if(term_posx >= term_sx) 1.274 + { 1.275 + term_posx = 0; 1.276 + term_posy++; 1.277 + } 1.278 + 1.279 + if(output_row < (int)term_posy) 1.280 + output_row = term_posy; 1.281 +} 1.282 + 1.283 +// Compares current screen with memory data 1.284 +void check_screen(void) 1.285 +{ 1.286 + // Exit if not in video mode 1.287 + if(!video_initialized) 1.288 + return; 1.289 + 1.290 + uint16_t memp = (vid_page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000); 1.291 + uint16_t *vm = (uint16_t *)(memory + 0xB8000 + memp); 1.292 + unsigned max = output_row + 1; 1.293 + for(unsigned y = output_row + 1; y < vid_sy; y++) 1.294 + for(unsigned x = 0; x < vid_sx; x++) 1.295 + if(vm[x + y * vid_sx] != term_screen[y][x]) 1.296 + max = y + 1; 1.297 + 1.298 + for(unsigned y = 0; y < max; y++) 1.299 + for(unsigned x = 0; x < vid_sx; x++) 1.300 + { 1.301 + int16_t vc = vm[x + y * vid_sx]; 1.302 + if(vc != term_screen[y][x]) 1.303 + { 1.304 + // Output character 1.305 + term_screen[y][x] = vc; 1.306 + put_vc_xy(vc & 0xFF, vc >> 8, x, y); 1.307 + } 1.308 + } 1.309 + if(term_cursor != vid_cursor) 1.310 + { 1.311 + term_cursor = vid_cursor; 1.312 + if(vid_cursor) 1.313 + fputs("\x1b[?25h", tty_file); 1.314 + else 1.315 + fputs("\x1b[?25l", tty_file); 1.316 + } 1.317 + if(term_cursor) 1.318 + // Move cursor 1.319 + term_goto_xy(vid_posx[vid_page], vid_posy[vid_page]); 1.320 + fflush(tty_file); 1.321 +} 1.322 + 1.323 +static void vid_scroll_up(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, int n, 1.324 + int page) 1.325 +{ 1.326 + debug(debug_video, "scroll up %d: (%d, %d) - (%d, %d)\n", n, x0, y0, x1, y1); 1.327 + 1.328 + // Check parameters 1.329 + if(x1 >= vid_sx) 1.330 + x1 = vid_sx - 1; 1.331 + if(y1 >= vid_sy) 1.332 + y1 = vid_sy - 1; 1.333 + if(y0 > y1 || x0 > x1) 1.334 + return; 1.335 + if(n > y1 - y0 + 1 || !n) 1.336 + n = y1 + 1 - y0; 1.337 + 1.338 + // Scroll TERMINAL if we are scrolling (almost) the entire screen 1.339 + if(y0 == 0 && y1 >= vid_sy - 2 && x0 < 2 && x1 >= vid_sx - 2) 1.340 + { 1.341 + // Update screen before 1.342 + check_screen(); 1.343 + int m = n > output_row + 1 ? output_row + 1 : n; 1.344 + if(term_posy < m) 1.345 + term_goto_xy(0, m); 1.346 + output_row -= m; 1.347 + term_posy -= m; 1.348 + for(unsigned y = 0; y + m < term_sy; y++) 1.349 + for(unsigned x = 0; x < term_sx; x++) 1.350 + term_screen[y][x] = term_screen[y + m][x]; 1.351 + for(unsigned y = term_sy - m; y < term_sy; y++) 1.352 + for(unsigned x = 0; x < term_sx; x++) 1.353 + term_screen[y][x] = 0x0720; 1.354 + } 1.355 + 1.356 + // Scroll VIDEO 1.357 + uint16_t memp = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000); 1.358 + uint16_t *vm = (uint16_t *)(memory + 0xB8000 + memp); 1.359 + for(unsigned y = y0; y + n <= y1; y++) 1.360 + for(unsigned x = x0; x <= x1; x++) 1.361 + vm[x + y * vid_sx] = vm[x + y * vid_sx + n * vid_sx]; 1.362 + // Set last rows 1.363 + for(unsigned y = y1 - (n - 1); y <= y1; y++) 1.364 + for(unsigned x = x0; x <= x1; x++) 1.365 + vm[x + y * vid_sx] = (vid_color << 8) + 0x20; 1.366 +} 1.367 + 1.368 +static void vid_scroll_dwn(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, unsigned n, 1.369 + int page) 1.370 +{ 1.371 + debug(debug_video, "scroll down %d: (%d, %d) - (%d, %d)\n", n, x0, y0, x1, y1); 1.372 + 1.373 + // Check parameters 1.374 + if(x1 >= vid_sx) 1.375 + x1 = vid_sx - 1; 1.376 + if(y1 >= vid_sy) 1.377 + y1 = vid_sy - 1; 1.378 + if(y0 > y1 || x0 > x1) 1.379 + return; 1.380 + if(n > y1 - y0 + 1 || !n) 1.381 + n = y1 + 1 - y0; 1.382 + 1.383 + // TODO: try to scroll TERMINAL 1.384 + 1.385 + // Scroll VIDEO 1.386 + uint16_t memp = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000); 1.387 + uint16_t *vm = (uint16_t *)(memory + 0xB8000 + memp); 1.388 + for(unsigned y = y1; y >= y0 + n; y--) 1.389 + for(unsigned x = x0; x <= x1; x++) 1.390 + vm[x + y * vid_sx] = vm[x + y * vid_sx - n * vid_sx]; 1.391 + // Set first rows 1.392 + for(unsigned y = y0; y < y0 + n; y++) 1.393 + for(unsigned x = x0; x <= x1; x++) 1.394 + vm[x + y * vid_sx] = (vid_color << 8) + 0x20; 1.395 +} 1.396 + 1.397 +static void set_xy(unsigned x, unsigned y, uint16_t c, uint16_t mask, int page) 1.398 +{ 1.399 + uint16_t mem = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000); 1.400 + uint16_t *vm = (uint16_t *)(memory + 0xB8000 + mem); 1.401 + vm[x + y * vid_sx] = (vm[x + y * vid_sx] & mask) | c; 1.402 +} 1.403 + 1.404 +static uint16_t get_xy(unsigned x, unsigned y, int page) 1.405 +{ 1.406 + uint16_t mem = (page & 7) * (vid_sy > 25 ? 0x2000 : 0x1000); 1.407 + uint16_t *vm = (uint16_t *)(memory + 0xB8000 + mem); 1.408 + return vm[x + y * vid_sx]; 1.409 +} 1.410 + 1.411 +static void video_putchar(uint8_t ch, uint16_t at, int page) 1.412 +{ 1.413 + page = page & 7; 1.414 + if(ch == 0x0A) 1.415 + { 1.416 + vid_posy[page]++; 1.417 + while(vid_posy[page] >= vid_sy) 1.418 + { 1.419 + vid_posy[page] = vid_sy - 1; 1.420 + vid_scroll_up(0, 0, vid_sx - 1, vid_sy - 1, 1, page); 1.421 + } 1.422 + } 1.423 + else if(ch == 0x0D) 1.424 + vid_posx[page] = 0; 1.425 + else if(ch == 0x08) 1.426 + { 1.427 + if(vid_posx[page] > 0) 1.428 + vid_posx[page]--; 1.429 + } 1.430 + else 1.431 + { 1.432 + if(at & 0xFFFF) 1.433 + set_xy(vid_posx[page], vid_posy[page], ch, 0xFF00, page); 1.434 + else 1.435 + set_xy(vid_posx[page], vid_posy[page], ch + (at << 8), 0, page); 1.436 + vid_posx[page]++; 1.437 + if(vid_posx[page] >= vid_sx) 1.438 + { 1.439 + vid_posx[page] = 0; 1.440 + vid_posy[page]++; 1.441 + while(vid_posy[page] >= vid_sy) 1.442 + { 1.443 + vid_posy[page] = vid_sy - 1; 1.444 + vid_scroll_up(0, 0, vid_sx - 1, vid_sy - 1, 1, page); 1.445 + } 1.446 + } 1.447 + } 1.448 + update_posxy(); 1.449 +} 1.450 + 1.451 +void video_putch(char ch) 1.452 +{ 1.453 + debug(debug_video, "putchar %02x at (%d,%d)\n", ch & 0xFF, vid_posx[vid_page], 1.454 + vid_posy[vid_page]); 1.455 + video_putchar(ch, 0xFF00, vid_page); 1.456 +} 1.457 + 1.458 +// VIDEO int 1.459 +void int10() 1.460 +{ 1.461 + debug(debug_int, "V-10%04X: BX=%04X\n", cpuGetAX(), cpuGetBX()); 1.462 + debug(debug_video, "V-10%04X: BX=%04X\n", cpuGetAX(), cpuGetBX()); 1.463 + if(!video_initialized) 1.464 + init_video(); 1.465 + unsigned ax = cpuGetAX(); 1.466 + switch(ax >> 8) 1.467 + { 1.468 + case 0x00: // SET VIDEO MODE 1.469 + if((ax & 0x7F) > 3) 1.470 + debug(debug_video, "-> SET GRAPHICS MODE %x<-\n", ax & 0xFF); 1.471 + else 1.472 + { 1.473 + set_text_mode((ax & 0x80) == 0, (ax < 2) ? 40 : 80, 25); 1.474 + vid_no_blank = ax & 0x80; 1.475 + } 1.476 + break; 1.477 + case 0x01: // SET CURSOR SHAPE 1.478 + if((cpuGetCX() & 0x6000) == 0x2000) // Hide cursor 1.479 + vid_cursor = 0; 1.480 + else 1.481 + vid_cursor = 1; 1.482 + break; 1.483 + case 0x02: // SET CURSOR POS 1.484 + { 1.485 + int page = (cpuGetBX() >> 8) & 7; 1.486 + vid_posx[page] = cpuGetDX() & 0xFF; 1.487 + vid_posy[page] = cpuGetDX() >> 8; 1.488 + if(vid_posx[page] >= vid_sx) 1.489 + vid_posx[page] = vid_sx - 1; 1.490 + if(vid_posy[page] >= vid_sy) 1.491 + vid_posy[page] = vid_sy - 1; 1.492 + update_posxy(); 1.493 + break; 1.494 + } 1.495 + case 0x03: // GET CURSOR POS 1.496 + { 1.497 + int page = (cpuGetBX() >> 8) & 7; 1.498 + cpuSetDX(vid_posx[page] + (vid_posy[page] << 8)); 1.499 + cpuSetCX(0x0010); 1.500 + break; 1.501 + } 1.502 + case 0x05: // SELECT DISPLAY PAGE 1.503 + if((ax & 0xFF) > 7) 1.504 + debug(debug_video, "WARN: Select display page > 7!\n"); 1.505 + else 1.506 + { 1.507 + vid_page = ax & 7; 1.508 + update_posxy(); 1.509 + } 1.510 + break; 1.511 + case 0x06: // SCROLL UP WINDOW 1.512 + { 1.513 + uint16_t cx = cpuGetCX(), dx = cpuGetDX(); 1.514 + vid_color = cpuGetBX() >> 8; 1.515 + vid_scroll_up(cx, cx >> 8, dx, dx >> 8, ax & 0xFF, vid_page); 1.516 + break; 1.517 + } 1.518 + case 0x07: // SCROLL DOWN WINDOW 1.519 + { 1.520 + uint16_t cx = cpuGetCX(), dx = cpuGetDX(); 1.521 + vid_color = cpuGetBX() >> 8; 1.522 + vid_scroll_dwn(cx, cx >> 8, dx, dx >> 8, ax & 0xFF, vid_page); 1.523 + break; 1.524 + } 1.525 + case 0x08: // READ CHAR AT CURSOR 1.526 + { 1.527 + int page = (cpuGetBX() >> 8) & 7; 1.528 + cpuSetAX(get_xy(vid_posx[page], vid_posy[page], page)); 1.529 + break; 1.530 + } 1.531 + case 0x09: // WRITE CHAR AT CURSOR 1.532 + case 0x0A: // WRITE CHAR ONLY AT CURSOR 1.533 + { 1.534 + int page = (cpuGetBX() >> 8) & 7; 1.535 + uint16_t px = vid_posx[page]; 1.536 + uint16_t py = vid_posy[page]; 1.537 + uint16_t mask = (ax & 0x0100) ? 0 : 0xFF00; 1.538 + uint16_t ch = ((ax & 0xFF) | (cpuGetBX() << 8)) & ~mask; 1.539 + for(int i = cpuGetCX(); i > 0; i--) 1.540 + { 1.541 + set_xy(px, py, ch, mask, page); 1.542 + px++; 1.543 + if(px >= vid_sx) 1.544 + { 1.545 + px = 0; 1.546 + py++; 1.547 + if(py >= vid_sy) 1.548 + py = 0; 1.549 + } 1.550 + } 1.551 + break; 1.552 + } 1.553 + case 0x0E: // TELETYPE OUTPUT 1.554 + video_putchar(ax, 0xFF00, (cpuGetBX() >> 8) & 7); 1.555 + break; 1.556 + case 0x0F: // GET CURRENT VIDEO MODE 1.557 + cpuSetAX((vid_sx << 8) | 0x0003 | vid_no_blank); // 80x25 mode 1.558 + cpuSetBX((vid_page << 8) | (0xFF & cpuGetBX())); 1.559 + break; 1.560 + case 0x10: 1.561 + if(ax == 0x1002) // TODO: Set pallete registers - ignore 1.562 + break; 1.563 + else if(ax == 0x1003) // TODO: Set blinking state 1.564 + break; 1.565 + debug(debug_video, "UNHANDLED INT 10, AX=%04x\n", ax); 1.566 + break; 1.567 + case 0x11: 1.568 + if(ax == 0x1130) 1.569 + { 1.570 + cpuSetDX((vid_sy - 1) & 0xFF); 1.571 + cpuSetCX(vid_font_lines); 1.572 + } 1.573 + else if(ax == 0x1104 || ax == 0x1111 || ax == 0x1114) 1.574 + { 1.575 + // Clear end-of-screen 1.576 + unsigned max = get_last_used_row(); 1.577 + debug(debug_video, "set 25 lines mode %d\n", max); 1.578 + if(max > 25) 1.579 + { 1.580 + term_goto_xy(0, 24); 1.581 + set_color(0x07); 1.582 + fputs("\x1b[J", tty_file); 1.583 + for(int y = 25; y < 64; y++) 1.584 + for(int x = 0; x < 256; x++) 1.585 + term_screen[y][x] = 0x0720; 1.586 + if(output_row > 24) 1.587 + output_row = 24; 1.588 + } 1.589 + // Set 8x16 font - 80x25 mode: 1.590 + vid_sy = 25; 1.591 + vid_font_lines = 16; 1.592 + memory[0x484] = vid_sy - 1; 1.593 + update_posxy(); 1.594 + } 1.595 + else if(ax == 0x1102 || ax == 0x1112) 1.596 + { 1.597 + // Set 8x8 font - 80x43 or 80x50 mode: 1.598 + debug(debug_video, "set 43/50 lines mode\n"); 1.599 + // Hack - QBASIC.EXE assumes that the mode is always 50 lines on VGA, 1.600 + // and *sets* the height into the BIOS area! 1.601 + if(memory[0x484] > 42) 1.602 + vid_sy = 50; 1.603 + else 1.604 + vid_sy = 43; 1.605 + vid_font_lines = 8; 1.606 + memory[0x484] = vid_sy - 1; 1.607 + update_posxy(); 1.608 + } 1.609 + break; 1.610 + case 0x12: // ALT FUNCTION SELECT 1.611 + { 1.612 + int bl = cpuGetBX() & 0xFF; 1.613 + if(bl == 0x10) // GET EGA INFO 1.614 + { 1.615 + cpuSetBX(0x0003); 1.616 + cpuSetCX(0x0000); 1.617 + cpuSetAX(0); 1.618 + } 1.619 + else if(bl == 0x30) // SET VERTICAL RESOLUTION 1.620 + { 1.621 + // TODO: select 25/28 lines 1.622 + cpuSetAX(0x1212); 1.623 + } 1.624 + else 1.625 + debug(debug_video, "UNHANDLED INT 10, AH=12 BL=%02x\n", bl); 1.626 + } 1.627 + break; 1.628 + case 0x13: // WRITE STRING 1.629 + { 1.630 + int page = (cpuGetBX() >> 8) & 7; 1.631 + vid_posx[page] = cpuGetDX() & 0xFF; 1.632 + vid_posy[page] = cpuGetDX() >> 8; 1.633 + if(vid_posx[page] >= vid_sx) 1.634 + vid_posx[page] = vid_sx - 1; 1.635 + if(vid_posy[page] >= vid_sy) 1.636 + vid_posy[page] = vid_sy - 1; 1.637 + int save_posx = vid_posx[page]; 1.638 + int save_posy = vid_posy[page]; 1.639 + int addr = cpuGetAddrES(cpuGetBP()); 1.640 + int cnt = cpuGetCX(); 1.641 + if(ax & 2) 1.642 + { 1.643 + while(cnt && addr < 0xFFFFF) 1.644 + { 1.645 + video_putchar(memory[addr], memory[addr + 1], page); 1.646 + addr += 2; 1.647 + cnt--; 1.648 + } 1.649 + } 1.650 + else 1.651 + { 1.652 + uint8_t at = cpuGetBX() >> 8; 1.653 + while(cnt && addr <= 0xFFFFF) 1.654 + { 1.655 + video_putchar(memory[addr], at, page); 1.656 + addr++; 1.657 + cnt--; 1.658 + } 1.659 + } 1.660 + if(!(ax & 1)) 1.661 + { 1.662 + vid_posx[page] = save_posx; 1.663 + vid_posy[page] = save_posy; 1.664 + } 1.665 + update_posxy(); 1.666 + } 1.667 + break; 1.668 + case 0x1A: // GET/SET DISPLAY COMBINATION CODE 1.669 + cpuSetAX(0x001A); 1.670 + cpuSetBX(0x0008); // VGA + analog color display 1.671 + break; 1.672 + case 0x1B: // STATE INFO 1.673 + if(cpuGetBX() == 0x0000) 1.674 + { 1.675 + int addr = cpuGetAddrES(cpuGetDI()); 1.676 + if(addr < 0xFFF00) 1.677 + { 1.678 + // Store state information 1.679 + memset(memory + addr, 0, 64); 1.680 + memory[addr + 0] = 0x00; 1.681 + memory[addr + 1] = 0x01; 1.682 + memory[addr + 2] = 0x00; 1.683 + memory[addr + 3] = 0xC0; // static-func table at C000:0000 1.684 + memory[addr + 4] = 0x03; // Video mode 1.685 + memory[addr + 5] = vid_sx; 1.686 + memory[addr + 6] = vid_sx >> 8; 1.687 + for(int i = 0; i < 8; i++) 1.688 + { 1.689 + memory[addr + 11 + i * 2] = vid_posx[i]; 1.690 + memory[addr + 12 + i * 2] = vid_posy[i]; 1.691 + } 1.692 + memory[addr + 27] = vid_cursor * 6; // cursor start scanline 1.693 + memory[addr + 28] = vid_cursor * 7; // cursor end scanline 1.694 + memory[addr + 29] = 0; // current page 1.695 + memory[addr + 30] = 0xD4; 1.696 + memory[addr + 31] = 0x03; // CRTC port: 03D4 1.697 + memory[addr + 34] = vid_sy; 1.698 + memory[addr + 35] = vid_font_lines; 1.699 + memory[addr + 36] = 0x00; // font lines: 0010 1.700 + memory[addr + 39] = 0x10; 1.701 + memory[addr + 40] = 0x00; // # of colors: 0010 1.702 + memory[addr + 41] = vid_sy > 25 ? 4 : 8; // # of pages 1.703 + memory[addr + 42] = 2; // # of scan-lines - get from vid_sy 1.704 + memory[addr + 49] = 3; // 256k memory 1.705 + cpuSetAX(0x1B1B); 1.706 + } 1.707 + } 1.708 + break; 1.709 + case 0xEF: // TEST MSHERC.COM DISPLAY TYPE 1.710 + // Ignored 1.711 + break; 1.712 + default: 1.713 + debug(debug_video, "UNHANDLED INT 10, AX=%04x\n", ax); 1.714 + } 1.715 +} 1.716 + 1.717 +// CRTC port emulation, some software use it to fix "snow" in CGA modes. 1.718 +static uint8_t crtc_port; 1.719 +static uint16_t crtc_cursor_loc; 1.720 + 1.721 +uint8_t video_crtc_read(int port) 1.722 +{ 1.723 + if(port & 1) 1.724 + { 1.725 + if(crtc_port == 0x0E) 1.726 + return crtc_cursor_loc >> 8; 1.727 + if(crtc_port == 0x0F) 1.728 + return crtc_cursor_loc; 1.729 + else 1.730 + return 0; 1.731 + } 1.732 + else 1.733 + return crtc_port; 1.734 +} 1.735 + 1.736 +void video_crtc_write(int port, uint8_t value) 1.737 +{ 1.738 + if(port & 1) 1.739 + { 1.740 + if(crtc_port == 0x0E) 1.741 + crtc_cursor_loc = (crtc_cursor_loc & 0xFF) | (value << 8); 1.742 + if(crtc_port == 0x0F) 1.743 + crtc_cursor_loc = (crtc_cursor_loc & 0xFF00) | (value); 1.744 + else 1.745 + debug(debug_video, "CRTC port write [%02x] <- %02x\n", crtc_port, value); 1.746 + } 1.747 + else 1.748 + crtc_port = value; 1.749 +} 1.750 + 1.751 +int video_get_col(void) 1.752 +{ 1.753 + return vid_posx[vid_page]; 1.754 +}