/* * ______ ____ ______ _____ ______ * | ____| | _ \| ____| / / _ \| ____| * | |__ _ __ ___ ___| |_) | |__ / / |_| | |__ * | __| '__/ _ \/ _ \ _ <| __| / /| _ | __| * | | | | | __/ __/ |_) | |____ / / | | | | | * |_| |_| \___|\___|____/|______/_/ |_| |_|_| * * * Paradise driver (based on the Allegro Paradise code). * * By Francois Charton and Shawn Hargreaves. * * See freebe.txt for copyright information. */ // #define NO_HWPTR #include #include "vbeaf.h" /* chipset information */ #define PVGA1 1 #define WD90C 2 int paradise_type = 0; /* driver function prototypes */ void CirrusSetBank32( ); void CirrusSetBank32End( ); void CirrusSetBank(AF_DRIVER *af, long bank); void ParadiseSetBank32( ); void ParadiseSetBank32End( ); void ParadiseSetBank(AF_DRIVER *af, long bank); int ExtStub( ); long GetVideoModeInfo(AF_DRIVER *af, short mode, AF_MODE_INFO *modeInfo); long SetVideoMode(AF_DRIVER *af, short mode, long virtualX, long virtualY, long *bytesPerLine, int numBuffers, AF_CRTCInfo *crtc); void RestoreTextMode(AF_DRIVER *af); long GetClosestPixelClock(AF_DRIVER *af, short mode, unsigned long pixelClock); void SaveRestoreState(AF_DRIVER *af, int subfunc, void *saveBuf); void SetDisplayStart(AF_DRIVER *af, long x, long y, long waitVRT); void SetActiveBuffer(AF_DRIVER *af, long index); void SetVisibleBuffer(AF_DRIVER *af, long index, long waitVRT); int GetDisplayStartStatus(AF_DRIVER *af); void SetPaletteData(AF_DRIVER *af, AF_PALETTE *pal, long num, long index, long waitVRT); /* list which ports we are going to access (only needed under Linux) */ unsigned short ports_table[] = { 0xFFFF }; /* internal driver state variables */ int af_bpp; int af_width; int af_height; int af_visible_page; int af_active_page; int af_scroll_x; int af_scroll_y; int af_bank; /* FreeBE/AF extension allowing farptr access to video memory */ FAF_HWPTR_DATA hwptr; /* list of available video modes */ typedef struct VIDEO_MODE { int w, h; int bpp; int num; } VIDEO_MODE; VIDEO_MODE mode_list[] = { { 640, 400, 8, 0x5E }, { 640, 480, 8, 0x5F }, { 800, 600, 8, 0x5C } }; #define NUM_MODES (int)(sizeof(mode_list) / sizeof(VIDEO_MODE)) short available_modes[NUM_MODES + 1] = { 1, 2, 3, -1 }; /* detect: * Detects the presence of a Paradise card. */ char *detect(unsigned long *vidmem) { char *name = NULL; RM_REGS r; int old, old2; old = read_vga_register(0x3CE, 0xF); write_vga_register(0x3CE, 0xF, old | 0x17); /* lock extended registers */ if (test_vga_register(0x3CE, 0x9, 0x7F)) { /* not a Paradise card! */ write_vga_register(0x3CE, 0xF, old); return NULL; } alter_vga_register(0x3CE, 0xF, 0x17, 5); /* unlock extended regs */ if (!test_vga_register(0x3CE, 0x9, 0x7F)) { /* not a Paradise card! */ write_vga_register(0x3CE, 0xF, old); return NULL; } old2 = read_vga_register(0x3D4, 0x29); alter_vga_register(0x3D4, 0x29, 0x8F, 0x85); if (!test_vga_register(0x3D4, 0x2B, 0xFF)) { paradise_type = PVGA1; name = "PVGA1"; goto end; } write_vga_register(0x3C4, 0x06, 0x48); if (!test_vga_register(0x3C4, 0x7, 0xF0)) { paradise_type = PVGA1; name = "WD90C0x"; goto end; } if (!test_vga_register(0x3C4, 0x10, 0xFF)) { paradise_type = PVGA1; name = "WD90C2x"; write_vga_register(0x3D4, 0x34, 0xA6); if (read_vga_register(0x3D4, 0x32) & 0x20) write_vga_register(0x3D4, 0x34, 0); goto end; } paradise_type = WD90C; name = "WD90C1x or 24+"; end: write_vga_register(0x3D4, 0x29, old2); write_vga_register(0x3CE, 0xF, old); r.x.ax = 0x007F; r.h.bh = 0x02; rm_int(0x10, &r); *vidmem = r.h.ch * 64; return name; } /* SetupDriver: * Fills in our driver header block. */ int SetupDriver(AF_DRIVER *af) { char *name; int i; name = detect(&af->TotalMemory); if (!name) return 1; i = 0; while (af->OemVendorName[i]) i++; af->OemVendorName[i++] = ','; af->OemVendorName[i++] = ' '; while (*name) af->OemVendorName[i++] = *(name++); af->OemVendorName[i] = 0; af->AvailableModes = available_modes; af->Attributes = (afHaveMultiBuffer | afHaveVirtualScroll | afHaveBankedBuffer); af->BankSize = 64; af->BankedBasePtr = 0xA0000; af->IOPortsTable = ports_table; if (paradise_type == PVGA1) { af->SetBank32 = CirrusSetBank32; af->SetBank32Len = (long)CirrusSetBank32End - (long)CirrusSetBank32; af->SetBank = CirrusSetBank; } else { af->SetBank32 = ParadiseSetBank32; af->SetBank32Len = (long)ParadiseSetBank32End - (long)ParadiseSetBank32; af->SetBank = ParadiseSetBank; } af->SupplementalExt = ExtStub; af->GetVideoModeInfo = GetVideoModeInfo; af->SetVideoMode = SetVideoMode; af->RestoreTextMode = RestoreTextMode; af->GetClosestPixelClock = GetClosestPixelClock; af->SaveRestoreState = SaveRestoreState; af->SetDisplayStart = SetDisplayStart; af->SetActiveBuffer = SetActiveBuffer; af->SetVisibleBuffer = SetVisibleBuffer; af->GetDisplayStartStatus = GetDisplayStartStatus; af->SetPaletteData = SetPaletteData; return 0; } /* InitDriver: * The second thing to be called during the init process, after the * application has mapped all the memory and I/O resources we need. */ int InitDriver(AF_DRIVER *af) { hwptr_init(hwptr.IOMemMaps[0], af->IOMemMaps[0]); hwptr_init(hwptr.IOMemMaps[1], af->IOMemMaps[1]); hwptr_init(hwptr.IOMemMaps[2], af->IOMemMaps[2]); hwptr_init(hwptr.IOMemMaps[3], af->IOMemMaps[3]); hwptr_init(hwptr.BankedMem, af->BankedMem); hwptr_init(hwptr.LinearMem, af->LinearMem); return 0; } /* FreeBEX: * Returns an interface structure for the requested FreeBE/AF extension. */ void *FreeBEX(AF_DRIVER *af, unsigned long id) { switch (id) { #ifndef NO_HWPTR case FAFEXT_HWPTR: /* allow farptr access to video memory */ return &hwptr; #endif default: return NULL; } } /* ExtStub: * Vendor-specific extension hook: we don't provide any. */ int ExtStub( ) { return 0; } /* GetVideoModeInfo: * Retrieves information about this video mode, returning zero on success * or -1 if the mode is invalid. */ long GetVideoModeInfo(AF_DRIVER *af, short mode, AF_MODE_INFO *modeInfo) { VIDEO_MODE *info; int i; if ((mode <= 0) || (mode > NUM_MODES)) return -1; info = &mode_list[mode - 1]; for (i = 0; i < (int)sizeof(AF_MODE_INFO); i++) ((char *)modeInfo)[i] = 0; modeInfo->Attributes = (afHaveMultiBuffer | afHaveVirtualScroll | afHaveBankedBuffer); modeInfo->XResolution = info->w; modeInfo->YResolution = info->h; modeInfo->BitsPerPixel = info->bpp; modeInfo->MaxBuffers = (af->TotalMemory * 1024) / (info->w * info->h * BYTES_PER_PIXEL(info->bpp)); if (info->w > 1024) { modeInfo->MaxBytesPerScanLine = 2048 * BYTES_PER_PIXEL(info->bpp); modeInfo->MaxScanLineWidth = 2048; } else { modeInfo->MaxBytesPerScanLine = 1024 * BYTES_PER_PIXEL(info->bpp); modeInfo->MaxScanLineWidth = 1024; } modeInfo->BytesPerScanLine = info->w * BYTES_PER_PIXEL(info->bpp); modeInfo->BnkMaxBuffers = modeInfo->MaxBuffers; modeInfo->MaxPixelClock = 135000000; return 0; } /* SetVideoMode: * Sets the specified video mode, returning zero on success. */ long SetVideoMode(AF_DRIVER *af, short mode, long virtualX, long virtualY, long *bytesPerLine, int numBuffers, AF_CRTCInfo *crtc) { long available_vram; long used_vram; int width; VIDEO_MODE *info; RM_REGS r; /* reject anything with hardware stereo, linear framebuffer, or noclear */ if (mode & 0xC400) return -1; mode &= 0x3FF; if ((mode <= 0) || (mode > NUM_MODES)) return -1; info = &mode_list[mode - 1]; /* call BIOS to set the mode */ r.x.ax = info->num; rm_int(0x10, &r); /* adjust the virtual width for widescreen modes */ if (virtualX > info->w) { if (virtualX > 1024) return -1; *bytesPerLine = ((virtualX * BYTES_PER_PIXEL(info->bpp)) + 15) & 0xFFF0; width = read_vga_register(0x3D4, 0x13); write_vga_register(0x3D4, 0x13, (width * (*bytesPerLine)) / (info->w * BYTES_PER_PIXEL(info->bpp))); } else *bytesPerLine = info->w * BYTES_PER_PIXEL(info->bpp); /* set up some hardware registers */ if (paradise_type != PVGA1) { write_vga_register(0x3C4, 0x06, 0x48); alter_vga_register(0x3C4, 0x11, 0x80, 0x80); alter_vga_register(0x3CE, 0x0B, 0x80, 0x80); write_vga_register(0x3C4, 0x06, 0x00); } /* store info about the current mode */ af_bpp = info->bpp; af_width = *bytesPerLine; af_height = MAX(info->h, virtualY); af_visible_page = 0; af_active_page = 0; af_scroll_x = 0; af_scroll_y = 0; af_bank = -1; af->BufferEndX = af_width / BYTES_PER_PIXEL(af_bpp) - 1; af->BufferEndY = af_height - 1; af->OriginOffset = 0; used_vram = af_width * af_height * numBuffers; available_vram = af->TotalMemory * 1024; if (used_vram > available_vram) return -1; if (available_vram - used_vram >= af_width) { af->OffscreenOffset = used_vram; af->OffscreenStartY = af_height * numBuffers; af->OffscreenEndY = available_vram / af_width - 1; } else { af->OffscreenOffset = 0; af->OffscreenStartY = 0; af->OffscreenEndY = 0; } return 0; } /* RestoreTextMode: * Returns to text mode, shutting down the accelerator hardware. */ void RestoreTextMode(AF_DRIVER *af) { RM_REGS r; r.x.ax = 3; rm_int(0x10, &r); } /* GetClosestPixelClock: * I don't have a clue what this should return: it is used for the * refresh rate control. */ long GetClosestPixelClock(AF_DRIVER *af, short mode, unsigned long pixelClock) { /* ??? */ return 135000000; } /* SaveRestoreState: * Stores the current driver status: not presently implemented. */ void SaveRestoreState(AF_DRIVER *af, int subfunc, void *saveBuf) { /* not implemented (not used by Allegro) */ } /* SetDisplayStart: * Hardware scrolling function. The waitVRT value may be one of: * * -1 = don't set hardware, just store values for next page flip to use * 0 = set values and return immediately * 1 = set values and wait for retrace */ void SetDisplayStart(AF_DRIVER *af, long x, long y, long waitVRT) { if (waitVRT >= 0) { long a = (x * BYTES_PER_PIXEL(af_bpp)) + ((y + af_visible_page * af_height) * af_width); asm volatile("cli"); if (waitVRT) { do { } while (inportb(0x3DA) & 1); } /* write high bits to Paradise register 3CE indx 0xD, bits 3-4 */ alter_vga_register(0x3CE, 0x0D, 0x18, a >> 15); /* write to normal VGA address registers */ write_vga_register(0x3D4, 0x0D, (a >> 2) & 0xFF); write_vga_register(0x3D4, 0x0C, (a >> 10) & 0xFF); asm volatile("sti"); if (waitVRT) { do { } while (!(inportb(0x3DA) & 8)); } } af_scroll_x = x; af_scroll_y = y; } /* SetActiveBuffer: * Sets which buffer is being drawn onto, for use in multi buffering * systems (not used by Allegro). */ void SetActiveBuffer(AF_DRIVER *af, long index) { if (af->OffscreenOffset) { af->OffscreenStartY += af_active_page * af_height; af->OffscreenEndY += af_active_page * af_height; } af_active_page = index; af->OriginOffset = af_width * af_height * index; if (af->OffscreenOffset) { af->OffscreenStartY -= af_active_page * af_height; af->OffscreenEndY -= af_active_page * af_height; } } /* SetVisibleBuffer: * Sets which buffer is displayed on the screen, for use in multi buffering * systems (not used by Allegro). */ void SetVisibleBuffer(AF_DRIVER *af, long index, long waitVRT) { af_visible_page = index; SetDisplayStart(af, af_scroll_x, af_scroll_y, waitVRT); } /* GetDisplayStartStatus: * Status poll for triple buffering. Not possible on the majority of * present cards: this function is just a placeholder. */ int GetDisplayStartStatus(AF_DRIVER *af) { return 1; } /* SetPaletteData: * Palette setting routine. */ void SetPaletteData(AF_DRIVER *af, AF_PALETTE *pal, long num, long index, long waitVRT) { int i; if (waitVRT) { do { } while (inportb(0x3DA) & 8); do { } while (!(inportb(0x3DA) & 8)); } for (i = 0; i < num; i++) { outportb(0x3C8, index + i); outportb(0x3C9, pal[i].red / 4); outportb(0x3C9, pal[i].green / 4); outportb(0x3C9, pal[i].blue / 4); } } /* CirrusSetBank32: * Relocatable bank switch function, called with a bank number in %edx. */ asm (" .globl _CirrusSetBank32, _CirrusSetBank32End .align 4 _CirrusSetBank32: pushl %edx pushl %eax pushl %edx movl $0x3CE, %edx /* 3CE index 0x9 */ movb $0x9, %al outb %al, %dx popl %eax /* write bank number */ shll $4, %eax incl %edx outb %al, %dx popl %eax popl %edx ret _CirrusSetBank32End: "); /* CirrusSetBank: * C-callable bank switch function. This version simply chains to the * relocatable CirrusSetBank32() above. */ void CirrusSetBank(AF_DRIVER *af, long bank) { asm(" call _CirrusSetBank32 " : : "d"(bank)); af_bank = bank; } /* ParadiseSetBank32: * Relocatable bank switch function, called with a bank number in %edx. */ asm (" .globl _ParadiseSetBank32, _ParadiseSetBank32End .align 4 _ParadiseSetBank32: pushl %eax pushl %ecx pushl %edx movl %edx, %ecx shll $4, %ecx movl $0x3CE, %edx /* 3CE index 0x9 */ movb $0x9, %al outb %al, %dx movl %ecx, %eax /* set read bank number */ incl %edx outb %al, %dx decl %edx /* 3CE index 0xA */ movb $0xA, %al outb %al, %dx movl %ecx, %eax /* set write bank number */ incl %edx outb %al, %dx popl %edx popl %ecx popl %eax ret _ParadiseSetBank32End: "); /* ParadiseSetBank: * C-callable bank switch function. This version simply chains to the * relocatable ParadiseSetBank32() above. */ void ParadiseSetBank(AF_DRIVER *af, long bank) { asm(" call _ParadiseSetBank32 " : : "d"(bank)); af_bank = bank; }