correct window size in banked vbe, and added doublebuffered test
[vidsys] / drv_vbe.c
index 59ec306..e09ca4d 100644 (file)
--- a/drv_vbe.c
+++ b/drv_vbe.c
@@ -4,6 +4,7 @@
 #include "vidsys.h"
 #include "drv.h"
 #include "vbe.h"
+#include "vga.h"
 #include "cdpmi.h"
 
 #define farptr_to_linear(rmaddr) \
@@ -11,6 +12,7 @@
 
 static int init(void);
 static void cleanup(void);
+static struct vid_modeinfo *find_mode(int mode);
 static int setmode(int mode);
 static int getmode(void);
 static const char *memsize_str(long sz);
@@ -18,11 +20,20 @@ static int get_mode_info(int mode, struct vbe_mode_info *mi);
 static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi);
 static unsigned int calc_mask(int nbits, int pos);
 
+static void pack(uint32_t *pix, int r, int g, int b);
+static void unpack(uint32_t pix, int *r, int *g, int *b);
+static void clear(uint32_t color);
+static void blitfb_lfb(void *fb, int pitch);
+static void blitfb_banked(void *fb, int pitch);
+static void flip(int vsync);
+
 static struct vid_driver drv;
 static struct vid_drvops drvops = {init, cleanup, setmode, getmode};
 static unsigned int vbe_ver;
 
 static int cur_mode;
+static struct vid_modeinfo *cur_mi;
+static int cur_pgsize;
 
 
 void vid_register_vbe(void)
@@ -46,6 +57,7 @@ static int init(void)
        struct vid_modeinfo modeinf;
 
        cur_mode = -1;
+       cur_mi = 0;
 
        vbe = dpmi_lowbuf();
        bufseg = (intptr_t)vbe >> 4;
@@ -55,7 +67,7 @@ static int init(void)
        regs.eax = 0x4f00;
        regs.es = bufseg;
        dpmi_rmint(0x10, &regs);
-       if(regs.eax != 0x4f || memcmp(vbe->sig, "VESA", 4) != 0) {
+       if((regs.eax & 0xffff) != 0x4f || memcmp(vbe->sig, "VESA", 4) != 0) {
                fprintf(stderr, "failed to get VBE controller information\n");
                return -1;
        }
@@ -114,24 +126,48 @@ static void cleanup(void)
        drv.num_modes = 0;
 }
 
