/* ** thinlib (c) 2001 Matthew Conte (matt@conte.com) ** ** ** tl_vga.c ** ** VGA-related functions ** ** $Id: $ */ #include #include #include #include #include "tl_types.h" #include "tl_bmp.h" #include "tl_video.h" #include "tl_vga.h" #include "tl_djgpp.h" #include "tl_int.h" #define DEFAULT_OVERSCAN 0 #define MODE_TEXT 0x03 #define MODE_13H 0x13 #define VGA_ADDRESS 0xA0000 /* we love segments! */ /* VGA card register addresses */ #define VGA_ATTR 0x3C0 /* Attribute reg */ #define VGA_MISC 0x3C2 /* Misc. output register */ #define VGA_SEQ_ADDR 0x3C4 /* Base port of sequencer */ #define VGA_SEQ_DATA 0x3C5 /* Data port of sequencer */ #define VGA_CRTC_ADDR 0x3D4 /* Base port of CRT controller */ #define VGA_CRTC_DATA 0x3D5 /* Data port of CRT controller */ #define VGA_STATUS 0x3DA /* Input status #1 register */ #define VGA_PAL_READ 0x3C7 /* Palette read address */ #define VGA_PAL_WRITE 0x3C8 /* Palette write address */ #define VGA_PAL_DATA 0x3C9 /* Palette data register */ /* generic VGA CRTC register indexes */ #define HZ_DISPLAY_TOTAL 0x00 #define HZ_DISPLAY_END 0x01 #define CRTC_OVERFLOW 0x07 #define VT_DISPLAY_END 0x12 #define MEM_OFFSET 0x13 /* indices into our register array */ #define CLOCK_INDEX 0 #define H_TOTAL_INDEX 1 #define H_DISPLAY_INDEX 2 #define H_BLANKING_START_INDEX 3 #define H_BLANKING_END_INDEX 4 #define H_RETRACE_START_INDEX 5 #define H_RETRACE_END_INDEX 6 #define V_TOTAL_INDEX 7 #define OVERFLOW_INDEX 8 #define MAXIMUM_SCANLINE_INDEX 10 #define V_RETRACE_START_INDEX 11 #define V_RETRACE_END_INDEX 12 #define V_END_INDEX 13 #define MEM_OFFSET_INDEX 14 #define UNDERLINE_LOC_INDEX 15 #define V_BLANKING_START_INDEX 16 #define V_BLANKING_END_INDEX 17 #define MODE_CONTROL_INDEX 18 #define MEMORY_MODE_INDEX 20 typedef struct vgareg_s { int port; int index; uint8 value; } vgareg_t; typedef struct vgamode_s { int width; int height; char *name; vgareg_t *regs; } vgamode_t; /* 60 Hz */ static vgareg_t mode_256x224[] = { { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x5F }, { 0x3D4, 0x01, 0x3F }, { 0x3D4, 0x02, 0x40 }, { 0x3D4, 0x03, 0x82 }, { 0x3D4, 0x04, 0x4A }, { 0x3D4, 0x05, 0x9A }, { 0x3D4, 0x06, 0x0B }, { 0x3D4, 0x07, 0xB2 }, { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x61 }, { 0x3d4, 0x10, 0x00 }, { 0x3D4, 0x11, 0xAC }, { 0x3D4, 0x12, 0xBF }, { 0x3D4, 0x13, 0x20 }, { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0x01 }, { 0x3D4, 0x16, 0x0A }, { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, { 0, 0, 0 } }; static vgareg_t mode_256x240[] = { { 0x3c2, 0x00, 0xe3},{ 0x3d4, 0x00, 0x55},{ 0x3d4, 0x01, 0x3f}, { 0x3d4, 0x02, 0x80},{ 0x3d4, 0x03, 0x90},{ 0x3d4, 0x04, 0x49}, { 0x3d4, 0x05, 0x80},{ 0x3D4, 0x06, 0x43},{ 0x3d4, 0x07, 0xb2}, { 0x3d4, 0x08, 0x00},{ 0x3D4, 0x09, 0x61},{ 0x3d4, 0x10, 0x04}, { 0x3d4, 0x11, 0xac},{ 0x3D4, 0x12, 0xdf},{ 0x3d4, 0x13, 0x20}, { 0x3d4, 0x14, 0x40},{ 0x3d4, 0x15, 0x07},{ 0x3D4, 0x16, 0x11}, { 0x3d4, 0x17, 0xa3},{ 0x3c4, 0x01, 0x01},{ 0x3c4, 0x04, 0x0e}, { 0, 0, 0 } }; static vgareg_t mode_256x256[] = { { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x5F }, { 0x3D4, 0x01, 0x3F }, { 0x3D4, 0x02, 0x40 }, { 0x3D4, 0x03, 0x82 }, { 0x3D4, 0x04, 0x4A }, { 0x3D4, 0x05, 0x9A }, { 0x3D4, 0x06, 0x23 }, { 0x3D4, 0x07, 0xB2 }, { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x61 }, { 0x3D4, 0x10, 0x0A }, { 0x3D4, 0x11, 0xAC }, { 0x3D4, 0x12, 0xFF }, { 0x3D4, 0x13, 0x20 }, { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0x07 }, { 0x3D4, 0x16, 0x1A }, { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, { 0, 0, 0 } }; /* 60 Hz */ static vgareg_t mode_256x256wide[] = { { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x52 }, { 0x3D4, 0x01, 0x3F }, { 0x3D4, 0x02, 0x80 }, { 0x3D4, 0x03, 0x90 }, { 0x3D4, 0x04, 0x49 }, { 0x3D4, 0x05, 0x80 }, { 0x3D4, 0x06, 0x55 }, { 0x3D4, 0x07, 0xB2 }, { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x61 }, { 0x3D4, 0x10, 0x20 }, { 0x3D4, 0x11, 0xAC }, { 0x3D4, 0x12, 0xFF }, { 0x3D4, 0x13, 0x20 }, { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0x07 }, { 0x3D4, 0x16, 0x1A }, { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, { 0, 0, 0 } }; /* 60 Hz */ static vgareg_t mode_288x224[] = { { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x5F }, { 0x3D4, 0x01, 0x47 }, { 0x3D4, 0x02, 0x50 }, { 0x3D4, 0x03, 0x82 }, { 0x3D4, 0x04, 0x50 }, { 0x3D4, 0x05, 0x80 }, { 0x3D4, 0x06, 0x08 }, { 0x3D4, 0x07, 0x3E }, { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x41 }, { 0x3D4, 0x10, 0xDA }, { 0x3D4, 0x11, 0x9C }, { 0x3D4, 0x12, 0xBF }, { 0x3D4, 0x13, 0x24 }, { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0xC7 }, { 0x3D4, 0x16, 0x04 }, { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, { 0, 0, 0 } }; static vgareg_t mode_320x200[] = { { 0, 0, 0 } }; static vgamode_t vidmodes[] = { { 288, 224, "288x224", mode_288x224 }, { 256, 224, "256x224", mode_256x224 }, { 256, 240, "256x240", mode_256x240 }, { 256, 256, "256x256 (wide)", mode_256x256wide }, { 256, 256, "256x256", mode_256x256 }, { 320, 200, "320x200", mode_320x200 }, { 0, 0, NULL, 0 } }; static bitmap_t *screen = NULL; static bitmap_t *hardware = NULL; /* current VGA mode */ static vgamode_t *vga_mode = NULL; static bool vga_hardware = false; /* Set a VGA mode */ static void vga_setvgamode(uint8 mode) { __dpmi_regs r; r.x.ax = mode; __dpmi_int(0x10, &r); } static void vga_set_overscan(int index) { outportb(VGA_ATTR, 0x31); outportb(VGA_ATTR, index); } static void vga_outregs(vgareg_t *reg) { uint8 crtc_val; /* Disable interrupts, wait for vertical retrace */ // thin_vga_waitvsync(); THIN_DISABLE_INTS(); /* Sequencer reset */ outportb(VGA_SEQ_ADDR, 0x00); outportb(VGA_SEQ_DATA, 0x01); crtc_val = inportb(VGA_CRTC_DATA) & 0x7F; /* Unprotect registers 0-7 */ outportb(VGA_CRTC_ADDR, 0x11); outportb(VGA_CRTC_DATA, crtc_val); /* Reset read/write flip-flop */ inportb(VGA_STATUS); /* Do the icky register stuff */ while (reg->port) { switch(reg->port) { case VGA_ATTR: /* Reset read/write flip-flop */ inportb(VGA_STATUS); /* Ensure VGA output is enabled - bit 5 */ outportb(VGA_ATTR, reg->index | 0x20); outportb(VGA_ATTR, reg->value); break; case VGA_MISC: /* Write directly to port */ outportb(reg->port, reg->value); break; case VGA_SEQ_ADDR: case VGA_CRTC_ADDR: default: /* Index to port, value to port + 1 */ outportb(reg->port, reg->index); outportb(reg->port + 1, reg->value); break; } reg++; } /* Set overscan color */ vga_set_overscan(DEFAULT_OVERSCAN); /* Clear sequencer reset */ outportb(VGA_SEQ_ADDR, 0x00); outportb(VGA_SEQ_DATA, 0x03); THIN_ENABLE_INTS(); } /* Set up VGA mode 13h, then tweak it appropriately */ int thin_vga_setmode(int width, int height, int bpp) { if (8 != bpp) return -1; vga_mode = vidmodes; /* Search for the video mode */ while ((vga_mode->width != width) || (vga_mode->height != height)) { if (NULL == vga_mode->regs) { vga_mode = NULL; return -1; } vga_mode++; } /* Set up our standard mode 13h */ vga_setvgamode(MODE_13H); vga_outregs(vga_mode->regs); return 0; } /* Destroy VGA */ void thin_vga_shutdown(void) { /* set textmode */ vga_setvgamode(MODE_TEXT); if (screen) thin_bmp_destroy(&screen); if (hardware) thin_bmp_destroy(&hardware); } /* Initialize VGA */ int thin_vga_init(int width, int height, int bpp, int param) { if (8 != bpp) return -1; /* ensure we really want a hardware surface */ if (thinlib_nearptr && (param & THIN_VIDEO_HWSURFACE)) { vga_hardware = true; screen = thin_bmp_createhw((uint8 *) THIN_PHYSICAL_ADDR(VGA_ADDRESS), width, height, bpp, width); if (NULL == screen) return -1; } else { vga_hardware = false; hardware = thin_bmp_createhw((uint8 *) VGA_ADDRESS, width, height, bpp, width); if (NULL == hardware) return -1; screen = thin_bmp_create(width, height, bpp, 0); if (NULL == screen) return -1; } /* Set the initial video mode, no scanlines */ if (thin_vga_setmode(width, height, bpp)) { thin_vga_shutdown(); return -1; } return 0; } /* cram an 8-bit, 256 entry rgb palette into 6-bit vga */ void thin_vga_setpalette(rgb_t *palette, int index, int length) { int i; /* we also want to find the closest color index to black, ** and set that as our overscan color */ int overscan_index = 0; int overscan_sum = 255 * 3; outportb(VGA_PAL_WRITE, index); for (i = 0; i < length; i++) { if (palette[i].r + palette[i].g + palette[i].b < overscan_sum) { overscan_sum = palette[i].r + palette[i].g + palette[i].b; overscan_index = index + i; } outportb(VGA_PAL_DATA, palette[i].r >> 2); outportb(VGA_PAL_DATA, palette[i].g >> 2); outportb(VGA_PAL_DATA, palette[i].b >> 2); } vga_set_overscan(overscan_index); } void thin_vga_waitvsync(void) { while (0 == (inportb(VGA_STATUS) & 0x08)); //while (inportb(VGA_STATUS) & 0x08); } bitmap_t *thin_vga_lockwrite(void) { /* always return screen */ return screen; } void thin_vga_freewrite(int num_dirties, rect_t *dirty_rects) { UNUSED(num_dirties); UNUSED(dirty_rects); if (false == vga_hardware) { dosmemput(screen->line[0], hardware->pitch * hardware->height, (int) hardware->line[0]); } } /* ** $Log: $ */