mfld-fr / emu86

Intel IA16 emulator for embedded development
35 stars 6 forks source link

Cannot play Alleycat ? #57

Closed mfld-fr closed 3 years ago

mfld-fr commented 3 years ago

This is a very good test case for the graphical console :wink:

A:\>alleycat
Please turn on the color display.
error: INT 10h AH=00h: unsupported mode 4h
error: execute operation
2B0F:5C93  CD 10              INT     10h
ghaerr commented 3 years ago

Interesting. I will look into this after #56 is committed, as it needs your new "reference" PC BIOS in order to boot...

It looks like INT 10h AH=00h video mode 4 (according to Table of Video Modes) is 320x200 four-color graphics; I assume EGA 4-plane mode. Its probably a lot of work to emulate the EGA graphics modes, not sure how worth it this would be. Also, the SDL backend will need to be modified to resize its window from 640x400 (used for 80x25 text) to 320x200 etc for each graphics mode. (And it looks like there's 4 other graphics modes as well, all different sizes).

Emulating the EGA 4-plane mode involves interpreting the following outb functions to the CRT Controller (taken from Nano-X EGA graphics driver):

/* Program the Set/Reset Register for drawing in color COLOR for write
   mode 0. */
#define set_color(c)        { outb (0, 0x3ce); outb (c, 0x3cf); }

/* Set the Enable Set/Reset Register. */
#define set_enable_sr(mask) { outb (1, 0x3ce); outb (mask, 0x3cf); }

/* Select the Bit Mask Register on the Graphics Controller. */
#define select_mask()       { outb (8, 0x3ce); }

/* Program the Bit Mask Register to affect only the pixels selected in
   MASK.  The Bit Mask Register must already have been selected with
   select_mask (). */
#define set_mask(mask)      { outb (mask, 0x3cf); }

/* Set the Data Rotate Register.  Bits 0-2 are rotate count, bits 3-4
   are logical operation (0=NOP, 1=AND, 2=OR, 3=XOR). */
#define set_op(op)      { outb (3, 0x3ce); outb (op, 0x3cf); }

/* Set the Memory Plane Write Enable register. */
#define set_write_planes(mask) { outb (2, 0x3c4); outb (mask, 0x3c5); }

/* Set the Read Map Select register. */
#define set_read_plane(plane)   { outb (4, 0x3ce); outb (plane, 0x3cf); }

/* Set the Graphics Mode Register.  The write mode is in bits 0-1, the
   read mode is in bit 3. */
#define set_mode(mode)      { outb (5, 0x3ce); outb (mode, 0x3cf); }

Now you know why framebuffers took over the world (at least before graphics pipeline GPUs) and nobody writes any code for EGA/VGA 4-plane hardware anymore!!!

ghaerr commented 3 years ago

To give you an example of how complicated EGA graphics are, here is the "fast" EGA horizontal line draw code from the Nano-X driver. This is what it takes to just "quickly" draw a horizontal line. On ELKS, users with real IBM PC hardware are complaining how slow this displays (in C, not ASM). I can't imagine the speed under EMU86!

/* read-modify-write at address*/
#define RMW_FP(addr)        ((*(FARADDR)(addr)) |= 1)

/* Draw horizontal line from x1,y to x2,y not including final point*/
void
ega_drawhorzline(PSD psd, unsigned int x1, unsigned int x2, unsigned int y,
    PIXELVAL c)
{   
    FARADDR dst, last;

    --x2;
    assert (x1 >= 0 && x1 < psd->xres);
    assert (x2 >= 0 && x2 < psd->xres);
    assert (x2 >= x1);
    assert (y >= 0 && y < psd->yres);
    assert (c >= 0 && c < psd->ncolors);

    DRAWON;
    set_color (c);
    set_op(mode_table[gr_mode]);
    /*
    * The following fast drawhline code is buggy for XOR drawing,
    * for some reason.  So, we use the equivalent slower drawpixel
    * method when not drawing MODE_SET.
    */
    if(gr_mode == MODE_SET) { 
        dst = SCREENBASE + x1 / 8 + y * BYTESPERLINE;
        select_mask ();
        if (x1 / 8 == x2 / 8) {
            set_mask ((0xff >> (x1 % 8)) & (0xff << (7 - x2 % 8)));
            RMW_FP (dst);
        } else {

            set_mask (0xff >> (x1 % 8));
            RMW_FP (dst++);

            set_mask (0xff);
            last = SCREENBASE + x2 / 8 + y * BYTESPERLINE;
            while (dst < last)
                PUTBYTE_FP(dst++, 1);

            set_mask (0xff << (7 - x2 % 8));
            RMW_FP (dst);
        }
    } else {
        /* slower method, draw pixel by pixel*/
        select_mask ();
        while(x1 < x2) {
            set_mask (mask[x1&7]);
            RMW_FP (SCREENBASE + x1++ / 8 + y * BYTESPERLINE);
        }
    }
    DRAWOFF;
}
mfld-fr commented 3 years ago

Hey, stop running, that issue was a only a joke after #30 :smile_cat: !

EMU86 is not intended for end user PC emulation, as stated in the README. One can play Alleycat with QEMU or whatever, and CGA support brings nothing in term of debugging for embedded.

Going further in booting a 'standard' OS like MS-DOS or FreeDOS was only a test case for the emulation engine.