diff options
author | Javier <dev.git@javispedro.com> | 2022-04-03 21:23:10 +0200 |
---|---|---|
committer | Javier <dev.git@javispedro.com> | 2022-04-03 21:23:10 +0200 |
commit | a5abb118e0e61587adfa7b4e4cc72948311381d3 (patch) | |
tree | b226705401bd0b4c78399c621cebc1fbab6003b9 | |
parent | 7d93442564b57c2d292df7f823c2115d3e0b8c12 (diff) | |
download | vbados-a5abb118e0e61587adfa7b4e4cc72948311381d3.tar.gz vbados-a5abb118e0e61587adfa7b4e4cc72948311381d3.zip |
try to handle graphical cursor rendering in a more generic way
-rw-r--r-- | dosmain.c | 19 | ||||
-rw-r--r-- | dostsr.c | 372 | ||||
-rw-r--r-- | dostsr.h | 17 | ||||
-rw-r--r-- | int10vga.h | 196 | ||||
-rw-r--r-- | int33.h | 9 | ||||
-rw-r--r-- | vbox.h | 9 |
6 files changed, 398 insertions, 224 deletions
@@ -24,6 +24,7 @@ #include <i86.h> #include "dlog.h" +#include "int33.h" #include "ps2.h" #include "vbox.h" #include "dostsr.h" @@ -85,7 +86,7 @@ static int set_integration(LPTSRDATA data, bool enable) printf("Disabled VirtualBox integration\n"); data->vbavail = false; } else { - printf("VirtualBox already disabled or not available\n"); + printf("VirtualBox integration already disabled or not available\n"); } } @@ -164,17 +165,17 @@ static bool check_if_driver_uninstallable(LPTSRDATA data) void (__interrupt __far *our_int33_handler)() = MK_FP(FP_SEG(data), FP_OFF(int33_isr)); if (cur_int33_handler != our_int33_handler) { - fprintf(stderr, "INT33 has been hooked by some other driver, removing anyway (will likely crash soon)\n"); + fprintf(stderr, "INT33 has been hooked by someone else, removing anyway\n"); return true; } #if USE_INT2F { - void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x33); + void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f); void (__interrupt __far *our_int2f_handler)() = MK_FP(FP_SEG(data), FP_OFF(int2f_isr)); if (cur_int2f_handler != our_int2f_handler) { - fprintf(stderr, "INT2F has been hooked by some other driver, removing anyway (will likely crash soon)\n"); + fprintf(stderr, "INT2F has been hooked by someone else, removing anyway\n"); return true; } } @@ -214,18 +215,10 @@ static int uninstall_driver(LPTSRDATA data) return 0; } -static unsigned int33_call(unsigned ax); -#pragma aux int33_call parm [ax] value [ax] = \ - "int 0x33"; - -static unsigned int2f_call(unsigned ax); -#pragma aux int2f_call parm [ax] value [ax] = \ - "int 0x2f"; - static int driver_reset(void) { printf("Reset mouse driver\n"); - return int33_call(0x0) == 0xFFFF; + return int33_reset() == 0xFFFF; } static int driver_not_found(void) @@ -26,6 +26,8 @@ #include "int33.h" #include "dostsr.h" +#define MSB_MASK 0x8000U + TSRDATA data; static const uint16_t default_cursor_graphic[] = { @@ -39,6 +41,7 @@ static const uint16_t default_cursor_graphic[] = { 0x0600, 0x0300, 0x0300, 0x0000 }; +/** Constraint current mouse position to the user-set window. */ static void bound_position_to_window(void) { if (data.pos.x < data.min.x) data.pos.x = data.min.x; @@ -47,24 +50,10 @@ static void bound_position_to_window(void) if (data.pos.y > data.max.y) data.pos.y = data.max.y; } -static inline bool is_text_mode(uint8_t mode) -{ - switch (mode) { - case 0: - case 1: - case 2: - case 3: // CGA text modes with 25 rows and variable columns - case 7: // MDA Mono text mode - return true; - default: - return false; - } -} - static void hide_text_cursor(void) { // Restore the character under the old position of the cursor - uint16_t __far *ch = get_video_char(data.screen_page, + uint16_t __far *ch = get_video_char(&data.video_mode, data.cursor_pos.x / 8, data.cursor_pos.y / 8); *ch = data.cursor_prev_char; data.cursor_visible = false; @@ -72,7 +61,7 @@ static void hide_text_cursor(void) static void show_text_cursor(void) { - uint16_t __far *ch = get_video_char(data.screen_page, + uint16_t __far *ch = get_video_char(&data.video_mode, data.pos.x / 8, data.pos.y / 8); data.cursor_prev_char = *ch; data.cursor_pos = data.pos; @@ -80,15 +69,17 @@ static void show_text_cursor(void) data.cursor_visible = true; } +/** Given the new position of the cursor, compute the both our source + * (i.e. the mouse cursor shape) and target clipping areas. + * @param cursor_pos cursor position + * @param start top left corner of clipping box in screen + * @param size size of the clipping box in screen as well as cursor shape + * @param offset top left corner of clipping box in mouse cursor shape */ static bool get_graphic_cursor_area(struct point __far *cursor_pos, struct point __far *start, struct point __far *size, struct point __far *offset) { - struct point screen_size; - screen_size.x = ((data.screen_max.x + 1) / data.screen_scale.x); - screen_size.y = ((data.screen_max.y + 1) / data.screen_scale.y); - start->x = (cursor_pos->x / data.screen_scale.x) - data.cursor_hotspot.x; start->y = (cursor_pos->y / data.screen_scale.y) - data.cursor_hotspot.y; size->x = GRAPHIC_CURSOR_WIDTH; @@ -97,133 +88,176 @@ static bool get_graphic_cursor_area(struct point __far *cursor_pos, offset->y = 0; // Start clipping around - if (start->x < 0) { + // Cursor is left/top of visible area + if (start->x <= -size->x) { + return false; + } else if (start->x < 0) { offset->x += -start->x; + size->x -= -start->x; start->x = 0; } - if (start->y < 0) { + if (start->y <= -size->y) { + return false; + } else if (start->y < 0) { offset->y += -start->y; + size->y -= -start->y; start->y = 0; } - if (start->x > screen_size.x) { + // Cursor is right/bottom of visible area + if (start->x > data.video_mode.pixels_width) { return false; // Don't render cursor - } else if (start->x + size->x > screen_size.x) { - size->x -= (start->x + size->x) - screen_size.x; + } else if (start->x + size->x > data.video_mode.pixels_width) { + size->x -= (start->x + size->x) - data.video_mode.pixels_width; } - if (start->y > screen_size.y) { + if (start->y > data.video_mode.pixels_height) { return false; - } else if (start->y + size->y > screen_size.y) { - size->y -= (start->y + size->y) - screen_size.y; + } else if (start->y + size->y > data.video_mode.pixels_height) { + size->y -= (start->y + size->y) - data.video_mode.pixels_height; } return true; } -static inline uint8_t * get_prev_graphic_cursor_scanline(unsigned y) +static inline uint8_t * get_prev_graphic_cursor_scanline(unsigned bytes_per_line, + unsigned num_lines, + unsigned plane, + unsigned y) { - return &data.cursor_prev_graphic[y * GRAPHIC_CURSOR_WIDTH]; + return &data.cursor_prev_graphic[((plane * num_lines) + y) * bytes_per_line]; } -static inline uint16_t get_graphic_cursor_and_mask(unsigned y) +static inline uint16_t get_graphic_cursor_and_mask_line(unsigned y) { return data.cursor_graphic[y]; } -static inline uint16_t get_graphic_cursor_xor_mask(unsigned y) +static inline uint16_t get_graphic_cursor_xor_mask_line(unsigned y) { return data.cursor_graphic[GRAPHIC_CURSOR_HEIGHT + y]; } +/** Compute the total number of bytes between start and end pixels, + * rounding up as necessary to cover all bytes. */ +static inline unsigned get_scanline_segment_bytes(unsigned bits_per_pixel, unsigned start, unsigned size) +{ + // Get starting byte (round down) + unsigned start_byte = (start * bits_per_pixel) / 8; + // Get end byte (round up) + unsigned end_byte = (((start + size) * bits_per_pixel) + (8-1)) / 8; + + return end_byte - start_byte; +} + +/** Creates a bitmask that extracts the topmost N bits of a byte. */ +static inline uint8_t build_pixel_mask(unsigned bits_per_pixel) +{ + if (bits_per_pixel == 1) return 0x80; + else if (bits_per_pixel == 2) return 0xC0; + else if (bits_per_pixel == 4) return 0xF0; + else return 0xFF; +} + +/** Hides the graphical mouse cursor, by restoring the contents of + * data.cursor_prev_graphic (i.e. what was below the cursor before we drew it) + * to video memory. */ static void hide_graphic_cursor(void) { - uint8_t __far *pixel; - uint8_t *cursor_prev; + struct modeinfo *info = &data.video_mode; struct point start, size, offset; - unsigned pixels_per_byte, y, x; + unsigned cursor_bytes_per_line; + unsigned plane, y; // Compute the area where the cursor is currently positioned if (!get_graphic_cursor_area(&data.cursor_pos, &start, &size, &offset)) { return; } - switch (data.screen_mode) { - case 4: - case 5: - case 6: // CGA modes - pixels_per_byte = data.screen_mode == 0x6 ? 8 : 4; + // For each scanline, we will copy this amount of bytes + cursor_bytes_per_line = get_scanline_segment_bytes(info->bits_per_pixel, + start.x, size.x); + + for (plane = 0; plane < info->num_planes; plane++) { + if (info->num_planes) vga_select_plane(plane); for (y = 0; y < size.y; y++) { - cursor_prev = get_prev_graphic_cursor_scanline(offset.y + y); - pixel = get_video_scanline(data.screen_mode, data.screen_page, start.y + y) - + start.x / pixels_per_byte; + uint8_t __far *line = get_video_scanline(info, start.y + y) + + (start.x * info->bits_per_pixel) / 8; + uint8_t *prev = get_prev_graphic_cursor_scanline(cursor_bytes_per_line, size.y, + plane, y); // Restore this scaline from cursor_prev - nfmemcpy(pixel, cursor_prev, - ((start.x % pixels_per_byte) + size.x + (pixels_per_byte - 1)) / pixels_per_byte); + nfmemcpy(line, prev, cursor_bytes_per_line); } - - break; } data.cursor_visible = false; } +/** Renders the graphical cursor. + * It will also backup whatever pixels are below + * the cursor area to cursor_prev_graphic. */ static void show_graphic_cursor(void) { - static const uint16_t msb_mask = 0x8000; - uint16_t cursor_and_mask, cursor_xor_mask; - uint8_t __far *pixel, pixel_mask; - uint8_t *cursor_prev; + const struct modeinfo *info = &data.video_mode; struct point start, size, offset; - unsigned pixels_per_byte, x, y; + unsigned cursor_bytes_per_line; + const uint8_t msb_pixel_mask = build_pixel_mask(info->bits_per_pixel); + unsigned plane, y; // Compute the area where the cursor is supposed to be drawn if (!get_graphic_cursor_area(&data.pos, &start, &size, &offset)) { return; } - switch (data.screen_mode) { - case 4: - case 5: - case 6: // CGA modes - pixels_per_byte = data.screen_mode == 0x6 ? 8 : 4; + // For each scanline, we will copy this amount of bytes + cursor_bytes_per_line = get_scanline_segment_bytes(info->bits_per_pixel, + start.x, size.x); + + for (plane = 0; plane < info->num_planes; plane++) { + if (info->num_planes) vga_select_plane(plane); for (y = 0; y < size.y; y++) { - cursor_and_mask = get_graphic_cursor_and_mask(offset.y + y) << offset.x; - cursor_xor_mask = get_graphic_cursor_xor_mask(offset.y + y) << offset.y; - cursor_prev = get_prev_graphic_cursor_scanline(offset.y + y); - pixel = get_video_scanline(data.screen_mode, data.screen_page, start.y + y) - + start.x / pixels_per_byte; - - // First copy this scanline to cursor_prev before any changes - fnmemcpy(cursor_prev, pixel, - ((start.x % pixels_per_byte) + size.x + (pixels_per_byte - 1)) / pixels_per_byte); - - // pixel points the previous multiple of pixels_per_byte; - // now advance to the start of cursor while updating the pixel_mask - pixel_mask = pixels_per_byte == 8 ? 0x80 : 0xC0; - for (x = 0; x < (start.x % pixels_per_byte); x++) { - pixel_mask >>= 8 / pixels_per_byte; + uint8_t __far *line = get_video_scanline(info, start.y + y) + + (start.x * info->bits_per_pixel) / 8; + uint8_t *prev = get_prev_graphic_cursor_scanline(cursor_bytes_per_line, size.y, + plane, y); + uint16_t cursor_and_mask = get_graphic_cursor_and_mask_line(offset.y + y) + << offset.x; + uint16_t cursor_xor_mask = get_graphic_cursor_xor_mask_line(offset.y + y) + << offset.x; + uint8_t pixel_mask = msb_pixel_mask; + unsigned x; + + // First, backup this scanline to prev before any changes + fnmemcpy(prev, line, cursor_bytes_per_line); + + // when start.x is not pixel aligned, + // scaline points the previous multiple of pixels_per_byte; + // and the initial pixel will not be at the MSB of it. + // advance the pixel_mask accordingly + if (info->bits_per_pixel < 8) { + pixel_mask >>= (start.x * info->bits_per_pixel) % 8; } - // Now pixel points to the start of cursor - for (; x < (start.x % pixels_per_byte) + size.x; x++) { - uint8_t rest = *pixel & ~pixel_mask; + for (x = 0; x < size.x; x++) { + uint8_t pixel = *line; - if (!(cursor_and_mask & msb_mask)) { - *pixel = rest; + if (!(cursor_and_mask & MSB_MASK)) { + pixel &= ~pixel_mask; } - if (cursor_xor_mask & msb_mask) { - *pixel = rest | (*pixel ^ 0xFF) & pixel_mask; + if (cursor_xor_mask & MSB_MASK) { + pixel ^= pixel_mask; + } + if (!(cursor_and_mask & MSB_MASK) || (cursor_xor_mask & MSB_MASK)) { + *line = pixel; } // Advance to the next pixel - if (x % pixels_per_byte == pixels_per_byte - 1) { - // Next iteration starts new byte, reload pixel_mask - pixel++; - pixel_mask = pixels_per_byte == 8 ? 0x80 : 0xC0; - } else { - pixel_mask >>= 8 / pixels_per_byte; + pixel_mask >>= info->bits_per_pixel; + if (!pixel_mask) { + // Time to advance to the next byte + line++; + pixel_mask = msb_pixel_mask; } // Advance to the next bit in the cursor mask @@ -231,12 +265,6 @@ static void show_graphic_cursor(void) cursor_xor_mask <<= 1; } } - break; - default: - dlog_print("Graphic mode 0x"); - dlog_printx(data.screen_mode); - dlog_puts(" not supported"); - return; } data.cursor_pos = data.pos; @@ -247,6 +275,7 @@ static void show_graphic_cursor(void) static void refresh_cursor(void) { bool should_show = data.visible_count >= 0; + bool pos_changed, needs_refresh; #if USE_INT2F // Windows 386 is already rendering the cursor for us. @@ -256,21 +285,32 @@ static void refresh_cursor(void) #if USE_VIRTUALBOX if (data.vbwantcursor) { + // We want to use the VirtualBox host cursor. + // See if we have to update its visibility. int err = 0; if (should_show != data.cursor_visible) { - int err = vbox_set_pointer_visible(&data.vb, should_show); + err = vbox_set_pointer_visible(&data.vb, should_show); if (err == 0 && data.vbhaveabs) { data.cursor_visible = should_show; } } if (err == 0 & data.vbhaveabs) { - // No need to show the cursor; VirtualBox is already showing it for us. + // No need to refresh the cursor; VirtualBox is already showing it for us. return; } } #endif - if (is_text_mode(data.screen_mode)) { + pos_changed = data.cursor_pos.x != data.pos.x || data.cursor_pos.y != data.pos.y; + needs_refresh = should_show && pos_changed || should_show != data.cursor_visible; + + if (!needs_refresh) { + // Nothing to do + return; + } + + if (data.video_mode.type == VIDEO_TEXT) { + // Text video mode if (data.cursor_visible) { // Hide the cursor at the old position if any hide_text_cursor(); @@ -279,13 +319,30 @@ static void refresh_cursor(void) // Show the cursor at the new position show_text_cursor(); } - } else { + } else if (data.video_mode.type != VIDEO_UNKNOWN) { + // Graphic video modes + + bool video_planar = data.video_mode.num_planes > 1; + struct videoregs regs; + // If current video mode is planar, + // we will have to play with the VGA registers + // so let's save and restore them. + if (video_planar) { + save_video_registers(®s); + } + if (data.cursor_visible) { hide_graphic_cursor(); } if (should_show) { show_graphic_cursor(); } + + if (video_planar) { + restore_video_registers(®s); + } + } else { + // Unknown video mode, don't render cursor. } } @@ -301,12 +358,10 @@ static void hide_cursor(void) } #endif - if (is_text_mode(data.screen_mode)) { - if (data.cursor_visible) { + if (data.cursor_visible) { + if (data.video_mode.type == VIDEO_TEXT) { hide_text_cursor(); - } - } else { - if (data.cursor_visible) { + } else if (data.video_mode.type != VIDEO_UNKNOWN) { hide_graphic_cursor(); } } @@ -320,15 +375,12 @@ static void load_cursor(void) if (data.vbwantcursor) { VMMDevReqMousePointer *req = (VMMDevReqMousePointer *) data.vb.buf; const unsigned width = GRAPHIC_CURSOR_WIDTH, height = GRAPHIC_CURSOR_HEIGHT; - const unsigned and_mask_size = (width + 7) / 8 * height; - const unsigned xor_mask_size = width * height * 4; - const unsigned data_size = and_mask_size + xor_mask_size; - const unsigned full_size = MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size); - unsigned int offset = 0, y, x; + uint8_t *output = req->pointerData; + unsigned int y, x; - bzero(req, full_size); + bzero(req, sizeof(VMMDevReqMousePointer)); - req->header.size = full_size; + req->header.size = vbox_req_mouse_pointer_size(width, height); req->header.version = VMMDEV_REQUEST_HEADER_VERSION; req->header.requestType = VMMDevReq_SetPointerShape; req->header.rc = -1; @@ -339,28 +391,34 @@ static void load_cursor(void) req->width = width; req->height = height; - // Just byteswap the AND mask + // AND mask + // int33 format is 1-bit per pixel packed into 16-bit LE values, + // while VirtualBox wants 1-bit per pixel packed into 8-bit. + // All we have to do is byteswap 16-bit values. for (y = 0; y < height; ++y) { - uint16_t line = get_graphic_cursor_and_mask(y); - req->pointerData[(y*2)] = (line >> 8) & 0xFF; - req->pointerData[(y*2)+1] = line & 0xFF; + uint16_t cursor_line = get_graphic_cursor_and_mask_line(y); + output[0] = (cursor_line >> 8) & 0xFF; + output[1] = cursor_line & 0xFF; + output += GRAPHIC_CURSOR_SCANLINE_LEN; } - offset += and_mask_size; - // But the XOR mask needs to be converted to huge 4-byte "RGBA" format. + // XOR mask + // int33 format is again 1-bit per pixel packed into 16-bit LE values, + // however VirtualBox wants 4-byte per pixel packed "RGBA". for (y = 0; y < height; ++y) { - uint16_t line = get_graphic_cursor_xor_mask(y); + uint16_t cursor_line = get_graphic_cursor_xor_mask_line(y); for (x = 0; x < width; ++x) { - unsigned int pos = offset + (y * width * 4) + (x*4) + 0; - uint8_t val = (line & 0x8000) ? 0xFF : 0; + // MSB of line is current mask bit, we shift it on each iteration + uint8_t val = (cursor_line & MSB_MASK) ? 0xFF : 0; - req->pointerData[pos + 0] = val; - req->pointerData[pos + 1] = val; - req->pointerData[pos + 2] = val; - req->pointerData[pos + 3] = 0; + output[0] = val; + output[1] = val; + output[2] = val; + output[3] = 0; - line <<= 1; + cursor_line <<= 1; + output += 4; } } @@ -384,8 +442,8 @@ static void load_cursor(void) /** Refreshes the information about the current video mode. */ static void refresh_video_info(void) { - uint8_t mode = bda_get_video_mode() & ~0x80; - bool mode_change = mode != data.screen_mode; + uint8_t cur_mode = bda_get_video_mode() & ~0x80; + bool mode_change = cur_mode != data.video_mode.mode; if (mode_change && data.cursor_visible) { // Assume cursor is lost @@ -393,63 +451,22 @@ static void refresh_video_info(void) } dlog_print("Current video mode="); - dlog_printx(mode); - dlog_print(" with cols="); - dlog_printd(bda_get_num_columns()); - dlog_print(" lastrow="); - dlog_printd(bda_get_last_row()); + dlog_printx(cur_mode); dlog_endline(); - data.screen_mode = mode; - data.screen_page = bda_get_cur_video_page(); + get_current_video_mode_info(&data.video_mode); + + data.screen_max.x = data.video_mode.pixels_width - 1; + data.screen_max.y = data.video_mode.pixels_height - 1; data.screen_scale.x = 1; data.screen_scale.y = 1; - // Compute screen coordinates which are used to events from - // absolute mouse coordinates. - switch (mode) { - case 0: - case 1: - case 2: - case 3: // CGA text modes with 25 rows and variable columns - case 7: // MDA Mono text mode - data.screen_max.x = (bda_get_num_columns() * 8) - 1; - data.screen_max.y = ( 25 * 8) - 1; - break; - - case 4: - case 5: // CGA low-res modes - case 0xd: // EGA low-res mode + // The actual range of coordinates expected by int33 clients + // is, for some reason, different than real resolution in some modes. + // For example, 320x... modes are mapped to 640x... pixels. + if (data.video_mode.pixels_width == 320) { data.screen_max.x = 640 - 1; - data.screen_max.y = 200 - 1; - data.screen_scale.x = 2; // Really 320x200 - break; - - case 6: // CGA hi-res mode - case 0xe: // EGA modes - case 0x13: // VGA 256color mode - data.screen_max.x = 640 - 1; - data.screen_max.y = 200 - 1; - break; - - case 0xf: - case 0x10: // EGA 640x350 modes - data.screen_max.x = 640 - 1; - data.screen_max.y = 350 - 1; - break; - - case 0x11: - case 0x12: // VGA 640x480 modes - data.screen_max.x = 640 - 1; - data.screen_max.y = 480 - 1; - break; - - default: - // Unknown mode; assume default coordinates - // Note that if program sets up larger window coordinates, we'll use that instead. - data.screen_max.x = 640 - 1; - data.screen_max.y = 200 - 1; - break; + data.screen_scale.x = 640 / 320; } } @@ -602,6 +619,7 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in } } +/** PS/2 BIOS calls this routine to notify mouse events. */ static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z) { #pragma aux (PS2_CB) ps2_mouse_callback @@ -694,6 +712,7 @@ static void reset_mouse_hardware() ps2m_enable(true); } +/** Reset "software" mouse settings, i.e. those configurable by the client program. */ static void reset_mouse_settings() { data.event_mask = 0; @@ -717,6 +736,7 @@ static void reset_mouse_settings() refresh_cursor(); // This will hide the cursor and update data.cursor_visible } +/** Reset the current mouse state and throw away past events. */ static void reset_mouse_state() { int i; @@ -765,6 +785,7 @@ static void return_clear_button_counter(union INTPACK __far *r, struct buttoncou c->count = 0; } +/** Entry point for our int33 API. */ static void int33_handler(union INTPACK r) #pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx es] { @@ -861,7 +882,7 @@ static void int33_handler(union INTPACK r) hide_cursor(); data.cursor_hotspot.x = r.x.bx; data.cursor_hotspot.y = r.x.cx; - fnmemcpy(data.cursor_graphic, MK_FP(r.x.es, r.x.dx), 64); + fnmemcpy(data.cursor_graphic, MK_FP(r.x.es, r.x.dx), sizeof(data.cursor_graphic)); load_cursor(); refresh_cursor(); break; @@ -1016,6 +1037,7 @@ void __declspec(naked) __far int33_isr(void) } #if USE_INT2F +/** Windows will call this function to notify events when we are inside a DOS box. */ static void windows_mouse_handler(int action, int x, int y, int buttons, int events) #pragma aux windows_mouse_handler "*" parm [ax] [bx] [cx] [dx] [si] modify [ax bx cx dx es] { @@ -25,6 +25,7 @@ #include "vbox.h" #include "int2fwin.h" +#include "int10vga.h" #define USE_VIRTUALBOX 1 #define USE_INT2F 1 @@ -34,6 +35,9 @@ #define GRAPHIC_CURSOR_WIDTH 16 #define GRAPHIC_CURSOR_HEIGHT 16 +#define GRAPHIC_CURSOR_SCANLINE_LEN 2 +#define GRAPHIC_CURSOR_MASK_LEN (GRAPHIC_CURSOR_HEIGHT * GRAPHIC_CURSOR_SCANLINE_LEN) +#define GRAPHIC_CURSOR_DATA_LEN (2 * GRAPHIC_CURSOR_MASK_LEN) #define VERSION_MAJOR 0 #define VERSION_MINOR 3 @@ -55,16 +59,15 @@ typedef struct tsrdata { bool usewheel; // Video settings - /** Current video mode. */ - uint8_t screen_mode; - /** Active video page. */ - uint8_t screen_page; + /** Information of the current video mode. */ + struct modeinfo video_mode; /** Max (virtual) coordinates of full screen in the current mode. * Used for rendering graphic cursor, mapping absolute coordinates, * and initializing the default min/max window. */ struct point screen_max; - /** Some graphic modes have pixel doubling, so the virtual coordinates - * are double vs real framebuffer coordinates. */ + /** In some modes, the virtual coordinates are larger than the + * physical screen coordinates. + * real coordinates = virtual coordinates * screen_scale. */ struct point screen_scale; // Detected mouse hardware @@ -89,7 +92,7 @@ typedef struct tsrdata { /** Hotspot for the graphic cursor. */ struct point cursor_hotspot; /** Masks for the graphic cursor. */ - uint16_t cursor_graphic[GRAPHIC_CURSOR_HEIGHT*2]; + uint16_t cursor_graphic[GRAPHIC_CURSOR_DATA_LEN/sizeof(uint16_t)]; // Current mouse status /** Current cursor position (in pixels). */ @@ -5,18 +5,6 @@ #define BIOS_DATA_AREA_SEGMENT 0x40 -// TODO Assuming screenwidth, videopage on ss rather than using far -static uint8_t int10_get_video_mode(uint8_t *screenwidth, uint8_t *videopage); -#pragma aux int10_get_video_mode = \ - "mov al, 0" \ - "mov ah, 0x0F" \ - "int 0x10" \ - "mov ss:[si], ah" \ - "mov ss:[di], bh" \ - __parm [si] [di] \ - __value [al] \ - __modify [ax bh] - static inline uint32_t bda_get_dword(unsigned int offset) { uint32_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset); return *p; @@ -43,29 +31,183 @@ static inline uint8_t bda_get_byte(unsigned int offset) { #define bda_get_tick_count() bda_get_dword(0x6c) #define bda_get_tick_count_lo() bda_get_word(0x6c) -static inline uint16_t __far * get_video_char(uint8_t page, unsigned int x, unsigned int y) -{ - return MK_FP(0xB800, (page * bda_get_video_page_size()) - + ((y * bda_get_num_columns()) + x) * 2); -} +enum videotype { + VIDEO_UNKNOWN, + VIDEO_TEXT, + VIDEO_CGA, + VIDEO_EGA, + VIDEO_VGA +}; + +struct modeinfo { + uint8_t mode; + uint8_t page; + enum videotype type; + + uint16_t pixels_width, pixels_height; + uint16_t bytes_per_line; + uint16_t odd_scanline_offset; + uint8_t bits_per_pixel; + uint8_t num_planes; -static inline uint8_t __far * get_video_scanline(uint8_t mode, uint8_t page, unsigned int y) + /** Pointer to video memory. */ + uint8_t __far * begin; +}; + +static void get_current_video_mode_info(struct modeinfo *info) { - switch (mode) { + uint16_t segment; + + info->mode = bda_get_video_mode() & ~0x80; + info->page = bda_get_cur_video_page(); + + info->odd_scanline_offset = 0; + + switch (info->mode) { + case 0: + case 1: + case 2: + case 3: // CGA text modes with 25 rows and variable columns + case 7: // MDA Mono text mode + info->type = VIDEO_TEXT; + info->pixels_width = bda_get_num_columns() * 8; + info->pixels_height = (bda_get_last_row()+1) * 8; + info->bytes_per_line = bda_get_num_columns() * 2; + info->bits_per_pixel = 2 * 8; + info->num_planes = 1; + segment = 0xB800; + break; + case 4: - case 5: - case 6: // CGA modes - // Scalines are 80 bytes long, however they are interleaved. - // Even numbered scanlines begin at 0, odd lines begin at 0x2000. - // So offset by y%2 * 0x2000. - return MK_FP(0xB800, (page * bda_get_video_page_size()) - + ((y/2) * 80) + (y%2) * 0x2000); + case 5: // CGA 320x200 4-color + info->type = VIDEO_CGA; + info->pixels_width = 320; + info->pixels_height = 200; + info->bytes_per_line = 80; + info->odd_scanline_offset = 0x2000; + info->bits_per_pixel = 2; + info->num_planes = 1; + segment = 0xB800; + break; + + case 6: // CGA 640x200 2-color + info->type = VIDEO_CGA; + info->pixels_width = 640; + info->pixels_height = 200; + info->bytes_per_line = 80; + info->odd_scanline_offset = 0x2000; + info->bits_per_pixel = 1; + info->num_planes = 1; + segment = 0xB800; + break; + + case 0xd: // EGA 320x200 16-color + info->type = VIDEO_EGA; + info->pixels_width = 320; + info->pixels_height = 200; + info->bytes_per_line = 40; + info->bits_per_pixel = 1; + info->num_planes = 4; + segment = 0xA000; + break; + + case 0xe: // EGA 640x200 16-color + info->type = VIDEO_EGA; + info->pixels_width = 640; + info->pixels_height = 200; + info->bytes_per_line = 80; + info->bits_per_pixel = 1; + info->num_planes = 4; + segment = 0xA000; + break; + + case 0xf: // EGA 640x350 4-color + info->type = VIDEO_EGA; + info->pixels_width = 640; + info->pixels_height = 350; + info->bytes_per_line = 80; + info->bits_per_pixel = 1; + info->num_planes = 2; + segment = 0xA000; + break; + + case 0x10: // EGA 640x350 16-color + info->type = VIDEO_EGA; + info->pixels_width = 640; + info->pixels_height = 350; + info->bytes_per_line = 80; + info->bits_per_pixel = 1; + info->num_planes = 4; + segment = 0xA000; + break; + + case 0x11: // VGA 640x480 2-color + case 0x12: // VGA 640x480 16-color + info->type = VIDEO_VGA; + info->pixels_width = 640; + info->pixels_height = 480; + info->bytes_per_line = 80; + info->bits_per_pixel = 1; + info->num_planes = 4; + segment = 0xA000; + break; + + case 0x13: // VGA 320x200 256-color + info->type = VIDEO_VGA; + info->pixels_width = 320; + info->pixels_height = 200; + info->bytes_per_line = 320; + info->bits_per_pixel = 8; + info->num_planes = 1; + segment = 0xA000; + break; + default: - return 0; + info->type = VIDEO_UNKNOWN; + // Let's put in some default coordinates at leas + info->pixels_width = 640; + info->pixels_height = 200; + segment = 0; + } + + info->begin = MK_FP(segment, info->page * bda_get_video_page_size()); +} + +static inline uint16_t __far * get_video_char(const struct modeinfo *info, unsigned int x, unsigned int y) +{ + return (uint16_t __far *) (info->begin + (y * info->bytes_per_line) + (x * 2)); +} + +static inline uint8_t __far * get_video_scanline(const struct modeinfo *info, unsigned int y) +{ + if (info->odd_scanline_offset) { + return info->begin + + (y%2 * info->odd_scanline_offset) + + (y/2 * info->bytes_per_line); + } else { + return info->begin + + (y * info->bytes_per_line); } } +struct videoregs { + uint16_t dummy; +}; +static void save_video_registers(struct videoregs *regs) +{ + // TODO +} + +static void restore_video_registers(struct videoregs *regs) +{ + // TODO +} + +static inline void vga_select_plane(unsigned plane) +{ + // TODO +} #endif @@ -16,9 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #ifndef INT33_H #define INT33_H +#include <stdint.h> +#include <stdbool.h> + enum INT33_API { /** Reinitializes mouse hardware and resets mouse to default driver values. * On return, ax = 0xFFFF, bx = number of buttons. */ @@ -185,7 +189,8 @@ static uint16_t int33_reset(void); #pragma aux int33_reset = \ "mov ax, 0x0" \ "int 0x33" \ - __value [ax] + __value [ax] \ + __modify [ax bx] static void int33_set_horizontal_window(int16_t min, int16_t max); #pragma aux int33_set_horizontal_window = \ @@ -221,4 +226,4 @@ static uint16_t int33_get_driver_version(void); __value [bx] \ __modify [ax bx cx dx] -#endif +#endif /* INT33_H */ @@ -145,4 +145,13 @@ static int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible) return req->header.rc; } +/** Computes size of a VMMDevReqMousePointer message. */ +static inline unsigned vbox_req_mouse_pointer_size(unsigned width, unsigned height) +{ + const unsigned and_mask_size = (width + 7) / 8 * height; + const unsigned xor_mask_size = width * height * 4; + const unsigned data_size = and_mask_size + xor_mask_size; + return MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size); +} + #endif |