========
Background:
========

I don't really think it is appropriate to include this patch in it's
current form, but I'm posting it to illustrate some of the obscure
things that Microport UNIX System V/386 v 2.1 (ca 1987) is trying
to do to the video card.

On real hardware, this version of UNIX could drive an MDA graphics
card (actually an HGC [hurcules graphics card] in text mode only)
just fine.  BUT, the VGA card and monitor would always
display snow (timings were out of sync, and/or other problems).
In 1987, I initially had an HGC-only system, and added a VGA
to it later as a second graphics card and monitor.  DOS could switch
between them (the "mode" command), but UNIX was only usable on the HGC.

The fact that it couldn't run a real VGA properly suggests that
hacking the emulated VGA isn't really the best solution.  A better
approach might be to add specific support for graphics cards
(MDA, HGC, and/or maybe plain-CGA) that UNIX works properly on.

If anyone is interested in examining this UNIX system
directly (for these VGA issues or other issues), let me know and
I can email you at least the first (bootable) installation
floppy image.

========
Visible Problems in Qemu:
========

Running and debugging UNIX in qemu, I see two main VGA-related
problems, and a few associated nits (not counting non-VGA issues
written up separately):

1. (Blank screen): UNIX weirdly writes exactly
   one byte to 0x3c0 (VGA_ATT_W).  After reading
   from 0x3da (which resets 0x3c0 to index mode),
   it writes a 0 to 0x3c0, and then doesn't touch the register again.
   It doesn't actually modify the contents of the data behind any index.
   Writing zero also clears the 0x20 "palette access bit", which forces
   qemu vga_update_display() to treat the screen as "GMODE_BLANK".
    - My hackish workaround will not blank the screen if the index
      register is still 0.  Perhaps this hack could be (slightly)
      improved to look at the index/data flip-flop as well, under
      the theory that until the OS actually starts modifying things,
      it is OK for the card to keep using the old palette?

2. (Characters cut in half): UNIX appears to be trying
   to program the CRTC under the assumption that the text
   mode is using a font that is only 8 pixels high, instead
   of 16.  I would guess it is trying to treat it like a CGA card.
    - (main problem): It changes CRTC register 9 (0x3d4[9]/0x3d5)
      from 0x4f to 0x07.  The 0xf vs 7 halves the height of
      the character, cutting off the bottom halves.  The 0x40
      bit doesn't seem to hurt, but documentation explains how
      it has something to do with split screen scrolling.
    - My hackish workaround is to ignore attempts to modify
      register 9 if the lock bit in register 0x11 is locked.
    - UNIX is also changing the cursor-height-within-a-character
      values from (0xe through 0xf) to (6 through 7), effectively
      moving the cursor to the middle of the character.
      (I haven't tried to work around this, but it would be
      trivial to disable these registers under some conditions TBD.
      Or perhaps translate the supplied values with scaling logic.)
    - UNIX changes register 8 (0x3d4[8]/0x3d5) from 0 to 2, but
      this doesn't seem to hurt.  (Documentation says it has to
      do with smooth text mode scrolling, but UNIX only sets it
      once...)
    - UNIX also attempts to change registers 0-7 (0x3d4[0-7]/0x3d5), but
      these attempts are ignored by qemu because of the lock bit
      in register 0x11 (0x3d4[0x11]/0x3d5).  (UNIX doesn't seem to
      know anything about registers greater than 0xf.)

3. I haven't looked to see if it programming other VGA subsystems
   strangely, but it wouldn't surprise me.

========
Possible "Real" Solutions:
========

1. The ideal solution would probably be to implement an emulated plain MDA
   adapter, and a way to select it on the command line.  (It could also be
   extended to optionally be HGC-capable and/or to support a
   dual monitor with a CGA/VGA [with two windows for the two
   monitors], as well.)
      Superficially, emulating an MDA or HGC card itself probably
   wouldn't be too hard (mostly just copy, strip down [simplify], and
   tweak the VGA support), but I'm still a little vague on the best
   way to get it all hooked into the overall system cleanly,
   and appropriate configurations options (perhaps options
   like "-vga mda", "-vga mda+std", "-vgs cirrus+hgc", etc?).
      This seems like a moderate amount of work, but it has the highest
   likelyhood of being useful for running other ancient software,
   rather than a hackish workaround for one rare OS.

2. Perhaps add some special command line option to enable hacks similar
   to what I do in this patch (normally disabled).  This is probably
   the easiest.  Any suggestions for command-line options, etc?

3. I wonder if maybe an alternative approach to these problems would be
   to somehow identify that the target is trying to use the VGA like
   a CGA, and have the fixups/hacks move it towards CGA functionality
   instead of leaving things in VGA mode and disabling the bad
   parts?  (e.g. use a 8 pixel high font, etc)
    - Identify if this is the case, and make a narrow exception
      for better CGA support?
    - Perhaps add a command line option to have the graphics card be
      limited to just CGA functionality?

(Intentionally not signed off; this version of this patch isn't appropriate
for general use.)
---
 hw/vga.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/hw/vga.c b/hw/vga.c
index f82ced8..c57d8e2 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -548,7 +548,8 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 #endif
         /* handle CR0-7 protection */
         if ((s->cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) &&
-            s->cr_index <= VGA_CRTC_OVERFLOW) {
+            (s->cr_index <= VGA_CRTC_OVERFLOW ||
+             s->cr_index == VGA_CRTC_MAX_SCAN)) {
             /* can always write bit 4 of CR7 */
             if (s->cr_index == VGA_CRTC_OVERFLOW) {
                 s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) |
@@ -1886,7 +1887,7 @@ static void vga_update_display(void *opaque)
         /* nothing to do */
     } else {
         full_update = 0;
-        if (!(s->ar_index & 0x20)) {
+        if (!(s->ar_index & 0x20) && s->ar_index != 0) {
             graphic_mode = GMODE_BLANK;
         } else {
             graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE;
