What's a better small project for the holiday break, than fixing emulation of games and programs from your youth (saying "a few decades ago" makes me feel old)? At least it is easy to explain a suspicious wife why it is not work!
Paolo
Paolo Bonzini (2): vgabios: fix graphics operation with Bochs VGA in non-DISPI modes vgabios: implement read char in graphics mode
vgasrc/bochsvga.c | 27 +++++++++++++++++++-------- vgasrc/vgafb.c | 41 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 11 deletions(-)
For legacy VGA modes that do not set the VBE_DISPI_ENABLED bit, bochsvga_get_linelength returns 0. Thus all characters are squashed into the first scanline. Fix this by falling back to stdvga for the legacy modes.
Signed-off-by: Paolo Bonzini pbonzini@redhat.com --- vgasrc/bochsvga.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/vgasrc/bochsvga.c b/vgasrc/bochsvga.c index 5313f54..ee6f437 100644 --- a/vgasrc/bochsvga.c +++ b/vgasrc/bochsvga.c @@ -151,10 +151,21 @@ static inline void dispi_write(u16 reg, u16 val) outw(val, VBE_DISPI_IOPORT_DATA); }
+static u8 +bochsvga_dispi_enabled(void) +{ + if (!GET_GLOBAL(dispi_found)) + return 0; + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + if (!(en & VBE_DISPI_ENABLED)) + return 0; + return 1; +} + int bochsvga_get_window(struct vgamode_s *vmode_g, int window) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_window(vmode_g, window); if (window != 0) return -1; @@ -164,7 +175,7 @@ bochsvga_get_window(struct vgamode_s *vmode_g, int window) int bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_set_window(vmode_g, window, val); if (window != 0) return -1; @@ -177,7 +188,7 @@ bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val) int bochsvga_get_linelength(struct vgamode_s *vmode_g) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_linelength(vmode_g); return dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * vga_bpp(vmode_g) / 8; } @@ -186,7 +197,7 @@ int bochsvga_set_linelength(struct vgamode_s *vmode_g, int val) { stdvga_set_linelength(vmode_g, val); - if (GET_GLOBAL(dispi_found)) { + if (bochsvga_dispi_enabled()) { int pixels = (val * 8) / vga_bpp(vmode_g); dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, pixels); } @@ -196,7 +207,7 @@ bochsvga_set_linelength(struct vgamode_s *vmode_g, int val) int bochsvga_get_displaystart(struct vgamode_s *vmode_g) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_displaystart(vmode_g); int bpp = vga_bpp(vmode_g); int linelength = dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * bpp / 8; @@ -209,7 +220,7 @@ int bochsvga_set_displaystart(struct vgamode_s *vmode_g, int val) { stdvga_set_displaystart(vmode_g, val); - if (GET_GLOBAL(dispi_found)) { + if (bochsvga_dispi_enabled()) { int bpp = vga_bpp(vmode_g); int linelength = dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * bpp / 8; if (!linelength) @@ -223,7 +234,7 @@ bochsvga_set_displaystart(struct vgamode_s *vmode_g, int val) int bochsvga_get_dacformat(struct vgamode_s *vmode_g) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_dacformat(vmode_g); u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); return (en & VBE_DISPI_8BIT_DAC) ? 8 : 6; @@ -232,7 +243,7 @@ bochsvga_get_dacformat(struct vgamode_s *vmode_g) int bochsvga_set_dacformat(struct vgamode_s *vmode_g, int val) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_set_dacformat(vmode_g, val); u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); if (val == 6)
On Thu, Jan 01, 2015 at 09:27:46PM +0100, Paolo Bonzini wrote:
For legacy VGA modes that do not set the VBE_DISPI_ENABLED bit, bochsvga_get_linelength returns 0. Thus all characters are squashed into the first scanline. Fix this by falling back to stdvga for the legacy modes.
[...]
+static u8 +bochsvga_dispi_enabled(void) +{
- if (!GET_GLOBAL(dispi_found))
return 0;
- u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE);
- if (!(en & VBE_DISPI_ENABLED))
return 0;
- return 1;
+}
Thanks. Can the existing is_bochsvga_mode() function be used instead?
-Kevin
On 02/01/2015 04:10, Kevin O'Connor wrote:
On Thu, Jan 01, 2015 at 09:27:46PM +0100, Paolo Bonzini wrote:
For legacy VGA modes that do not set the VBE_DISPI_ENABLED bit, bochsvga_get_linelength returns 0. Thus all characters are squashed into the first scanline. Fix this by falling back to stdvga for the legacy modes.
[...]
+static u8 +bochsvga_dispi_enabled(void) +{
- if (!GET_GLOBAL(dispi_found))
return 0;
- u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE);
- if (!(en & VBE_DISPI_ENABLED))
return 0;
- return 1;
+}
Thanks. Can the existing is_bochsvga_mode() function be used instead?
Yes, but this matches what bochsvga_save/restore_state are already doing.
Paolo
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@redhat.com --- vgasrc/vgafb.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-)
diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index bb27660..90d7373 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -400,6 +400,41 @@ get_font_data(u8 c) return font; }
+// Read a character to the screen in graphics mode. +static int +gfx_read_char(struct vgamode_s *vmode_g + , struct cursorpos cp) +{ + if (cp.x >= GET_BDA(video_cols)) + return 0; + + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.op = GO_READ8; + op.x = cp.x * 8; + op.y = -1; + int cheight = GET_BDA(char_height); + u16 basey = cp.y * cheight; + u16 car; + u8 i, j; + for (car = 0; car < 256; car++) { + struct segoff_s font = get_font_data(car); + for (i = 0; i < cheight; i++) { + if (op.y != basey + i) { + op.y = basey + i; + handle_gfx_op(&op); + } + u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i)); + for (j = 0; j < 8; j++, fontline <<= 1) + if (!!op.pixels[j] != !!(fontline & 0x80)) + goto trynext; + } + return car; +trynext:; + } + return 0; +} + // Write a character to the screen in graphics mode. static void gfx_write_char(struct vgamode_s *vmode_g @@ -561,9 +596,9 @@ vgafb_read_char(struct cursorpos cp) goto fail;
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { - // FIXME gfx mode - dprintf(1, "Read char in graphics mode\n"); - goto fail; + u8 v = gfx_read_char(vmode_g, cp); + struct carattr ca = {v, 0, 0}; + return ca; }
u16 *dest_far = text_address(cp);
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.
Heh.
[...]
I couldn't find documentation for what to return as the attribute, but experimenting with DOSBox suggests 0 (and GWBasic accepts it).
Just for reference, for the "cbvga" text mode emulation, I grab the background attribute by assuming the lower right pixel of the cell is the background, and then assume the foreground attribute is the background attribute xor'd with 0x7 (see gfx_write_char()). I'm not sure if that hack applies here though. :-)
One thing I'm curious about is what should happen when the cell contains a space character (all pixels off) - your patch returns 0 right now, where I'd think a space (32) would be more probable. Maybe callers already know how to handle this though. I might run a quick test under coreboot with a proprietary vgabios to see what it does. :-)
[...]
+// Read a character to the screen in graphics mode. +static int +gfx_read_char(struct vgamode_s *vmode_g
, struct cursorpos cp)
+{
- if (cp.x >= GET_BDA(video_cols))
return 0;
- struct gfx_op op;
- init_gfx_op(&op, vmode_g);
- op.op = GO_READ8;
- op.x = cp.x * 8;
- op.y = -1;
- int cheight = GET_BDA(char_height);
- u16 basey = cp.y * cheight;
- u16 car;
- u8 i, j;
- for (car = 0; car < 256; car++) {
struct segoff_s font = get_font_data(car);
for (i = 0; i < cheight; i++) {
if (op.y != basey + i) {
op.y = basey + i;
handle_gfx_op(&op);
}
u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
for (j = 0; j < 8; j++, fontline <<= 1)
if (!!op.pixels[j] != !!(fontline & 0x80))
goto trynext;
}
return car;
+trynext:;
- }
- return 0;
+}
// Write a character to the screen in graphics mode. static void gfx_write_char(struct vgamode_s *vmode_g @@ -561,9 +596,9 @@ vgafb_read_char(struct cursorpos cp) goto fail;
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
// FIXME gfx mode
dprintf(1, "Read char in graphics mode\n");
goto fail;
u8 v = gfx_read_char(vmode_g, cp);
struct carattr ca = {v, 0, 0};
return ca;
Any reason not to just "return gfx_read_char(vmode_g, cp);" here and have gfx_read_char() return a struct carattr?
Patch looks good to me - thanks. -Kevin
On 02/01/2015 04:40, Kevin O'Connor wrote:
I couldn't find documentation for what to return as the attribute, but experimenting with DOSBox suggests 0 (and GWBasic accepts it).
Just for reference, for the "cbvga" text mode emulation, I grab the background attribute by assuming the lower right pixel of the cell is the background, and then assume the foreground attribute is the background attribute xor'd with 0x7 (see gfx_write_char()). I'm not sure if that hack applies here though. :-)
No, it doesn't. http://www.ctyme.com/intr/rb-0098.htm says AH is only defined in text mode, so it's probably safer to return zero.
Actually, it even says "only characters drawn with white foreground pixels are matched by the pattern-comparison routine". I can tweak the patch to only match color (1 << vga_bpp) - 1, or to add a comment that we extend the behavior.
Any reason not to just "return gfx_read_char(vmode_g, cp);" here and have gfx_read_char() return a struct carattr?
No reason.
Paolo
On Fri, Jan 02, 2015 at 09:36:13AM +0100, Paolo Bonzini wrote:
On 02/01/2015 04:40, Kevin O'Connor wrote:
I couldn't find documentation for what to return as the attribute, but experimenting with DOSBox suggests 0 (and GWBasic accepts it).
Just for reference, for the "cbvga" text mode emulation, I grab the background attribute by assuming the lower right pixel of the cell is the background, and then assume the foreground attribute is the background attribute xor'd with 0x7 (see gfx_write_char()). I'm not sure if that hack applies here though. :-)
No, it doesn't. http://www.ctyme.com/intr/rb-0098.htm says AH is only defined in text mode, so it's probably safer to return zero.
Actually, it even says "only characters drawn with white foreground pixels are matched by the pattern-comparison routine". I can tweak the patch to only match color (1 << vga_bpp) - 1, or to add a comment that we extend the behavior.
Curiosity got the better of me, so I added some test code to SeaBIOS and ran it on machines with an ATI vgabios and Intel vgabios. The test code writes the letter 'A' with various foregrounds on various backgrounds.
It looks to me that these vgabios look for just zero and non-zero pixels (ie, they don't care what the foreground color is, they just want a non-black foreground). In the cases where there was a non-black background on a non-black foreground (eg, blue 'A' on green background) it treated the cell as character 0xdb (the font character with all pixels on), and cases where there was black foreground on non-black background (eg, black 'A' on green background) got treated as an undetected character.
Cases where the entire cell was blank (eg, black 'A' on black background) returned 0x00 instead of space (0x20). So, no special handling of that situation.
In many cases these vgabios did return an attribute in AH, but it's not clear to me how it decided which attr to return.
-Kevin
On AMD:
Mode 0: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 1: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 2: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 3: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 4: 200,241,241,241,1db,0,1db,1db,1db,1db,0,1db,1db,1db,1db,0, Mode 5: 200,241,241,241,1db,0,1db,1db,1db,1db,0,1db,1db,1db,1db,0, Mode 6: 241,241,241,241,0,0,0,0,0,0,0,0,0,0,0,0, Mode 7: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode d: 100,141,141,141,1db,0,1db,1db,1db,1db,0,1db,1db,1db,1db,0, Mode e: 100,141,141,141,1db,0,1db,1db,1db,1db,0,1db,1db,1db,1db,0, Mode f: 100,141,100,141,1db,0,1db,0,100,141,100,141,1db,0,1db,0, Mode 10: 100,141,141,141,1db,0,1db,1db,1db,1db,0,1db,1db,1db,1db,0, Mode 11: 141,141,141,141,0,0,0,0,0,0,0,0,0,0,0,0, Mode 12: 100,141,141,141,1db,0,1db,1db,1db,1db,0,1db,1db,1db,1db,0, Mode 13: 100,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, Mode 6a: 100,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,
On Intel:
Mode 0: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 1: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 2: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 3: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode 4: 0,41,41,41,db,6d38,db,db,db,db,6d38,db,db,db,db,6d38, Mode 5: 0,41,41,41,db,6d38,db,db,db,db,6d38,db,db,db,db,6d38, Mode 6: 41,41,41,41,6d38,6d38,6d38,6d38,6d38,6d38,6d38,6d38,6d38,6d38,6d38,6d38, Mode 7: 741,741,741,741,741,741,741,741,741,741,741,741,741,741,741,741, Mode d: 500,541,541,541,5db,500,5db,5db,5db,5db,500,5db,5db,5db,5db,500, Mode e: 500,541,541,541,5db,500,5db,5db,5db,5db,500,5db,5db,5db,5db,500, Mode f: 500,541,500,541,5db,500,5db,500,500,500,500,500,5db,500,5db,500, Mode 10: 500,541,541,541,5db,500,5db,5db,5db,5db,500,5db,5db,5db,5db,500, Mode 11: 500,541,500,541,5db,500,5db,500,500,541,500,541,5db,500,5db,500, Mode 12: 500,541,541,541,5db,500,5db,5db,5db,5db,500,5db,5db,5db,5db,500, Mode 13: 0,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, Mode 6a: 0,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,
SeaBIOS Patch:
diff --git a/src/bootsplash.c b/src/bootsplash.c index c572685..5035124 100644 --- a/src/bootsplash.c +++ b/src/bootsplash.c @@ -36,6 +36,53 @@ call16_int10(struct bregs *br) * VGA text / graphics console ****************************************************************/
+static void +testit(int mode) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ax = mode; + call16_int10(&br); + + dprintf(1, "Mode %x: ", mode); + int i, j; + for (i=0; i<4; i++) { + memset(&br, 0, sizeof(br)); + br.ah = 0x02; + br.dx = i<<8; + call16_int10(&br); + + memset(&br, 0, sizeof(br)); + br.ah = 0x0A; + br.al = 219; + br.bl = i; + br.cx = 4; + call16_int10(&br); + + for (j=0; j<4; j++) { + memset(&br, 0, sizeof(br)); + br.ah = 0x02; + br.dx = j | (i<<8); + call16_int10(&br); + + memset(&br, 0, sizeof(br)); + br.ah = 0x0A; + br.al = 'A'; + br.bl = j | (i ? 0x80 : 0); + br.cx = 1; + call16_int10(&br); + + memset(&br, 0, sizeof(br)); + br.ah = 0x08; + call16_int10(&br); + dprintf(1, "%x,", br.ax); + } + } + dprintf(1, "\n"); + + msleep(8000); +} + void enable_vga_console(void) { @@ -47,6 +94,15 @@ enable_vga_console(void) br.ax = 0x0003; call16_int10(&br);
+ int i; + for (i=0; i<=0x13; i++) + if (i <= 0x07 || i >= 0x0D) + testit(i); + testit(0x6A); + + br.ax = 0x0003; + call16_int10(&br); + // Write to screen. printf("SeaBIOS (version %s)\n", VERSION); display_uuid();
Hi,
If you want to go even more retro. Check ROM BASIC I extracted from old 286. I programmed a ROM loader to it, so SeaBIOS can load it from misc/
http://assembler.cz/rombasic.rom
This is a complete ROM already. I have even an MP3 with cassette recored of IBM diag tools, which I converted to ROM + implemented a cassette BIOS patch.
This can be loaded from fake "rom" which emulates the cassette in the ROM basic.
If anyone is interrested I can send to anyone.
Thanks Rudolf
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@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@redhat.com Signed-off-by: Kevin O'Connor kevin@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
On 02/01/2015 18:43, Kevin O'Connor wrote:
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
Sure!
Paolo
commit a5d54337852f26abeed1c2baa517d1870a641174 Author: Paolo Bonzini pbonzini@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@redhat.com> Signed-off-by: Kevin O'Connor <kevin@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;
vgafb_set_swcursor(0);return (struct carattr){0, 0, 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
On Thu, Jan 01, 2015 at 09:27:45PM +0100, Paolo Bonzini wrote:
What's a better small project for the holiday break, than fixing emulation of games and programs from your youth (saying "a few decades ago" makes me feel old)? At least it is easy to explain a suspicious wife why it is not work!
Thanks. I committed your first patch as well as the modified second patch.
-Kevin