[SeaBIOS] [PATCH 2/2] vgabios: implement read char in graphics mode

Kevin O'Connor kevin at koconnor.net
Fri Jan 2 18:43:07 CET 2015


On Thu, Jan 01, 2015 at 09:27:47PM +0100, Paolo Bonzini wrote:
> GWBasic relies on this, so implement it to enable some serious retrocomputing.
> There is no better way to do it than trying to match all characters one by one
> against the current font.
> 
> This makes it possible to actually do something in SCREEN 1 and SCREEN 2
> (without it, you can use graphics in the programs but not in direct mode).
> 
> I couldn't find documentation for what to return as the attribute, but
> experimenting with DOSBox suggests 0 (and GWBasic accepts it).

Your patch works fine and matches what ATI and Intel seem to do.  But,
I couldn't help tinkering with it.  Are you okay with the patch below?

-Kevin


commit a5d54337852f26abeed1c2baa517d1870a641174
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Fri Jan 2 12:32:25 2015 -0500

    vgabios: implement read char in graphics mode
    
    GWBasic relies on this, so implement it to enable some serious retrocomputing.
    There is no better way to do it than trying to match all characters one by one
    against the current font.
    
    This makes it possible to actually do something in SCREEN 1 and SCREEN 2
    (without it, you can use graphics in the programs but not in direct mode).
    
    I couldn't find documentation for what to return as the attribute, but
    experimenting with DOSBox suggests 0 (and GWBasic accepts it).
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
    Signed-off-by: Kevin O'Connor <kevin at koconnor.net>

diff --git a/src/string.c b/src/string.c
index 8556fe9..2e4e437 100644
--- a/src/string.c
+++ b/src/string.c
@@ -42,6 +42,19 @@ strlen(const char *s)
     return p-s;
 }
 
+int
+memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n)
+{
+    while (n--) {
+        int d = GET_FARVAR(s1seg, *(u8*)s1) - GET_FARVAR(s2seg, *(u8*)s2);
+        if (d)
+            return d < 0 ? -1 : 1;
+        s1++;
+        s2++;
+    }
+    return 0;
+}
+
 // Compare two areas of memory.
 int
 memcmp(const void *s1, const void *s2, size_t n)
diff --git a/src/string.h b/src/string.h
index ca751a4..a557d6a 100644
--- a/src/string.h
+++ b/src/string.h
@@ -8,6 +8,7 @@
 u8 checksum_far(u16 buf_seg, void *buf_far, u32 len);
 u8 checksum(void *buf, u32 len);
 size_t strlen(const char *s);
+int memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n);
 int memcmp(const void *s1, const void *s2, size_t n);
 int strcmp(const char *s1, const char *s2);
 inline void memset_far(u16 d_seg, void *d_far, u8 c, size_t len);
diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c
index 59ddc56..4bbbc16 100644
--- a/vgasrc/vgafb.c
+++ b/vgasrc/vgafb.c
@@ -454,6 +454,43 @@ gfx_write_char(struct vgamode_s *vmode_g
     }
 }
 
+// Read a character from the screen in graphics mode.
+static struct carattr
+gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
+{
+    u8 lines[16];
+    int cheight = GET_BDA(char_height);
+    if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
+        goto fail;
+
+    // Read cell from screen
+    struct gfx_op op;
+    init_gfx_op(&op, vmode_g);
+    op.op = GO_READ8;
+    op.x = cp.x * 8;
+    op.y = cp.y * cheight;
+    u8 i, j;
+    for (i=0; i<cheight; i++, op.y++) {
+        u8 line = 0;
+        handle_gfx_op(&op);
+        for (j=0; j<8; j++)
+            if (op.pixels[j])
+                line |= 0x80 >> j;
+        lines[i] = line;
+    }
+
+    // Determine font
+    u16 car;
+    for (car=0; car<256; car++) {
+        struct segoff_s font = get_font_data(car);
+        if (memcmp_far(GET_SEG(SS), lines
+                       , font.seg, (void*)(font.offset+0), cheight) == 0)
+            return (struct carattr){car, 0, 0};
+    }
+fail:
+    return (struct carattr){0, 0, 0};
+}
+
 // Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell
 void
 gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp)
@@ -607,23 +644,15 @@ vgafb_read_char(struct cursorpos cp)
 {
     struct vgamode_s *vmode_g = get_current_mode();
     if (!vmode_g)
-        goto fail;
+        return (struct carattr){0, 0, 0};
     vgafb_set_swcursor(0);
 
-    if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
-        // FIXME gfx mode
-        dprintf(1, "Read char in graphics mode\n");
-        goto fail;
-    }
+    if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
+        return gfx_read_char(vmode_g, cp);
 
     u16 *dest_far = text_address(cp);
     u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
-    struct carattr ca = {v, v>>8, 0};
-    return ca;
-
-fail: ;
-    struct carattr ca2 = {0, 0, 0};
-    return ca2;
+    return (struct carattr){v, v>>8, 0};
 }
 
 // Draw/undraw a cursor on the screen



More information about the SeaBIOS mailing list