Modules/VESA - Rework to handle boot-time modes (EGA text too)
[tpg/acess2.git] / KernelLand / Modules / Display / VESA / main.c
1 /*\r
2  * AcessOS 1\r
3  * Video BIOS Extensions (Vesa) Driver\r
4  */\r
5 #define DEBUG   1\r
6 #define VERSION 0x100\r
7 \r
8 #include <acess.h>\r
9 #include <vfs.h>\r
10 #include <api_drv_video.h>\r
11 #include <fs_devfs.h>\r
12 #include <modules.h>\r
13 #include <vm8086.h>\r
14 #include "common.h"\r
15 #include <timers.h>\r
16 \r
17 // === CONSTANTS ===\r
18 #define FLAG_LFB        0x1\r
19 #define FLAG_POPULATED  0x2\r
20 #define FLAG_VALID      0x4\r
21 #define VESA_DEFAULT_FRAMEBUFFER        (KERNEL_BASE|0xA0000)\r
22 #define BLINKING_CURSOR 0\r
23 #if BLINKING_CURSOR\r
24 # define VESA_CURSOR_PERIOD     1000\r
25 #endif\r
26 \r
27 // === PROTOTYPES ===\r
28  int    Vesa_Install(char **Arguments);\r
29  int    VBE_int_GetModeList(void);\r
30 size_t  Vesa_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);\r
31  int    Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
32  int    Vesa_Int_SetMode(int Mode);\r
33  int    Vesa_Int_FindMode(tVideo_IOCtl_Mode *data);\r
34  int    Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data);\r
35 void    Vesa_int_HideCursor(void);\r
36 void    Vesa_int_ShowCursor(void);\r
37 void    Vesa_FlipCursor(void *Arg);\r
38 Uint16  VBE_int_GetWord(const tVT_Char *Char);\r
39 void    VBE_int_Text_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);\r
40 void    VBE_int_Text_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);\r
41 \r
42 // === GLOBALS ===\r
43 MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL);\r
44 tVFS_NodeType   gVesa_NodeType = {\r
45         .Write = Vesa_Write,\r
46         .IOCtl = Vesa_IOCtl\r
47         };\r
48 tDevFS_Driver   gVesa_DriverStruct = {\r
49         NULL, "Vesa",\r
50         {.Type = &gVesa_NodeType}\r
51         };\r
52 tMutex  glVesa_Lock;\r
53 tVM8086 *gpVesa_BiosState;\r
54  int    giVesaDriverId = -1;\r
55 // --- Video Modes ---\r
56  int    giVesaCurrentMode = 0;\r
57 tVesa_Mode      gVesa_BootMode = {0x03, 80*8, 25*16, 80*8*2, 12, FLAG_POPULATED, 80*25*2, 0xB8000};\r
58 tVesa_Mode      *gVesa_Modes;\r
59 tVesa_Mode      *gpVesaCurMode;\r
60  int    giVesaModeCount = 0;\r
61  int    gbVesaModesChecked;\r
62 // --- Framebuffer ---\r
63 char    *gpVesa_Framebuffer = (void*)VESA_DEFAULT_FRAMEBUFFER;\r
64  int    giVesaPageCount = 0;    //!< Framebuffer size in pages\r
65 // --- Cursor Control ---\r
66  int    giVesaCursorX = -1;\r
67  int    giVesaCursorY = -1;\r
68 #if BLINKING_CURSOR\r
69 tTimer  *gpVesaCursorTimer;\r
70 #endif\r
71  int    gbVesa_CursorVisible = 0;\r
72 // --- 2D Video Stream Handlers ---\r
73 tDrvUtil_Video_BufInfo  gVesa_BufInfo;\r
74 tDrvUtil_Video_2DHandlers       gVBE_Text2DFunctions = {\r
75         NULL,\r
76         VBE_int_Text_2D_Fill,\r
77         VBE_int_Text_2D_Blit\r
78 };\r
79 // --- Settings ---\r
80 bool    gbVesa_DisableBIOSCalls;        // Disables calls to the BIOS\r
81 // int  gbVesa_DisableFBCache;  // Disables the main-memory framebuffer cache\r
82 \r
83 // === CODE ===\r
84 int Vesa_Install(char **Arguments)\r
85 {\r
86          int    rv;\r
87 \r
88         for( int i = 0; Arguments && Arguments[i]; i ++ )\r
89         {\r
90                 if( strcmp(Arguments[i], "nobios") == 0 )\r
91                         gbVesa_DisableBIOSCalls = 1;\r
92                 //else if( strcmp(Arguments[i], "nocache") == 0 )\r
93                 //      gbVesa_DisableFBCache = 1;\r
94                 else {\r
95                         Log_Notice("VBE", "Unknown argument '%s'", Arguments[i]);\r
96                 }\r
97         }\r
98 \r
99         if( !gbVesa_DisableBIOSCalls )\r
100         {\r
101                 gpVesa_BiosState = VM8086_Init();\r
102                 \r
103                 if( (rv = VBE_int_GetModeList()) )\r
104                         return rv;\r
105         }\r
106                 \r
107         #if BLINKING_CURSOR\r
108         // Create blink timer\r
109         gpVesaCursorTimer = Time_AllocateTimer( Vesa_FlipCursor, NULL );\r
110         #endif\r
111 \r
112         // Install Device\r
113         giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct );\r
114         if(giVesaDriverId == -1)        return MODULE_ERR_MISC;\r
115 \r
116         return MODULE_ERR_OK;\r
117 }\r
118 \r
119 int VBE_int_GetModeList(void)\r
120 {\r
121         tVesa_CallInfo  *info;\r
122         tFarPtr infoPtr;\r
123         Uint16  *modes;\r
124         \r
125         // Allocate Info Block\r
126         info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs);\r
127         // Set Requested Version\r
128         memcpy(info->signature, "VBE2", 4);\r
129         // Set Registers\r
130         gpVesa_BiosState->AX = 0x4F00;\r
131         gpVesa_BiosState->ES = infoPtr.seg;     gpVesa_BiosState->DI = infoPtr.ofs;\r
132         // Call Interrupt\r
133         VM8086_Int(gpVesa_BiosState, 0x10);\r
134         if(gpVesa_BiosState->AX != 0x004F) {\r
135                 Log_Warning("VBE", "Vesa_Install - VESA/VBE Unsupported (AX = 0x%x)", gpVesa_BiosState->AX);\r
136                 return MODULE_ERR_NOTNEEDED;\r
137         }\r
138         \r
139         modes = (Uint16 *) VM8086_GetPointer(gpVesa_BiosState, info->VideoModes.seg, info->VideoModes.ofs);\r
140         LOG("Virtual addres of mode list from %04x:%04x is %p",\r
141                 info->VideoModes.seg, info->VideoModes.ofs, modes);\r
142 //      VM8086_Deallocate( gpVesa_BiosState, info );\r
143         \r
144         // Count Modes\r
145         for( giVesaModeCount = 0; modes[giVesaModeCount] != 0xFFFF; giVesaModeCount++ )\r
146                 ;\r
147         gVesa_Modes = (tVesa_Mode *)malloc( giVesaModeCount * sizeof(tVesa_Mode) );\r
148         \r
149         Log_Debug("VBE", "%i Modes", giVesaModeCount);\r
150 \r
151         // Insert Text Mode\r
152         \r
153         for( int i = 0; i < giVesaModeCount; i++ )\r
154         {\r
155                 gVesa_Modes[i].code = modes[i];\r
156         }\r
157         \r
158         return 0;\r
159 }\r
160 \r
161 int VBE_int_GetModeInfo(Uint16 Code, tFarPtr *BufPtr)\r
162 {\r
163         // Get Mode info\r
164         gpVesa_BiosState->AX = 0x4F01;\r
165         gpVesa_BiosState->CX = Code;\r
166         gpVesa_BiosState->ES = BufPtr->seg;\r
167         gpVesa_BiosState->DI = BufPtr->ofs;\r
168         VM8086_Int(gpVesa_BiosState, 0x10);\r
169 \r
170         if( gpVesa_BiosState->AX != 0x004F ) {\r
171                 Log_Error("VBE", "Getting info on mode 0x%x failed (AX=0x%x)",\r
172                         Code, gpVesa_BiosState->AX);\r
173                 return 1;\r
174         }\r
175         return 0;\r
176 }\r
177 \r
178 \r
179 void VBE_int_FillMode_Int(tVesa_Mode *Mode, const tVesa_CallModeInfo *vbeinfo)\r
180 {\r
181         #if 1\r
182         #define S_LOG(s, fld, fmt)      LOG(" ."#fld" = "fmt, (s).fld)\r
183         LOG("vbeinfo[0x%x] = {", Mode->code);\r
184         S_LOG(*vbeinfo, attributes, "0x%02x");\r
185         S_LOG(*vbeinfo, winA, "0x%02x");\r
186         S_LOG(*vbeinfo, winB, "0x%02x");\r
187         S_LOG(*vbeinfo, granularity, "0x%04x");\r
188         S_LOG(*vbeinfo, winsize, "0x%04x");\r
189         S_LOG(*vbeinfo, segmentA, "0x%04x");\r
190         S_LOG(*vbeinfo, segmentB, "0x%04x");\r
191         LOG(" .realFctPtr = %04x:%04x", vbeinfo->realFctPtr.seg, vbeinfo->realFctPtr.ofs);\r
192         S_LOG(*vbeinfo, pitch, "0x%04x");\r
193 \r
194         // -- Extended\r
195         S_LOG(*vbeinfo, Xres, "%i");\r
196         S_LOG(*vbeinfo, Yres, "%i");\r
197         S_LOG(*vbeinfo, Wchar, "%i");\r
198         S_LOG(*vbeinfo, Ychar, "%i");\r
199         S_LOG(*vbeinfo, planes, "%i");\r
200         S_LOG(*vbeinfo, bpp, "%i");\r
201         S_LOG(*vbeinfo, banks, "%i");\r
202         S_LOG(*vbeinfo, memory_model, "%i");\r
203         S_LOG(*vbeinfo, bank_size, "%i");\r
204         S_LOG(*vbeinfo, image_pages, "%i");\r
205         // -- VBE 1.2+\r
206         LOG(" Red   = %i bits at %i", vbeinfo->red_mask,   vbeinfo->red_position  );\r
207         LOG(" Green = %i bits at %i", vbeinfo->green_mask, vbeinfo->green_position);\r
208         LOG(" Blue  = %i bits at %i", vbeinfo->blue_mask,  vbeinfo->blue_position );\r
209         #if 0\r
210         Uint8   rsv_mask, rsv_position;\r
211         Uint8   directcolor_attributes;\r
212         #endif\r
213         // --- VBE 2.0+\r
214         S_LOG(*vbeinfo, physbase, "0x%08x");\r
215         S_LOG(*vbeinfo, offscreen_ptr, "0x%08x");\r
216         S_LOG(*vbeinfo, offscreen_size_kb, "0x%04x");\r
217         // --- VBE 3.0+\r
218         S_LOG(*vbeinfo, lfb_pitch, "0x%04x");\r
219         S_LOG(*vbeinfo, image_count_banked, "%i");\r
220         S_LOG(*vbeinfo, image_count_lfb, "%i");\r
221         LOG("}");\r
222         #endif\r
223 \r
224         Mode->flags = FLAG_POPULATED;\r
225         if( !(vbeinfo->attributes & 1) )\r
226         {\r
227                 #if DEBUG\r
228                 Log_Log("VBE", "0x%x - not supported", Mode->code);\r
229                 #endif\r
230                 Mode->width = 0;\r
231                 Mode->height = 0;\r
232                 Mode->bpp = 0;\r
233                 return ;\r
234         }\r
235 \r
236         // Parse Info\r
237         Mode->flags |= FLAG_VALID;\r
238         switch( vbeinfo->attributes & 0x90 )    // LFB, Graphics\r
239         {\r
240         case 0x00:      // Banked, Text\r
241         case 0x10:      // Banked, Graphics\r
242         case 0x80:      // Linear, Text (?)\r
243                 Mode->width = 0;\r
244                 Mode->height = 0;\r
245                 Mode->bpp = 0;\r
246                 return ;\r
247         case 0x90:\r
248                 Mode->flags |= FLAG_LFB;\r
249                 Mode->framebuffer = vbeinfo->physbase;\r
250                 Mode->fbSize = vbeinfo->Yres*vbeinfo->pitch;\r
251                 break;\r
252         }\r
253         \r
254         Mode->pitch = vbeinfo->pitch;\r
255         Mode->width = vbeinfo->Xres;\r
256         Mode->height = vbeinfo->Yres;\r
257         Mode->bpp = vbeinfo->bpp;\r
258         \r
259         #if DEBUG\r
260         Log_Log("VBE", "0x%x - %ix%ix%i (%x)",\r
261                 Mode->code, Mode->width, Mode->height, Mode->bpp, Mode->flags);\r
262         #endif\r
263 \r
264\r
265 \r
266 void VBE_int_SetBootMode(Uint16 ModeID, const void *ModeInfo)\r
267 {\r
268         gVesa_BootMode.code = ModeID;\r
269         VBE_int_FillMode_Int(&gVesa_BootMode, ModeInfo);\r
270 }\r
271 \r
272 void Vesa_int_FillModeList(void)\r
273 {\r
274         if( !gbVesaModesChecked && !gbVesa_DisableBIOSCalls )\r
275         {\r
276                 tVesa_CallModeInfo      *modeinfo;\r
277                 tFarPtr modeinfoPtr;\r
278                 \r
279                 modeinfo = VM8086_Allocate(gpVesa_BiosState, 256, &modeinfoPtr.seg, &modeinfoPtr.ofs);\r
280                 for( int i = 0; i < giVesaModeCount; i ++ )\r
281                 {\r
282                         if( VBE_int_GetModeInfo(gVesa_Modes[i].code, &modeinfoPtr) == 0 )\r
283                         {\r
284                                 VBE_int_FillMode_Int( &gVesa_Modes[i], modeinfo );\r
285                         }\r
286                 }       \r
287 //              VM8086_Deallocate( gpVesa_BiosState, modeinfo );\r
288                 \r
289                 gbVesaModesChecked = 1;\r
290         }\r
291 }\r
292 \r
293 /**\r
294  * \brief Write to the framebuffer\r
295  */\r
296 size_t Vesa_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)\r
297 {\r
298         // Framebuffer modes - just pass on\r
299         if( gpVesaCurMode->flags & FLAG_LFB )\r
300                 return DrvUtil_Video_WriteLFB(&gVesa_BufInfo, Offset, Length, Buffer);\r
301         \r
302         // EGA text mode translation\r
303         switch( gVesa_BufInfo.BufferFormat )\r
304         {\r
305         case VIDEO_BUFFMT_TEXT: {\r
306                  int    num = Length / sizeof(tVT_Char);\r
307                  int    ofs = Offset / sizeof(tVT_Char);\r
308                  int    i = 0;\r
309                 const tVT_Char  *chars = Buffer;\r
310                 Uint16  word;\r
311                 \r
312                 for( ; num--; i ++, ofs ++)\r
313                 {\r
314                         word = VBE_int_GetWord( &chars[i] );\r
315                         ((Uint16*)gVesa_BufInfo.Framebuffer)[ ofs ] = word;\r
316                 }\r
317                 \r
318                 return Length; }\r
319         case VIDEO_BUFFMT_2DSTREAM:\r
320                 return DrvUtil_Video_2DStream(NULL, Buffer, Length,\r
321                         &gVBE_Text2DFunctions, sizeof(gVBE_Text2DFunctions));\r
322         default:\r
323                 Log_Warning("VBE", "TODO: Alternate modes in EGA text mode");\r
324                 return 0;\r
325         }\r
326 \r
327 }\r
328 \r
329 const char *csaVESA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
330 /**\r
331  * \brief Handle messages to the device\r
332  */\r
333 int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
334 {\r
335          int    ret;\r
336         switch(ID)\r
337         {\r
338         BASE_IOCTLS(DRV_TYPE_VIDEO, "VBE", VERSION, csaVESA_IOCtls);\r
339 \r
340         case VIDEO_IOCTL_GETSETMODE:\r
341                 if( !Data )     return giVesaCurrentMode;\r
342                 return Vesa_Int_SetMode( *(int*)Data );\r
343         \r
344         case VIDEO_IOCTL_FINDMODE:\r
345                 return Vesa_Int_FindMode((tVideo_IOCtl_Mode*)Data);\r
346         case VIDEO_IOCTL_MODEINFO:\r
347                 return Vesa_Int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
348         \r
349         case VIDEO_IOCTL_SETBUFFORMAT:\r
350                 Vesa_int_HideCursor();\r
351                 ret = gVesa_BufInfo.BufferFormat;\r
352                 if(Data)        gVesa_BufInfo.BufferFormat = *(int*)Data;\r
353                 if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
354                         DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor );\r
355                 Vesa_int_ShowCursor();\r
356                 return ret;\r
357         \r
358         case VIDEO_IOCTL_SETCURSOR:     // Set cursor position\r
359                 Vesa_int_HideCursor();\r
360                 giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x;\r
361                 giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y;\r
362                 Vesa_int_ShowCursor();\r
363                 return 0;\r
364         \r
365         case VIDEO_IOCTL_SETCURSORBITMAP:\r
366                 DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data );\r
367                 return 0;\r
368         }\r
369         return 0;\r
370 }\r
371 \r
372 /**\r
373  * \brief Updates the video mode\r
374  */\r
375 int Vesa_Int_SetMode(int mode)\r
376 {\r
377         tVesa_Mode      *modeptr;\r
378         // Check for fast return\r
379         if(mode == giVesaCurrentMode)   return 1;\r
380         \r
381         // Special case: Boot mode\r
382         if( mode == -1 )\r
383                 modeptr = &gVesa_BootMode;\r
384         else if( 0 <= mode && mode < giVesaModeCount )\r
385                 modeptr = &gVesa_Modes[mode];\r
386         else\r
387                 return -1;\r
388         \r
389         Vesa_int_FillModeList();\r
390 \r
391         #if BLINKING_CURSOR\r
392         Time_RemoveTimer(gpVesaCursorTimer);\r
393         #endif\r
394         \r
395         Mutex_Acquire( &glVesa_Lock );\r
396         \r
397         if( gbVesa_DisableBIOSCalls )\r
398         {\r
399                 ASSERT(mode == -1);\r
400         }\r
401         else\r
402         {\r
403                 gpVesa_BiosState->AX = 0x4F02;\r
404                 gpVesa_BiosState->BX = modeptr->code;\r
405                 if(modeptr->flags & FLAG_LFB) {\r
406                         gpVesa_BiosState->BX |= 1 << 14;        // Use LFB\r
407                 }\r
408                 LOG("In : AX=%04x/BX=%04x", gpVesa_BiosState->AX, gpVesa_BiosState->BX);\r
409                 \r
410                 // Set Mode\r
411                 VM8086_Int(gpVesa_BiosState, 0x10);\r
412 \r
413                 LOG("Out: AX = %04x", gpVesa_BiosState->AX);\r
414         }\r
415         \r
416         // Map Framebuffer\r
417         if( gpVesaCurMode )\r
418         {\r
419                 if( gpVesaCurMode->framebuffer < 1024*1024 )\r
420                         ;\r
421                 else\r
422                         MM_UnmapHWPages((tVAddr)gpVesa_Framebuffer, giVesaPageCount);\r
423         }\r
424         giVesaPageCount = (modeptr->fbSize + 0xFFF) >> 12;\r
425         if( modeptr->framebuffer < 1024*1024 )\r
426                 gpVesa_Framebuffer = (void*)(KERNEL_BASE|modeptr->framebuffer);\r
427         else\r
428                 gpVesa_Framebuffer = (void*)MM_MapHWPages(modeptr->framebuffer, giVesaPageCount);\r
429         \r
430         Log_Log("VBE", "Setting mode to %i 0x%x (%ix%i %ibpp) %p[0x%x] maps %P",\r
431                 mode, modeptr->code,\r
432                 modeptr->width, modeptr->height,\r
433                 modeptr->bpp,\r
434                 gpVesa_Framebuffer, giVesaPageCount << 12, modeptr->framebuffer\r
435                 );\r
436         \r
437         // Record Mode Set\r
438         giVesaCurrentMode = mode;\r
439         gpVesaCurMode = modeptr;\r
440         \r
441         Mutex_Release( &glVesa_Lock );\r
442 \r
443         // TODO: Disableable backbuffer\r
444         gVesa_BufInfo.BackBuffer  = realloc(gVesa_BufInfo.BackBuffer,\r
445                 modeptr->height * modeptr->pitch);\r
446         gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer;\r
447         gVesa_BufInfo.Pitch = modeptr->pitch;\r
448         gVesa_BufInfo.Width = modeptr->width;\r
449         gVesa_BufInfo.Height = modeptr->height;\r
450         gVesa_BufInfo.Depth = modeptr->bpp;     \r
451 \r
452         return 1;\r
453 }\r
454 \r
455 int VBE_int_MatchModes(tVideo_IOCtl_Mode *ReqMode, tVesa_Mode *ThisMode)\r
456 {\r
457         LOG("Matching %ix%i %ibpp", ThisMode->width, ThisMode->height, ThisMode->bpp);\r
458         if(ThisMode->width == ReqMode->width && ThisMode->height == ReqMode->height)\r
459         {\r
460                 //if( (data->bpp == 32 || data->bpp == 24)\r
461                 // && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) )\r
462                 if( ReqMode->bpp == ThisMode->bpp )\r
463                 {\r
464                         LOG("Perfect!");\r
465                         return -1;\r
466                 }\r
467         }\r
468         \r
469         int tmp = ThisMode->width * ThisMode->height - ReqMode->width * ReqMode->height;\r
470         tmp = tmp < 0 ? -tmp : tmp;\r
471         unsigned int factor = (Uint64)tmp * 1000 / (ReqMode->width * ReqMode->height);\r
472         if( ThisMode->bpp > ReqMode->bpp )\r
473                 factor += ThisMode->bpp - ReqMode->bpp;\r
474         else\r
475                 factor += ReqMode->bpp - ThisMode->bpp;\r
476 \r
477         if( ReqMode->bpp == ThisMode->bpp )\r
478                 factor /= 2;\r
479         else\r
480         {\r
481                 if( ReqMode->bpp == 8 && ThisMode->bpp != 8 )   factor *= 4;\r
482                 if( ReqMode->bpp == 16 && ThisMode->bpp != 16 ) factor *= 4;\r
483                 \r
484                 if( (ReqMode->bpp == 32 || ReqMode->bpp == 24)\r
485                  && (ThisMode->bpp == 32 || ThisMode->bpp == 24) )\r
486                 {\r
487                         // NC\r
488                 }\r
489                 else {\r
490                         if( ReqMode->bpp < ThisMode->bpp )\r
491                                 factor *= ThisMode->bpp / ReqMode->bpp + 1;\r
492                         else\r
493                                 factor *= ReqMode->bpp / ThisMode->bpp + 1;\r
494                 }\r
495         }\r
496         \r
497         return factor;\r
498 }\r
499 \r
500 int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data)\r
501 {\r
502          int    best = -1;\r
503         \r
504         ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp);\r
505 \r
506         Vesa_int_FillModeList();\r
507 \r
508         int bestFactor = VBE_int_MatchModes(data, &gVesa_BootMode);     \r
509         tVesa_Mode *bestPtr = &gVesa_BootMode;\r
510 \r
511         for(int i = 0; bestFactor > 0 && i < giVesaModeCount; i ++)\r
512         {\r
513                 LOG("Mode %i (%ix%ix%i)", i, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp);\r
514         \r
515                 int factor = VBE_int_MatchModes(data, &gVesa_Modes[i]);\r
516                 \r
517                 LOG("factor = %i, bestFactor = %i", factor, bestFactor);\r
518                 \r
519                 if(factor < bestFactor)\r
520                 {\r
521                         bestFactor = factor;\r
522                         best = i;\r
523                         bestPtr = &gVesa_Modes[i];\r
524                 }\r
525         }\r
526         data->id = best;\r
527         data->width = bestPtr->width;\r
528         data->height = bestPtr->height;\r
529         data->bpp = bestPtr->bpp;\r
530         LEAVE('i', best);\r
531         return best;\r
532 }\r
533 \r
534 int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data)\r
535 {\r
536         tVesa_Mode      *modeptr;\r
537         if( data->id == -1 )\r
538                 modeptr = &gVesa_BootMode;\r
539         else if( 0 <= data->id && data->id < giVesaModeCount)\r
540                 modeptr = &gVesa_Modes[data->id];\r
541         else\r
542                 return 0;\r
543 \r
544         Vesa_int_FillModeList();\r
545 \r
546         data->width  = modeptr->width;\r
547         data->height = modeptr->height;\r
548         data->bpp    = modeptr->bpp;\r
549         return 1;\r
550 }\r
551 \r
552 void Vesa_int_HideCursor(void)\r
553 {\r
554         if( gpVesaCurMode->flags & FLAG_LFB )\r
555         {\r
556                 DrvUtil_Video_RemoveCursor( &gVesa_BufInfo );\r
557                 #if BLINKING_CURSOR\r
558                 if(gpVesaCursorTimer) {\r
559                         Time_RemoveTimer(gpVesaCursorTimer);\r
560                 }\r
561                 #endif\r
562         }\r
563 }\r
564 \r
565 void Vesa_int_ShowCursor(void)\r
566 {\r
567         if( gpVesaCurMode->flags & FLAG_LFB )\r
568         {\r
569                 gbVesa_CursorVisible = (giVesaCursorX >= 0);\r
570                 if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
571                 {\r
572                         DrvUtil_Video_DrawCursor(\r
573                                 &gVesa_BufInfo,\r
574                                 giVesaCursorX*giVT_CharWidth,\r
575                                 giVesaCursorY*giVT_CharHeight\r
576                                 );\r
577                         #if BLINKING_CURSOR\r
578                         Time_ScheduleTimer( gpVesaCursorTimer, VESA_CURSOR_PERIOD );\r
579                         #endif\r
580                 }\r
581                 else\r
582                         DrvUtil_Video_DrawCursor(\r
583                                 &gVesa_BufInfo,\r
584                                 giVesaCursorX,\r
585                                 giVesaCursorY\r
586                                 );\r
587         }\r
588 }\r
589 \r
590 /**\r
591  * \brief Swaps the text cursor on/off\r
592  */\r
593 void Vesa_FlipCursor(void *Arg)\r
594 {\r
595         if( gVesa_BufInfo.BufferFormat != VIDEO_BUFFMT_TEXT )\r
596                 return ;\r
597 \r
598         if( gpVesaCurMode->flags & FLAG_LFB )\r
599         {\r
600                 if( gbVesa_CursorVisible )\r
601                         DrvUtil_Video_RemoveCursor(&gVesa_BufInfo);\r
602                 else\r
603                         DrvUtil_Video_DrawCursor(&gVesa_BufInfo,\r
604                                 giVesaCursorX*giVT_CharWidth,\r
605                                 giVesaCursorY*giVT_CharHeight\r
606                                 );\r
607                 gbVesa_CursorVisible = !gbVesa_CursorVisible;\r
608                         \r
609                 #if BLINKING_CURSOR\r
610                 Time_ScheduleTimer( gpVesaCursorTimer, VESA_CURSOR_PERIOD );\r
611                 #endif\r
612         }\r
613 }\r
614 \r
615 // ---\r
616 // Helpers for text mode\r
617 // ---\r
618 \r
619 /**\r
620  * \fn Uint8 VGA_int_GetColourNibble(Uint16 col)\r
621  * \brief Converts a 12-bit colour into a VGA 4-bit colour\r
622  */\r
623 Uint8 VBE_int_GetColourNibble(Uint16 col)\r
624 {\r
625         Uint8   ret = 0;\r
626          int    bright = 0;\r
627         \r
628         col = col & 0xCCC;\r
629         col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30);\r
630         bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2;\r
631         \r
632         switch(col)\r
633         {\r
634         //      Black\r
635         case 0x00:      ret = 0x0;      break;\r
636         // Dark Grey\r
637         case 0x15:      ret = 0x8;      break;\r
638         // Blues\r
639         case 0x01:\r
640         case 0x02:      ret = 0x1;      break;\r
641         case 0x03:      ret = 0x9;      break;\r
642         // Green\r
643         case 0x04:\r
644         case 0x08:      ret = 0x2;      break;\r
645         case 0x0C:      ret = 0xA;      break;\r
646         // Reds\r
647         case 0x10:\r
648         case 0x20:      ret = 0x4;      break;\r
649         case 0x30:      ret = 0xC;      break;\r
650         // Light Grey\r
651         case 0x2A:      ret = 0x7;      break;\r
652         // White\r
653         case 0x3F:      ret = 0xF;      break;\r
654         \r
655         default:\r
656                 ret |= (col & 0x03 ? 1 : 0);\r
657                 ret |= (col & 0x0C ? 2 : 0);\r
658                 ret |= (col & 0x30 ? 4 : 0);\r
659                 ret |= (bright ? 8 : 0);\r
660                 break;\r
661         }\r
662         return ret;\r
663 }\r
664 \r
665 /**\r
666  * \brief Convers a character structure to a VGA character word\r
667  */\r
668 Uint16 VBE_int_GetWord(const tVT_Char *Char)\r
669 {\r
670         Uint16  ret;\r
671         Uint16  col;\r
672         \r
673         // Get Character\r
674         if(Char->Ch < 128)\r
675                 ret = Char->Ch;\r
676         else {\r
677                 switch(Char->Ch)\r
678                 {\r
679                 default:        ret = 0;        break;\r
680                 }\r
681         }\r
682         \r
683         col = VBE_int_GetColourNibble(Char->BGCol);\r
684         ret |= col << 12;\r
685         \r
686         col = VBE_int_GetColourNibble(Char->FGCol);\r
687         ret |= col << 8;\r
688         \r
689         return ret;\r
690 }\r
691 \r
692 void VBE_int_Text_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)\r
693 {\r
694         const int       charw = 8;\r
695         const int       charh = 16;\r
696         const int       tw = gpVesaCurMode->width / charw;\r
697         const int       th = gpVesaCurMode->height / charh;\r
698 \r
699         X /= charw;\r
700         W /= charw;\r
701         Y /= charh;\r
702         H /= charh;\r
703 \r
704         tVT_Char        ch;\r
705         ch.Ch = 0x20;\r
706         ch.BGCol  = (Colour & 0x0F0000) >> (16-8);\r
707         ch.BGCol |= (Colour & 0x000F00) >> (8-4);\r
708         ch.BGCol |= (Colour & 0x00000F);\r
709         ch.FGCol = 0;\r
710         Uint16 word = VBE_int_GetWord(&ch);\r
711 \r
712         Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word);\r
713 \r
714         if( X >= tw || Y >= th )        return ;\r
715         if( X + W > tw )        W = tw - X;\r
716         if( Y + H > th )        H = th - Y;\r
717 \r
718         Uint16  *buf = (Uint16*)gpVesa_Framebuffer + Y*tw + X;\r
719 \r
720         \r
721         while( H -- ) {\r
722                 for( int i = 0; i < W; i ++ )\r
723                         *buf++ = word;\r
724                 buf += tw - W;\r
725         }\r
726 }\r
727 \r
728 void VBE_int_Text_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)\r
729 {\r
730         const int       charw = 8;\r
731         const int       charh = 16;\r
732         const int       tw = gpVesaCurMode->width / charw;\r
733         const int       th = gpVesaCurMode->height / charh;\r
734 \r
735         DstX /= charw;\r
736         SrcX /= charw;\r
737         W    /= charw;\r
738 \r
739         DstY /= charh;\r
740         SrcY /= charh;\r
741         H    /= charh;\r
742 \r
743 //      Log("(%i,%i) from (%i,%i) %ix%i", DstX, DstY, SrcX, SrcY, W, H);\r
744 \r
745         if( SrcX >= tw || SrcY >= th )  return ;\r
746         if( SrcX + W > tw )     W = tw - SrcX;\r
747         if( SrcY + H > th )     H = th - SrcY;\r
748         if( DstX >= tw || DstY >= th )  return ;\r
749         if( DstX + W > tw )     W = tw - DstX;\r
750         if( DstY + H > th )     H = th - DstY;\r
751 \r
752 \r
753         Uint16  *src = (Uint16*)gpVesa_Framebuffer + SrcY*tw + SrcX;\r
754         Uint16  *dst = (Uint16*)gpVesa_Framebuffer + DstY*tw + DstX;\r
755 \r
756         if( src > dst )\r
757         {\r
758                 // Simple copy\r
759                 while( H-- ) {\r
760                         memcpy(dst, src, W*2);\r
761                         dst += tw;\r
762                         src += tw;\r
763                 }\r
764         }\r
765         else\r
766         {\r
767                 dst += H*tw;\r
768                 src += H*tw;\r
769                 while( H -- ) {\r
770                         dst -= tw-W;\r
771                         src -= tw-W;\r
772                         for( int i = W; i --; ) *--dst = *--src;\r
773                 }\r
774         }\r
775 }\r
776 \r

UCC git Repository :: git.ucc.asn.au