+static struct vid_modeinfo *find_mode(int mode)
+{
+       int i;
+       for(i=0; i<drv.num_modes; i++) {
+               if(drv.modes[i].modeno == mode) {
+                       return drv.modes + i;
+               }
+       }
+       return 0;
+}
+
 static int setmode(int mode)
 {
+       struct vid_modeinfo *minf;
        struct dpmi_regs regs = {0};
 
+       if((minf = find_mode(mode)) && minf->lfb) {
+               mode |= VBE_MODE_LFB;
+       }
+
+retry:
        regs.eax = 0x4f02;
-       regs.ebx = mode | VBE_MODE_LFB;
+       regs.ebx = mode;
        dpmi_rmint(0x10, &regs);
 
-       if(regs.eax != 0x4f) {
-               regs.eax = 0x4f02;
-               regs.ebx = mode;
-               dpmi_rmint(0x10, &regs);
-               if(regs.eax != 0x4f) {
-                       return -1;
+       if((regs.eax & 0xffff) != 0x4f) {
+               if(mode & VBE_MODE_LFB) {
+                       mode &= ~VBE_MODE_LFB;
+                       goto retry;
                }
-               cur_mode = mode;
+               return -1;
+       }
+       cur_mode = mode;
+
+       if(!(cur_mi = minf)) return 0;
+
+       cur_pgsize = minf->height * minf->pitch;
+
+       if(mode & VBE_MODE_LFB) {
+               minf->ops.blitfb = blitfb_lfb;
        } else {
-               cur_mode = mode | VBE_MODE_LFB;
+               minf->ops.blitfb = blitfb_banked;
        }
        return 0;
 }
@@ -169,7 +205,7 @@ static int get_mode_info(int mode, struct vbe_mode_info *mi)
        regs.ecx = mode;
        regs.es = bufseg;
        dpmi_rmint(0x10, &regs);
-       if(regs.eax != 0x4f) {
+       if((regs.eax & 0xffff) != 0x4f) {
                return -1;
        }
 
@@ -177,8 +213,23 @@ static int get_mode_info(int mode, struct vbe_mode_info *mi)
        return 0;
 }
 
+int vid_setwin(int wid, int pos)
+{
+       struct dpmi_regs regs = {0};
+
+       regs.eax = 0x4f05;
+       regs.ebx = wid;
+       regs.edx = pos;
+       dpmi_rmint(0x10, &regs);
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return 0;
+}
+
 static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi)
 {
+       static int gran_shift;
        static const struct { int width, height, bpp; } stdmode[] = {
                {640, 400, 8},          /* 100h */
                {640, 480, 8},          /* 101h */
@@ -235,6 +286,26 @@ static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info
        mi->pitch = vbemi->scanline_bytes;
        mi->win_size = vbemi->win_size;
        mi->win_gran = vbemi->win_gran;
+
+       gran_shift = 0;
+       mi->win_step = 1;
+       if(mi->win_gran > 0 && mi->win_gran < 64) {
+               int gran = mi->win_gran;
+               while(gran < 64) {
+                       gran_shift++;
+                       gran <<= 1;
+               }
+               mi->win_step = 1 << gran_shift;
+       }
+
+       mi->ops.pack = pack;
+       mi->ops.unpack = unpack;
+       mi->ops.setpal = vga_setpal;
+       mi->ops.getpal = vga_getpal;
+       mi->ops.vsync = vid_vsync;
+       mi->ops.clear = clear;
+       mi->ops.blitfb = 0;
+       mi->ops.flip = flip;
        return 0;
 }
 
@@ -248,3 +319,63 @@ static unsigned int calc_mask(int nbits, int pos)
        }
        return mask << pos;
 }
+
+
+static void pack(uint32_t *pix, int r, int g, int b)
+{
+       *pix = (((uint32_t)r << cur_mi->rshift) & cur_mi->rmask) |
+               (((uint32_t)g << cur_mi->gshift) & cur_mi->gmask) |
+               (((uint32_t)b << cur_mi->bshift) & cur_mi->bmask);
+}
+
+static void unpack(uint32_t pix, int *r, int *g, int *b)
+{
+       *r = (pix & cur_mi->rmask) >> cur_mi->rshift;
+       *g = (pix & cur_mi->gmask) >> cur_mi->gshift;
+       *b = (pix & cur_mi->bmask) >> cur_mi->bshift;
+}
+
+static void clear(uint32_t color)
+{
+}
+
+static void blitfb_lfb(void *fb, int pitch)
+{
+       int i;
+       unsigned char *dest, *src;
+
+       dest = vid_vmem;
+       src = fb;
+
+       for(i=0; i<cur_mi->height; i++) {
+               memcpy(dest, src, cur_mi->pitch);
+               dest += cur_mi->pitch;
+               src += pitch;
+       }
+}
+
+static void blitfb_banked(void *fb, int pitch)
+{
+       int sz, offs, pending, winsz;
+       unsigned char *pptr = fb;
+
+       winsz = cur_mi->win_size << 10;
+
+       /* assume initial window offset at 0 */
+       offs = 0;
+       pending = cur_pgsize;
+       while(pending > 0) {
+               sz = pending > winsz ? winsz : pending;
+               memcpy((void*)0xa0000, pptr, sz);
+               pptr += sz;
+               pending -= sz;
+               offs += cur_mi->win_step;
+               vid_setwin(0, offs);
+       }
+       vid_setwin(0, 0);
+}
+
+static void flip(int vsync)
+{
+       /* TODO */
+}