/* * (C) Copyright 1992, ..., 2006 the "DOSEMU-Development-Team". * * for details see file COPYING.DOSEMU in the DOSEMU distribution */ #include "config.h" #include #include "emu.h" #include "vgaemu.h" #include "remap.h" #include "vgatext.h" #include "render.h" #include "video.h" RemapObject remap_obj; int remap_features; static struct render_system *Render; /* * Draw a text string for bitmap fonts. * The attribute is the VGA color/mono text attribute. */ static void bitmap_draw_string(int x, int y, unsigned char *text, int len, Bit8u attr) { RectArea ra = convert_bitmap_string(x, y, text, len, attr); /* put_ximage uses display, mainwindow, gc, ximage */ X_printf("image at %d %d %d %d\n", ra.x, ra.y, ra.width, ra.height); if (ra.width) Render->put_image(ra.x, ra.y, ra.width, ra.height); } static void bitmap_draw_line(int x, int y, int len) { RectArea ra; ra = draw_bitmap_line(x, y, len); if (ra.width) Render->put_image(ra.x, ra.y, ra.width, ra.height); } static void bitmap_draw_text_cursor(int x, int y, Bit8u attr, int start, int end, Boolean focus) { RectArea ra; ra = draw_bitmap_cursor(x, y, attr, start, end, focus); if (ra.width) Render->put_image(ra.x, ra.y, ra.width, ra.height); } static void bitmap_set_text_palette(DAC_entry col) { /* done by remapper */ } static struct text_system Text_bitmap = { bitmap_draw_string, bitmap_draw_line, bitmap_draw_text_cursor, bitmap_set_text_palette, }; int register_render_system(struct render_system *render_system) { Render = render_system; register_text_system(&Text_bitmap); return 1; } /* * Initialize the interface between the VGA emulator and X. * Check if X's color depth is supported. */ int remapper_init(unsigned *image_mode, unsigned bits_per_pixel, int have_true_color, int have_shmap) { int remap_src_modes; unsigned ximage_mode; set_remap_debug_msg(stderr); if(have_true_color) { switch(bits_per_pixel) { case 1: ximage_mode = MODE_TRUE_1_MSB; break; case 15: ximage_mode = MODE_TRUE_15; break; case 16: ximage_mode = MODE_TRUE_16; break; case 24: ximage_mode = MODE_TRUE_24; break; case 32: ximage_mode = MODE_TRUE_32; break; default: ximage_mode = MODE_UNSUP; } } else { switch(bits_per_pixel) { case 8: ximage_mode = have_shmap ? MODE_TRUE_8 : MODE_PSEUDO_8; break; default: ximage_mode = MODE_UNSUP; } } remap_features = 0; if(config.X_lin_filt) remap_features |= RFF_LIN_FILT; if(config.X_bilin_filt) remap_features |= RFF_BILIN_FILT; remap_obj = remap_init(0, ximage_mode, 0); remap_src_modes = remap_obj.supported_src_modes; remap_done(&remap_obj); *image_mode = ximage_mode; return remap_src_modes; } /* * Free resources associated with remap_obj. */ void remapper_done(void) { remap_done(&remap_obj); } /* * Update the displayed image to match the current DAC entries. * Will redraw the *entire* screen if at least one color has changed. */ static Boolean refresh_truecolor(DAC_entry *col, int num) { Boolean colchanged = False; int i; for(i = 0; i < num; i++) { colchanged |= remap_obj.palette_update(&remap_obj, col[i].index, vga.dac.bits, col[i].r, col[i].g, col[i].b); } return colchanged; } /* returns True if the screen needs to be redrawn */ Boolean refresh_palette(DAC_entry *col) { int j = changed_vga_colors(col); if(Render->refresh_private_palette) { Render->refresh_private_palette(col, j); return vga.mode_class == TEXT; } return refresh_truecolor(col, j); } /* * Update the active colormap to reflect the current DAC entries. */ static void refresh_graphics_palette(void) { DAC_entry col[256]; if (refresh_palette(col)) dirty_all_video_pages(); } void get_mode_parameters(int *wx_res, int *wy_res, int ximage_mode, vga_emu_update_type *veut) { int x_res, y_res, w_x_res, w_y_res; int mode_type; w_x_res = x_res = vga.width; w_y_res = y_res = vga.height; /* scale really small modes to ~320x240: TODO: look at vga registers */ if(w_x_res <= 160 && w_x_res > 0) w_x_res = (320 / w_x_res) * w_x_res; if(w_y_res <= 120 && w_y_res > 0) w_y_res = (240 / w_y_res) * w_y_res; /* 320x200-style modes */ if(w_x_res <= 320 && w_y_res <= 240) { w_x_res *= config.X_mode13fact; w_y_res *= config.X_mode13fact; } else if(w_y_res <= 240) { /* 640x200-style modes */ w_y_res *= 2; } else if(w_x_res <= 320) { /* 320x480-style modes */ w_x_res *= 2; } if(config.X_winsize_x > 0 && config.X_winsize_y > 0) { w_x_res = config.X_winsize_x; w_y_res = config.X_winsize_y; } if(config.X_aspect_43) { w_y_res = (w_x_res * 3) >> 2; } remap_done(&remap_obj); switch(vga.mode_type) { case CGA: mode_type = vga.pixel_size == 2 ? MODE_CGA_2 : MODE_CGA_1; break; case HERC: mode_type = MODE_HERC; break; case PL1: mode_type = MODE_VGA_1; break; case PL2: mode_type = MODE_VGA_2; break; case PL4: mode_type = MODE_VGA_4; break; case P8: mode_type = MODE_PSEUDO_8; break; case P15: mode_type = MODE_TRUE_15; break; case P16: mode_type = MODE_TRUE_16; break; case P24: mode_type = MODE_TRUE_24; break; case P32: mode_type = MODE_TRUE_32; break; default: mode_type = 0; } v_printf("setmode: remap_init(0x%04x, 0x%04x, 0x%04x)\n", mode_type, ximage_mode, remap_features); remap_obj = remap_init(mode_type, ximage_mode, remap_features); if(!(remap_obj.state & (ROS_SCALE_ALL | ROS_SCALE_1 | ROS_SCALE_2))) { error("setmode: video mode 0x%02x not supported on this screen\n", vga.mode); /* why do we need a blank screen? */ /* Because many games use 16bpp only for the trailer, so not quitting * here may actually help. */ #if 0 leavedos(24); #endif } adjust_gamma(&remap_obj, config.X_gamma); if(!(remap_obj.state & ROS_SCALE_ALL)) { if((remap_obj.state & ROS_SCALE_2) && !(remap_obj.state & ROS_SCALE_1)) { w_x_res = x_res << 1; w_y_res = y_res << 1; } else { w_x_res = x_res; w_y_res = y_res; } } veut->base = vga.mem.base; veut->max_max_len = 0; veut->max_len = 0; veut->display_start = 0; veut->display_end = vga.scan_len * vga.line_compare; if (vga.line_compare > vga.height) veut->display_end = vga.scan_len * vga.height; veut->update_gran = 0; veut->update_pos = veut->display_start; remap_obj.src_resize(&remap_obj, vga.width, vga.height, vga.scan_len); *wx_res = w_x_res; *wy_res = w_y_res; } /* * Modify the current graphics mode. * Currently used to turn on/off chain4 addressing, change * the VGA screen size, change the DAC size. */ static void modify_mode(vga_emu_update_type *veut) { RemapObject tmp_ro; if(vga.reconfig.mem) { if(remap_obj.src_mode == MODE_PSEUDO_8 || remap_obj.src_mode == MODE_VGA_X || remap_obj.src_mode == MODE_VGA_4) { if (remap_obj.src_mode == MODE_VGA_4) tmp_ro = remap_init(MODE_VGA_4, remap_obj.dst_mode, remap_features); else tmp_ro = remap_init(vga.seq.addr_mode == 2 ? MODE_PSEUDO_8 : MODE_VGA_X, remap_obj.dst_mode, remap_features); *tmp_ro.dst_color_space = *remap_obj.dst_color_space; tmp_ro.dst_image = remap_obj.dst_image; tmp_ro.src_resize(&tmp_ro, vga.width, vga.height, vga.scan_len); tmp_ro.dst_resize(&tmp_ro, remap_obj.dst_width, remap_obj.dst_height, remap_obj.dst_scan_len); if(!(tmp_ro.state & (ROS_SCALE_ALL | ROS_SCALE_1 | ROS_SCALE_2))) { v_printf("modify_mode: no memory config change of current graphics mode supported\n"); remap_done(&tmp_ro); } else { v_printf("modify_mode: chain4 addressing turned %s\n", vga.mem.planes == 1 ? "on" : "off"); remap_done(&remap_obj); remap_obj = tmp_ro; } dirty_all_video_pages(); /* * The new remap object does not yet know about our colors. * So we have to force an update. -- sw */ dirty_all_vga_colors(); vga.reconfig.display = vga.reconfig.dac = 0; } vga.reconfig.mem = 0; } if(vga.reconfig.display) { remap_obj.src_resize(&remap_obj, vga.width, vga.height, vga.scan_len); v_printf( "modify_mode: geometry changed to %d x% d, scan_len = %d bytes\n", vga.width, vga.height, vga.scan_len ); dirty_all_video_pages(); vga.reconfig.display = 0; } if(vga.reconfig.dac) { vga.reconfig.dac = 0; v_printf("modify_mode: DAC bits = %d\n", vga.dac.bits); } veut->display_start = vga.display_start; veut->display_end = veut->display_start + vga.scan_len * vga.line_compare; if (vga.line_compare > vga.height) veut->display_end = veut->display_start + vga.scan_len * vga.height; if(vga.reconfig.mem || vga.reconfig.display) { v_printf("modify_mode: failed to modify current graphics mode\n"); } } /* * DANG_BEGIN_FUNCTION update_screen * * description: * Update the part of the screen which has changed, in text mode * and in graphics mode. Usually called from the SIGALRM handler. * * X_update_screen returns 0 if nothing was updated, 1 if the whole * screen was updated, and 2 for a partial update. * * It is called in arch/linux/async/signal.c::SIGALRM_call() as part * of a struct video_system (see top of X.c) every 50 ms or * every 10 ms if 2 was returned, depending somewhat on various config * options as e.g. config.X_updatefreq and VIDEO_CHECK_DIRTY. * At least it is supposed to do that. * * DANG_END_FUNCTION * * Text and graphics updates are separate functions now; the code was * too messy. -- sw */ static int update_graphics_loop(int update_offset, vga_emu_update_type *veut) { RectArea ra; int update_ret; #ifdef DEBUG_SHOW_UPDATE_AREA static int dsua_fg_color = 0; #endif while((update_ret = vga_emu_update(veut)) > 0) { remap_obj.src_image = veut->base + veut->display_start - update_offset; ra = remap_obj.remap_mem(&remap_obj, update_offset + veut->update_start - veut->display_start, veut->update_len); #ifdef DEBUG_SHOW_UPDATE_AREA XSetForeground(display, gc, dsua_fg_color++); XFillRectangle(display, mainwindow, gc, ra.x, ra.y, ra.width, ra.height); XSync(display, False); #endif Render->put_image(ra.x, ra.y, ra.width, ra.height); v_printf("update_graphics_screen: func = %s, display_start = 0x%04x, write_plane = %d, start %d, len %u, win (%d,%d),(%d,%d)\n", remap_obj.remap_func_name, vga.display_start, vga.mem.write_plane, veut->update_start, veut->update_len, ra.x, ra.y, ra.width, ra.height ); } return update_ret; } static int update_graphics_screen(vga_emu_update_type *veut) { int update_ret; unsigned wrap; if(vga.reconfig.mem || vga.reconfig.display || vga.reconfig.dac) modify_mode(veut); refresh_graphics_palette(); if(vga.display_start != veut->display_start) { veut->display_start = vga.display_start; veut->display_end = veut->display_start + vga.scan_len * vga.line_compare; if (vga.line_compare > vga.height) veut->display_end = veut->display_start + vga.scan_len * vga.height; dirty_all_video_pages(); } wrap = 0; if (veut->display_end > vga.mem.wrap) { wrap = veut->display_end - vga.mem.wrap; veut->display_end = vga.mem.wrap; } /* * You can now set the maximum update length, like this: * * veut->max_max_len = 20000; * * vga_emu_update() will return -1, if this limit is exceeded and there * are still invalid pages; `max_max_len' is only a rough estimate, the * actual upper limit will vary (might even be 25000 in the above * example). */ veut->max_len = veut->max_max_len; update_ret = update_graphics_loop(0, veut); if (wrap > 0) { /* This is for programs such as Commander Keen 4 that set the display_start close to the end of the video memory, and we need to wrap at 0xb0000 */ veut->display_end = wrap; wrap = veut->display_start; veut->display_start = 0; veut->max_len = veut->max_max_len; update_ret = update_graphics_loop(vga.mem.wrap - wrap, veut); veut->display_start = wrap; veut->display_end += vga.mem.wrap; } if (vga.line_compare < vga.height) { veut->display_start = 0; veut->display_end = vga.scan_len * (vga.height - vga.line_compare); veut->max_len = veut->max_max_len; update_ret = update_graphics_loop(vga.scan_len * vga.line_compare, veut); veut->display_start = vga.display_start; veut->display_end = veut->display_start + vga.scan_len * vga.line_compare; } return update_ret < 0 ? 2 : 1; } int update_screen(vga_emu_update_type *veut) { if(vga.config.video_off) { v_printf("update_screen: nothing done (video_off = 0x%x)\n", vga.config.video_off); return 1; } return vga.mode_class == TEXT ? update_text_screen() : update_graphics_screen(veut); }