From d1714d8a27a603ce8ac4ba91e8c0c3d060d767ee Mon Sep 17 00:00:00 2001 From: John Hodge Date: Tue, 6 Apr 2010 23:09:12 +0800 Subject: [PATCH] Many changes, Mostly working on improving the BootConf script engine. - Added variable support and function returns - Working on VESA driver, still a little buggy - Misc changes in other places --- Kernel/Makefile.BuildNum | 2 +- Kernel/arch/x86/start.asm | 20 ++++ Kernel/drv/kb.c | 6 +- Kernel/drv/vterm.c | 1 + Kernel/heap.c | 16 +-- Kernel/include/acess.h | 1 + Kernel/system.c | 226 +++++++++++++++++++++++++----------- Makefile.cfg | 2 +- Modules/Display/VESA/main.c | 78 +++++++++---- 9 files changed, 253 insertions(+), 99 deletions(-) diff --git a/Kernel/Makefile.BuildNum b/Kernel/Makefile.BuildNum index f17258be..7bb7ce60 100644 --- a/Kernel/Makefile.BuildNum +++ b/Kernel/Makefile.BuildNum @@ -1 +1 @@ -BUILD_NUM = 1745 +BUILD_NUM = 1793 diff --git a/Kernel/arch/x86/start.asm b/Kernel/arch/x86/start.asm index b1884aef..d9a1e500 100644 --- a/Kernel/arch/x86/start.asm +++ b/Kernel/arch/x86/start.asm @@ -148,6 +148,26 @@ GetEIP: mov eax, [esp] ret +; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args) +; Call a function passing the array as arguments +[global CallWithArgArray] +CallWithArgArray: + push ebp + mov ebp, esp + mov ecx, [ebp+12] ; Get NArgs + mov edx, [ebp+16] + +.top: + mov eax, [edx+ecx*4-4] + push eax + loop .top + + mov eax, [ebp+8] + call eax + lea esp, [ebp] + pop ebp + ret + [extern Proc_Clone] [extern Threads_Exit] [global SpawnTask] diff --git a/Kernel/drv/kb.c b/Kernel/drv/kb.c index ba9eb72c..18f431ae 100644 --- a/Kernel/drv/kb.c +++ b/Kernel/drv/kb.c @@ -88,12 +88,10 @@ void KB_IRQHandler() scancode = inb(0x60); // Read from the keyboard's data buffer //Log_Debug("Keyboard", "scancode = %02x", scancode); - //Log("KB_IRQHandler: scancode = 0x%02x", scancode); - // Ignore ACKs if(scancode == 0xFA) { - // Oh man! This is anachic (I'm leaving it here to represent the - // mess that Acess once was) + // Oh man! This is anarchic (I'm leaving it here to represent + // the mess that Acess once was) //kb_lastChar = KB_ACK; return; } diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c index ff116f3e..69d85b3a 100644 --- a/Kernel/drv/vterm.c +++ b/Kernel/drv/vterm.c @@ -21,6 +21,7 @@ #define VT_SCROLLBACK 1 // 2 Screens of text #define DEFAULT_OUTPUT "VGA" //#define DEFAULT_OUTPUT "BochsGA" +//#define DEFAULT_OUTPUT "Vesa" #define DEFAULT_INPUT "PS2Keyboard" #define DEFAULT_WIDTH 80 #define DEFAULT_HEIGHT 25 diff --git a/Kernel/heap.c b/Kernel/heap.c index 61f7881e..3a0edb37 100644 --- a/Kernel/heap.c +++ b/Kernel/heap.c @@ -252,36 +252,37 @@ void free(void *Ptr) // Alignment Check if( (Uint)Ptr & (sizeof(Uint)-1) ) { - Warning("free - Passed a non-aligned address (%p)", Ptr); + Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr); return; } // Sanity check if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd) { - Warning("free - Passed a non-heap address (%p)\n", Ptr); + Log_Warning("Heap", "free - Passed a non-heap address (%p < %p < %p)\n", + gHeapStart, Ptr, gHeapEnd); return; } // Check memory block - Header head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); if(head->Magic == MAGIC_FREE) { - Warning("free - Passed a freed block (%p) by %p", head, __builtin_return_address(0)); + Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0)); return; } if(head->Magic != MAGIC_USED) { - Warning("free - Magic value is invalid (%p, 0x%x)\n", head, head->Magic); + Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)\n", head, head->Magic); return; } // Check memory block - Footer foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); if(foot->Head != head) { - Warning("free - Footer backlink is incorrect (%p, 0x%x)\n", head, foot->Head); + Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)\n", head, foot->Head); return; } if(foot->Magic != MAGIC_FOOT) { - Warning("free - Footer magic is invalid (%p, %p = 0x%x)\n", head, &foot->Magic, foot->Magic); + Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)\n", head, &foot->Magic, foot->Magic); return; } @@ -387,13 +388,14 @@ void *calloc(size_t num, size_t size) /** * \fn int IsHeap(void *Ptr) - * \brief Checks if an address is a heap address + * \brief Checks if an address is a heap pointer */ int IsHeap(void *Ptr) { tHeapHead *head; if((Uint)Ptr < (Uint)gHeapStart) return 0; if((Uint)Ptr > (Uint)gHeapEnd) return 0; + if((Uint)Ptr & (sizeof(Uint)-1)) return 0; head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE) diff --git a/Kernel/include/acess.h b/Kernel/include/acess.h index 7afdb5e1..963cb9cf 100644 --- a/Kernel/include/acess.h +++ b/Kernel/include/acess.h @@ -328,6 +328,7 @@ extern Uint8 ByteSum(void *Ptr, int Size); */ extern Uint rand(); +extern int CallWithArgArray(void *Function, int NArgs, Uint *Args); // --- Heap --- /** diff --git a/Kernel/system.c b/Kernel/system.c index 2097f9ba..f9139185 100644 --- a/Kernel/system.c +++ b/Kernel/system.c @@ -6,6 +6,9 @@ #define DEBUG 0 #include +#define N_VARIABLES 16 +#define N_MAX_ARGS BITS + // === TYPES === typedef struct { @@ -18,6 +21,15 @@ typedef struct int nLines; tConfigLine Lines[]; } tConfigFile; +typedef struct +{ + char *Name; // Name + int MinArgs; // Minimum number of arguments + int MaxArgs; // Maximum number of arguments + Uint IntArgs; // Bitmap of arguments that should be treated as integers + void *Func; // Function pointer + Uint OptDefaults[N_MAX_ARGS]; // Default values for optional arguments +} tConfigCommand; // === IMPORTS === extern int Modules_LoadBuiltins(); @@ -34,6 +46,22 @@ void System_ParseSetting(char *Arg); void System_ExecuteScript(); tConfigFile *System_Int_ParseFile(char *File); +// === CONSTANTS === +const tConfigCommand caConfigCommands[] = { + {"module", 1,2, 0, Module_LoadFile, {(Uint)"",0}}, // Load a module from a file + {"spawn", 1,1, 0, Proc_Spawn, {0}}, // Spawn a process + // --- VFS --- + {"mount", 3,4, 0, VFS_Mount, {(Uint)"",0}}, // Mount a device + {"symlink", 2,2, 0, VFS_Symlink, {0}}, // Create a Symbolic Link + {"mkdir", 1,1, 0, VFS_MkDir, {0}}, // Create a Directory + {"open", 1,2, 0, VFS_Open, {VFS_OPENFLAG_READ,0}}, // Open a file + {"close", 1,1, 0x1, VFS_Close, {0}}, // Close an open file + {"ioctl", 3,3, 0x3, VFS_IOCtl, {0}}, // Call an IOCtl + + {"", 0,0, 0, NULL, {0}} +}; +#define NUM_CONFIG_COMMANDS (sizeof(caConfigCommands)/sizeof(caConfigCommands[0])) + // === GLOBALS === char *gsConfigScript = "/Acess/Conf/BootConf.cfg"; @@ -193,8 +221,13 @@ void System_ExecuteScript() { int fp; int fLen = 0; - int i; + int i, j, k; + int val; + int result = 0; + int variables[N_VARIABLES]; + int bReplaced[N_MAX_ARGS]; char *fData; + char *jmpTarget; tConfigFile *file; tConfigLine *line; @@ -215,7 +248,6 @@ void System_ExecuteScript() VFS_Close(fp); - // Parse File file = System_Int_ParseFile(fData); @@ -225,88 +257,146 @@ void System_ExecuteScript() line = &file->Lines[i]; if( line->nParts == 0 ) continue; // Skip blank - // Mount Device - if( strcmp(line->Parts[0], "mount") == 0 ) - { - if( line->nParts != 4 ) { - Log_Warning("Config", "Configuration command 'mount' requires 3 arguments, %i given", - line->nParts-1); - continue; + if(line->Parts[0][0] == ':') continue; // Ignore labels + + // Prescan and eliminate variables + for( j = 1; j < line->nParts; j++ ) { + Log_Debug("Config", "Arg #%i is '%s'", j, line->Parts[j]); + bReplaced[j] = 0; + if( line->Parts[j][0] != '$' ) continue; + if( line->Parts[j][1] == '?' ) { + val = result; } - //Log_Log("Config", "Mount '%s' to '%s' (%s)", - // line->Parts[1], line->Parts[2], line->Parts[3]); - //! \todo Use an optional 4th argument for the options string - VFS_Mount(line->Parts[1], line->Parts[2], line->Parts[3], ""); - } - // Load a Module - else if(strcmp(line->Parts[0], "module") == 0) - { - if( line->nParts < 2 || line->nParts > 3 ) { - Log_Warning("CFG", - "Configuration command 'module' requires 1 or 2 arguments, %i given", - line->nParts-1); - continue; + else { + val = atoi( &line->Parts[j][1] ); + if( val < 0 || val > N_VARIABLES ) continue; + val = variables[ val ]; } - if( line->nParts == 3 ) - Module_LoadFile(line->Parts[1], line->Parts[2]); - else - Module_LoadFile(line->Parts[1], ""); + Log_Debug("Config", "Replaced arg %i ('%s') with 0x%x", j, line->Parts[j], val); + line->Parts[j] = malloc( BITS/8+2+1 ); + sprintf(line->Parts[j], "0x%x", val); + bReplaced[j] = 1; } - // Load a UDI Module - else if(strcmp(line->Parts[0], "udimod") == 0) + + for( j = 0; j < NUM_CONFIG_COMMANDS; j++ ) { - if( line->nParts != 2 ) { - Log_Warning("Config", "Configuration command 'udimod' requires 1 argument, %i given", - line->nParts-1); - continue; + Uint args[N_MAX_ARGS]; + if(strcmp(line->Parts[0], caConfigCommands[j].Name) != 0) continue; + + Log_Debug("Config", "Command '%s', %i args passed", line->Parts[0], line->nParts-1); + + if( line->nParts - 1 < caConfigCommands[j].MinArgs ) { + Log_Warning("Config", + "Configuration command '%s' requires at least %i arguments, %i given", + caConfigCommands[j].Name, caConfigCommands[j].MinArgs, line->nParts-1 + ); + break; } - Log_Log("Config", "Load UDI Module '%s'", line->Parts[1]); - Module_LoadFile(line->Parts[1], ""); - } - // Load a EDI Module - else if(strcmp(line->Parts[0], "edimod") == 0) - { - if( line->nParts != 2 ) { - Log_Warning("Config", "Configuration command 'edimod' requires 1 argument, %i given", - line->nParts-1); - continue; + + if( line->nParts - 1 > caConfigCommands[j].MaxArgs ) { + Log_Warning("Config", + "Configuration command '%s' takes at most %i arguments, %i given", + caConfigCommands[j].Name, caConfigCommands[j].MaxArgs, line->nParts-1 + ); + break; + } + + for( k = caConfigCommands[j].MaxArgs-1; k > line->nParts - 1; k-- ) { + args[k] = caConfigCommands[j].OptDefaults[k]; } - Log_Log("Config", "Load EDI Module '%s'", line->Parts[1]); - Module_LoadFile(line->Parts[1], ""); + + for( k = line->nParts-1; k--; ) + { + if( caConfigCommands[j].IntArgs & (1 << k) ) { + args[k] = atoi(line->Parts[k+1]); + } + else { + args[k] = (Uint)line->Parts[k+1]; + } + Log_Debug("Config", "args[%i] = 0x%x", k, args[k]); + } + result = CallWithArgArray(caConfigCommands[j].Func, caConfigCommands[j].MaxArgs, args); + Log_Debug("Config", "result = %i", result); + break; } - // Create a Symbolic Link - else if(strcmp(line->Parts[0], "symlink") == 0) + if( j < NUM_CONFIG_COMMANDS ) continue; + + // --- State and Variables --- + if(strcmp(line->Parts[0], "set") == 0) { - if( line->nParts != 3 ) { - Log_Warning("Config", "Configuration command 'symlink' requires 2 arguments, %i given", + int to, value; + if( line->nParts-1 != 2 ) { + Log_Warning("Config", "Configuration command 'set' requires 2 arguments, %i given", line->nParts-1); continue; } - Log_Log("Config", "Symlink '%s' pointing to '%s'", - line->Parts[1], line->Parts[2]); - VFS_Symlink(line->Parts[1], line->Parts[2]); + + to = atoi(line->Parts[1]); + value = atoi(line->Parts[2]); + + variables[to] = value; + result = value; } - // Create a Directory - else if(strcmp(line->Parts[0], "mkdir") == 0) + // if + else if(strcmp(line->Parts[0], "if") == 0) { - if( line->nParts != 2 ) { - Log_Warning("Config", "Configuration command 'mkdir' requires 1 argument, %i given", + if( line->nParts-1 != 4 ) { + Log_Warning("Config", "Configuration command 'if' requires 4 arguments, %i given", line->nParts-1); - continue; } - Log_Log("Config", "New Directory '%s'", line->Parts[1]); - VFS_MkDir(line->Parts[1]); + + result = atoi(line->Parts[1]); + val = atoi(line->Parts[3]); + + jmpTarget = line->Parts[4]; + + Log_Log("Config", "IF 0x%x %s 0x%x THEN GOTO %s", + result, line->Parts[2], val, jmpTarget); + + if( strcmp(line->Parts[2], "<" ) == 0 ) { + if( result < val ) goto jumpToLabel; + } + else if( strcmp(line->Parts[2], "<=") == 0 ) { + if( result <= val ) goto jumpToLabel; + } + else if( strcmp(line->Parts[2], ">" ) == 0 ) { + if (result > val ) goto jumpToLabel; + } + else if( strcmp(line->Parts[2], ">=") == 0 ) { + if( result >= val ) goto jumpToLabel; + } + else if( strcmp(line->Parts[2], "=") == 0 ) { + if( result == val ) goto jumpToLabel; + } + else if( strcmp(line->Parts[2], "!=") == 0 ) { + if( result != val ) goto jumpToLabel; + } + else { + Log_Warning("Config", "Unknown comparision '%s' in `if`", line->Parts[2]); + } + } - // Spawn a process - else if(strcmp(line->Parts[0], "spawn") == 0) - { - if( line->nParts != 2 ) { - Log_Warning("Config", "Configuration command 'spawn' requires 1 argument, %i given", + else if(strcmp(line->Parts[0], "goto") == 0) { + if( line->nParts-1 != 1 ) { + Log_Warning("Config", "Configuration command 'goto' requires 1 arguments, %i given", line->nParts-1); - continue; } - Log_Log("Config", "Starting '%s' as a new task", line->Parts[1]); - Proc_Spawn(line->Parts[1]); + jmpTarget = line->Parts[1]; + + jumpToLabel: + for( j = 0; j < file->nLines; j ++ ) + { + if(file->Lines[j].nParts == 0) + continue; + if(file->Lines[j].Parts[0][0] != ':') + continue; + if( strcmp(file->Lines[j].Parts[0]+1, jmpTarget) == 0) + break; + } + if( j == file->nLines ) + Log_Warning("Config", "Unable to find label '%s'", jmpTarget); + else + i = j; } else { Log_Warning("Config", "Unknown configuration command '%s' on line %i", @@ -319,6 +409,10 @@ void System_ExecuteScript() // Clean up after ourselves for( i = 0; i < file->nLines; i++ ) { if( file->Lines[i].nParts == 0 ) continue; // Skip blank + for( j = 0; j < file->Lines[i].nParts; j++ ) { + if(IsHeap(file->Lines[i].Parts[j])) + free(file->Lines[i].Parts[j]); + } free( file->Lines[i].Parts ); } free( file ); diff --git a/Makefile.cfg b/Makefile.cfg index 08cf64d4..51c4802d 100644 --- a/Makefile.cfg +++ b/Makefile.cfg @@ -27,7 +27,7 @@ FILESYSTEMS = DRIVERS = MODULES = Storage/ATA Storage/FDD MODULES += Network/NE2000 -#MODULES += Display/VESA +MODULES += Display/VESA #MODULES += Display/BochsGA MODULES += Filesystems/Ext2 MODULES += Filesystems/FAT diff --git a/Modules/Display/VESA/main.c b/Modules/Display/VESA/main.c index 216fcd1d..e960888f 100644 --- a/Modules/Display/VESA/main.c +++ b/Modules/Display/VESA/main.c @@ -2,7 +2,7 @@ * AcessOS 1 * Video BIOS Extensions (Vesa) Driver */ -#define DEBUG 0 +#define DEBUG 1 #define VERSION 0x100 #include @@ -28,7 +28,7 @@ Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); // === GLOBALS === MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL); tDevFS_Driver gVesa_DriverStruct = { - NULL, "VESA", + NULL, "Vesa", { .Read = Vesa_Read, .Write = Vesa_Write, @@ -79,8 +79,11 @@ int Vesa_Install(char **Arguments) // Insert Text Mode gVesa_Modes[0].width = 80; gVesa_Modes[0].height = 25; - gVesa_Modes[0].bpp = 4; + gVesa_Modes[0].bpp = 12; gVesa_Modes[0].code = 0x3; + gVesa_Modes[0].flags = VIDEO_FLAG_TEXT; + gVesa_Modes[0].fbSize = 80*25*2; + gVesa_Modes[0].framebuffer = 0xB8000; for( i = 1; i < giVesaModeCount; i++ ) { @@ -92,6 +95,8 @@ int Vesa_Install(char **Arguments) gpVesa_BiosState->DI = modeinfoPtr.ofs; VM8086_Int(gpVesa_BiosState, 0x10); + Log_Debug("Vesa", "gpVesa_BiosState->AX = 0x%04x", gpVesa_BiosState->AX); + // Parse Info gVesa_Modes[i].flags = 0; if ( (modeinfo->attributes & 0x90) == 0x90 ) @@ -109,7 +114,7 @@ int Vesa_Install(char **Arguments) gVesa_Modes[i].bpp = modeinfo->bpp; #if DEBUG - LogF(" Vesa_Install: 0x%x - %ix%ix%i\n", + Log_Log("Vesa", "0x%x - %ix%ix%i", gVesa_Modes[i].code, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp); #endif } @@ -137,13 +142,46 @@ Uint64 Vesa_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer) */ Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) { - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Bufffer); + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); if(Buffer == NULL) { LEAVE('i', 0); return 0; } + // Default Text mode + if( giVesaCurrentMode == 0 ) + { + Uint8 *fb = (Uint8 *)(KERNEL_BASE|0xB8000); + Uint32 *buf = Buffer; + if( Offset + Length > 25*80*8 ) { + Log_Warning("VESA", "Vesa_Write - Framebuffer Overflow"); + LEAVE('i', 0); + return 0; + } + + fb += 2*Offset; + for(; Length > 0; Length -= 8, fb += 2) + { + if( *buf < 0x80 ) + *fb = *buf & 0x7F; + else + *fb = 0x00; + buf ++; + + fb[1] = 0; + fb[1] |= (*buf & 0x888) == 0x888 ? 0x8 : 0; + fb[1] |= (*buf & 0x700) > 0x300 ? 0x4 : 0; + fb[1] |= (*buf & 0x070) > 0x030 ? 0x2 : 0; + fb[1] |= (*buf & 0x007) > 0x003 ? 0x1 : 0; + fb[1] |= (*buf & 0x888000) == 0x888000 ? 0x80 : 0; + fb[1] |= (*buf & 0x700000) > 0x300000 ? 0x40 : 0; + fb[1] |= (*buf & 0x070000) > 0x030000 ? 0x20 : 0; + fb[1] |= (*buf & 0x007000) > 0x003000 ? 0x10 : 0; + buf ++; + } + } + if( gVesa_Modes[giVesaCurrentMode].framebuffer == 0 ) { Log_Warning("VESA", "Vesa_Write - Non-LFB Modes not yet supported."); LEAVE('i', 0); @@ -206,7 +244,7 @@ int Vesa_Int_SetMode(int mode) gpVesa_BiosState->AX = 0x4F02; gpVesa_BiosState->BX = gVesa_Modes[mode].code; if(gVesa_Modes[mode].flags & FLAG_LFB) { - LogF(" Vesa_Int_SetMode: Using LFB\n"); + Log_Log("VESA", "Using LFB"); gpVesa_BiosState->BX |= 0x4000; // Bit 14 - Use LFB } @@ -218,8 +256,8 @@ int Vesa_Int_SetMode(int mode) giVesaPageCount = (gVesa_Modes[mode].fbSize + 0xFFF) >> 12; gVesaFramebuffer = (void*)MM_MapHWPages(gVesa_Modes[mode].framebuffer, giVesaPageCount); - LogF(" Vesa_Int_SetMode: Fb (Phys) = 0x%x\n", gVesa_Modes[mode].framebuffer); - LogF(" Vesa_Int_SetMode: Fb (Virt) = 0x%x\n", gVesaFramebuffer); + LogF("Vesa", "Framebuffer (Phys) = 0x%x", gVesa_Modes[mode].framebuffer); + LogF("Vesa", "Framebuffer (Virt) = 0x%x", gVesaFramebuffer); // Record Mode Set giVesaCurrentMode = mode; @@ -234,22 +272,23 @@ int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data) int i; int best = -1, bestFactor = 1000; int factor, tmp; - #if DEBUG - LogF("Vesa_Int_FindMode: (data={width:%i,height:%i,bpp:%i})\n", data->width, data->height, data->bpp); - #endif + + ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp); + + if(data->flags & VIDEO_FLAG_TEXT) { + LEAVE('i', 0); + return 0; + } + for(i=0;i= 2 - LogF("Mode %i (%ix%ix%i), ", i, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp); - #endif + LOG("Mode %i (%ix%ix%i)", i, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp); if(gVesa_Modes[i].width == data->width && gVesa_Modes[i].height == data->height && gVesa_Modes[i].bpp == data->bpp) { - #if DEBUG >= 2 - LogF("Perfect!\n"); - #endif + LOG("Perfect!"); best = i; break; } @@ -259,9 +298,7 @@ int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data) tmp = tmp < 0 ? -tmp : tmp; factor = tmp * 100 / (data->width * data->height * data->bpp); - #if DEBUG >= 2 - LogF("factor = %i\n", factor); - #endif + LOG("factor = %i", factor); if(factor < bestFactor) { @@ -273,6 +310,7 @@ int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data) data->width = gVesa_Modes[best].width; data->height = gVesa_Modes[best].height; data->bpp = gVesa_Modes[best].bpp; + LEAVE('i', best); return best; } -- 2.20.1