aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-04-03 21:23:10 +0200
committerJavier <dev.git@javispedro.com>2022-04-03 21:23:10 +0200
commita5abb118e0e61587adfa7b4e4cc72948311381d3 (patch)
treeb226705401bd0b4c78399c621cebc1fbab6003b9
parent7d93442564b57c2d292df7f823c2115d3e0b8c12 (diff)
downloadvbados-a5abb118e0e61587adfa7b4e4cc72948311381d3.tar.gz
vbados-a5abb118e0e61587adfa7b4e4cc72948311381d3.zip
try to handle graphical cursor rendering in a more generic way
-rw-r--r--dosmain.c19
-rw-r--r--dostsr.c372
-rw-r--r--dostsr.h17
-rw-r--r--int10vga.h196
-rw-r--r--int33.h9
-rw-r--r--vbox.h9
6 files changed, 398 insertions, 224 deletions
diff --git a/dosmain.c b/dosmain.c
index c35975e..7196efa 100644
--- a/dosmain.c
+++ b/dosmain.c
@@ -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)
diff --git a/dostsr.c b/dostsr.c
index 7d881e2..0d24248 100644
--- a/dostsr.c
+++ b/dostsr.c
@@ -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(&regs);
+ }
+
if (data.cursor_visible) {
hide_graphic_cursor();
}
if (should_show) {
show_graphic_cursor();
}
+
+ if (video_planar) {
+ restore_video_registers(&regs);
+ }
+ } 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]
{
diff --git a/dostsr.h b/dostsr.h
index e64c93e..7f1200b 100644
--- a/dostsr.h
+++ b/dostsr.h
@@ -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). */
diff --git a/int10vga.h b/int10vga.h
index a2a20a0..29c56a3 100644
--- a/int10vga.h
+++ b/int10vga.h
@@ -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
diff --git a/int33.h b/int33.h
index 44f61f9..13d5eea 100644
--- a/int33.h
+++ b/int33.h
@@ -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 */
diff --git a/vbox.h b/vbox.h
index 69721aa..a4ad727 100644
--- a/vbox.h
+++ b/vbox.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