DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include <stdlib.h> 00021 #include <stdarg.h> 00022 #include <string.h> 00023 #include <stdio.h> 00024 #include <errno.h> 00025 00026 #if defined(WIN32) 00027 #include <conio.h> 00028 #include <SDL.h> 00029 #endif 00030 00031 #include "dosbox.h" 00032 #include "logging.h" 00033 #include "support.h" 00034 #include "control.h" 00035 #include "regs.h" 00036 #include "menu.h" 00037 #include "debug.h" 00038 #include "debug_inc.h" 00039 00040 #include <stdexcept> 00041 #include <exception> 00042 00043 using namespace std; 00044 00045 bool log_int21 = false; 00046 bool log_fileio = false; 00047 00048 static bool has_LOG_Init = false; 00049 static bool has_LOG_EarlyInit = false; 00050 static bool do_LOG_stderr = false; 00051 00052 bool logBuffSuppressConsole = false; 00053 bool logBuffSuppressConsoleNeedUpdate = false; 00054 00055 _LogGroup loggrp[LOG_MAX]={{"",LOG_NORMAL},{0,LOG_NORMAL}}; 00056 FILE* debuglog = NULL; 00057 00058 #if C_DEBUG 00059 static bool logBuffHasDiscarded = false; 00060 00061 #include <curses.h> 00062 00063 #include <list> 00064 #include <string> 00065 using namespace std; 00066 00067 std::string DBGBlock::windowlist_by_name(void) { 00068 std::string comp; 00069 unsigned int i; 00070 00071 for (i=0;i < DBGBlock::WINI_MAX_INDEX;i++) { 00072 if (i != 0) comp += ","; 00073 comp += dbg_win_names[i]; 00074 } 00075 00076 return comp; 00077 } 00078 00079 const unsigned int dbg_def_win_height[DBGBlock::WINI_MAX_INDEX] = { 00080 5, /* WINI_REG */ 00081 9, /* WINI_DATA */ 00082 12, /* WINI_CODE */ 00083 5, /* WINI_VAR */ 00084 6 /* WINI_OUT */ 00085 }; 00086 00087 const char *dbg_def_win_titles[DBGBlock::WINI_MAX_INDEX] = { 00088 "Register Overview", /* WINI_REG */ 00089 "Data Overview", /* WINI_DATA */ 00090 "Code Overview", /* WINI_CODE */ 00091 "Variable Overview", /* WINI_VAR */ 00092 "Output" /* WINI_OUT */ 00093 }; 00094 00095 const char *dbg_win_names[DBGBlock::WINI_MAX_INDEX] = { 00096 "REG", 00097 "DATA", 00098 "CODE", 00099 "VAR", 00100 "OUT" 00101 }; 00102 00103 #define MAX_LOG_BUFFER 4000 00104 static list<string> logBuff; 00105 static list<string>::iterator logBuffPos = logBuff.end(); 00106 00107 extern int old_cursor_state; 00108 00109 const char *DBGBlock::get_winname(int idx) { 00110 if (idx >= 0 && idx < DBGBlock::WINI_MAX_INDEX) 00111 return dbg_win_names[idx]; 00112 00113 return NULL; 00114 } 00115 00116 const char *DBGBlock::get_wintitle(int idx) { 00117 if (idx >= 0 && idx < DBGBlock::WINI_MAX_INDEX) 00118 return dbg.win_title[idx].c_str(); 00119 00120 return NULL; 00121 } 00122 00123 int DBGBlock::name_to_win(const char *name) { 00124 for (unsigned int i=0;i < DBGBlock::WINI_MAX_INDEX;i++) { 00125 if (!strcasecmp(name,dbg_win_names[i])) 00126 return (int)i; 00127 } 00128 00129 return -1; 00130 } 00131 00132 void DBGBlock::next_window(void) { 00133 int order = win_find_order((int)active_win); 00134 00135 if (order < 0) order = 0; 00136 00137 order = win_next_by_order(order); 00138 if (order >= 0) active_win = win_order[order]; 00139 } 00140 00141 void DBGBlock::swap_order(int o1,int o2) { 00142 if (o1 != o2) 00143 std::swap(win_order[o1],win_order[o2]); 00144 } 00145 00146 WINDOW* &DBGBlock::get_win_ref(int idx) { 00147 switch (idx) { 00148 case WINI_REG: return win_reg; 00149 case WINI_DATA: return win_data; 00150 case WINI_CODE: return win_code; 00151 case WINI_VAR: return win_var; 00152 case WINI_OUT: return win_out; 00153 } 00154 00155 throw domain_error("get_win_ref"); 00156 } 00157 00158 WINDOW* DBGBlock::get_win(int idx) { 00159 return get_win_ref(idx); 00160 } 00161 00162 WINDOW *DBGBlock::get_active_win(void) { 00163 return get_win((int)active_win); 00164 } 00165 00166 int DBGBlock::win_find_order(int wnd) { 00167 for (unsigned int i=0;i < DBGBlock::WINI_MAX_INDEX;i++) { 00168 if (dbg.win_order[i] == wnd) 00169 return (int)i; 00170 } 00171 00172 return -1; 00173 } 00174 00175 int DBGBlock::win_prev_by_order(int order) { 00176 int limit = DBGBlock::WINI_MAX_INDEX; 00177 00178 do { 00179 if (--order < 0) 00180 order = DBGBlock::WINI_MAX_INDEX - 1; 00181 00182 if (--limit <= 0) 00183 break; 00184 } while (get_win(win_order[order]) == NULL); 00185 00186 return order; 00187 } 00188 00189 int DBGBlock::win_next_by_order(int order) { 00190 int limit = DBGBlock::WINI_MAX_INDEX; 00191 00192 do { 00193 if (++order >= DBGBlock::WINI_MAX_INDEX) 00194 order = 0; 00195 00196 if (--limit <= 0) 00197 break; 00198 } while (get_win(win_order[order]) == NULL); 00199 00200 return order; 00201 } 00202 00203 void DBGUI_DrawBlankOutputLine(int y) { 00204 if (dbg.win_out == NULL) return; 00205 00206 wattrset(dbg.win_out,COLOR_PAIR(PAIR_GREEN_BLACK)); 00207 if (logBuffHasDiscarded) 00208 mvwprintw(dbg.win_out, y, 0, "<LOG BUFFER ENDS, OLDER CONTENT DISCARDED BEYOND THIS POINT>"); 00209 else 00210 mvwprintw(dbg.win_out, y, 0, "<END OF LOG>"); 00211 // wclrtoeol(dbg.win_out); 00212 } 00213 00214 void DBGUI_DrawDebugOutputLine(int y,std::string line) { 00215 if (dbg.win_out == NULL) return; 00216 00217 int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx); 00218 bool ellipsisEnd = false; 00219 00220 (void)maxy;//UNUSED 00221 00222 /* cut the line short if it's too long for the terminal window */ 00223 if (line.length() > (size_t)maxx) { 00224 line = line.substr(0,(size_t)(maxx - 3)); 00225 ellipsisEnd = true; 00226 } 00227 00228 /* Const cast is needed for pdcurses which has no const char in mvwprintw (bug maybe) */ 00229 wattrset(dbg.win_out,0); 00230 mvwprintw(dbg.win_out, y, 0, const_cast<char*>(line.c_str())); 00231 00232 if (ellipsisEnd) { 00233 wattrset(dbg.win_out,COLOR_PAIR(PAIR_GREEN_BLACK)); 00234 mvwprintw(dbg.win_out, y, maxx-3, "..."); 00235 } 00236 00237 // wclrtoeol(dbg.win_out); 00238 } 00239 00240 void DEBUG_LimitTopPos(void) { 00241 if (dbg.win_out != NULL) { 00242 int w,h; 00243 00244 getmaxyx(dbg.win_out,h,w); 00245 00246 auto i = logBuff.begin(); 00247 for (int y=0;i != logBuff.end() && y < (h-1);y++) { 00248 if (i == logBuffPos) { 00249 i++; 00250 logBuffPos = i; 00251 } 00252 else { 00253 i++; 00254 } 00255 } 00256 } 00257 } 00258 00259 void DEBUG_RefreshPage(char scroll) { 00260 if (dbg.win_out == NULL) return; 00261 00262 while (scroll < 0 && logBuffPos!=logBuff.begin()) { 00263 --logBuffPos; 00264 scroll++; 00265 } 00266 while (scroll > 0 && logBuffPos!=logBuff.end()) { 00267 ++logBuffPos; 00268 scroll--; 00269 } 00270 00271 DEBUG_LimitTopPos(); 00272 00273 list<string>::iterator i = logBuffPos; 00274 int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx); 00275 int rem_lines = maxy; 00276 if(rem_lines <= 0) return; 00277 00278 wclear(dbg.win_out); 00279 00280 /* NTS: Often, i == logBuff.end() unless the user scrolled up in the interface. 00281 * 00282 * So the original intent of this code is that the iterator is one entry PAST 00283 * the line to begin displaying at the bottom just as end() is a sentinel 00284 * value just past the last element of the list. So if i == logBuff.begin() 00285 * then there's nothing to display. 00286 * 00287 * Note that the iterator defines what is drawn on the bottom-most line, 00288 * and iteration is done backwards until we've drawn the line at i == logBuff.begin() 00289 * or until we've drawn something at line 0 of the subwin. 00290 * 00291 * rem_lines starts out as the number of lines in the subwin. */ 00292 if (i != logBuff.begin()) { 00293 --i; 00294 00295 wattrset(dbg.win_out,0); 00296 while (rem_lines > 0) { 00297 rem_lines--; 00298 00299 DBGUI_DrawDebugOutputLine(rem_lines,*i); 00300 00301 if (i != logBuff.begin()) 00302 --i; 00303 else 00304 break; 00305 } 00306 00307 /* show that the lines above are beyond the end of the log */ 00308 while (rem_lines > 0) { 00309 rem_lines--; 00310 DBGUI_DrawBlankOutputLine(rem_lines); 00311 } 00312 } 00313 00314 wrefresh(dbg.win_out); 00315 } 00316 00317 void DEBUG_ScrollHomeOutput(void) { 00318 logBuffPos = logBuff.begin(); 00319 DEBUG_RefreshPage(0); 00320 } 00321 00322 void DEBUG_ScrollToEndOutput(void) { 00323 logBuffPos = logBuff.end(); 00324 DEBUG_RefreshPage(0); 00325 } 00326 00327 static void Draw_RegisterLayout(void) { 00328 if (dbg.win_main == NULL) 00329 return; 00330 00331 mvwaddstr(dbg.win_reg,0,0,"EAX="); 00332 mvwaddstr(dbg.win_reg,1,0,"EBX="); 00333 mvwaddstr(dbg.win_reg,2,0,"ECX="); 00334 mvwaddstr(dbg.win_reg,3,0,"EDX="); 00335 00336 mvwaddstr(dbg.win_reg,0,14,"ESI="); 00337 mvwaddstr(dbg.win_reg,1,14,"EDI="); 00338 mvwaddstr(dbg.win_reg,2,14,"EBP="); 00339 mvwaddstr(dbg.win_reg,3,14,"ESP="); 00340 00341 mvwaddstr(dbg.win_reg,0,28,"DS="); 00342 mvwaddstr(dbg.win_reg,0,38,"ES="); 00343 mvwaddstr(dbg.win_reg,0,48,"FS="); 00344 mvwaddstr(dbg.win_reg,0,58,"GS="); 00345 mvwaddstr(dbg.win_reg,0,68,"SS="); 00346 00347 mvwaddstr(dbg.win_reg,1,28,"CS="); 00348 mvwaddstr(dbg.win_reg,1,38,"EIP="); 00349 00350 mvwaddstr(dbg.win_reg,2,75,"CPL"); 00351 mvwaddstr(dbg.win_reg,2,68,"IOPL"); 00352 00353 mvwaddstr(dbg.win_reg,1,52,"C Z S O A P D I T "); 00354 } 00355 00356 static void DrawSubWinBox(WINDOW *wnd,const char *title) { 00357 bool active = false; 00358 int x,y; 00359 int w,h; 00360 00361 if (wnd == NULL) return; 00362 00363 const WINDOW *active_win = dbg.get_active_win(); 00364 00365 if (wnd == active_win) 00366 active = true; 00367 00368 getbegyx(wnd,y,x); 00369 getmaxyx(wnd,h,w); 00370 00371 (void)h;//UNUSED 00372 00373 if (has_colors()) { 00374 if (active) 00375 attrset(COLOR_PAIR(PAIR_BLACK_BLUE)); 00376 else 00377 attrset(COLOR_PAIR(PAIR_WHITE_BLUE)); 00378 } 00379 00380 mvhline(y-1,x,ACS_HLINE,w); 00381 if (title != NULL) mvaddstr(y-1,x+4,title); 00382 } 00383 00384 void DrawBars(void) { 00385 if (dbg.win_main == NULL) 00386 return; 00387 00388 for (unsigned int wnd=0;wnd < DBGBlock::WINI_MAX_INDEX;wnd++) { 00389 WINDOW* &ref = dbg.get_win_ref((int)wnd); 00390 00391 if (ref != NULL) DrawSubWinBox(ref,dbg.get_wintitle((int)wnd)); 00392 } 00393 00394 attrset(0); 00395 } 00396 00397 static void DestroySubWindows(void) { 00398 for (unsigned int wnd=0;wnd < DBGBlock::WINI_MAX_INDEX;wnd++) { 00399 WINDOW* &ref = dbg.get_win_ref((int)wnd); 00400 00401 if (ref != NULL) { 00402 if (ref) delwin(ref); 00403 ref = NULL; 00404 } 00405 } 00406 } 00407 00408 void DEBUG_GUI_DestroySubWindows(void) { 00409 DestroySubWindows(); 00410 } 00411 00412 static void MakeSubWindows(void) { 00413 /* The Std output win should go at the bottom */ 00414 /* Make all the subwindows */ 00415 int win_main_maxy, win_main_maxx; getmaxyx(dbg.win_main,win_main_maxy,win_main_maxx); 00416 unsigned int yheight[DBGBlock::WINI_MAX_INDEX]; 00417 unsigned int yofs[DBGBlock::WINI_MAX_INDEX]; 00418 int win_limit = win_main_maxy - 1; 00419 int expand_wndi = -1; 00420 int outy=0,height; 00421 00422 /* main window needs to clear itself */ 00423 werase(dbg.win_main); 00424 00425 /* arrange windows */ 00426 for (unsigned int wndi=0;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) { 00427 unsigned int wnd = dbg.win_order[wndi]; 00428 00429 /* window must have been freed, must have height greater than 1 (more than just titlebar) 00430 * must be visible and there must be room for the window on the screen, where the last 00431 * row is reserved for the input bar */ 00432 if (dbg.win_height[wnd] > 1 && 00433 dbg.win_vis[wnd] && (outy+1) < win_limit) { 00434 outy++; // header 00435 height=(int)dbg.win_height[wnd] - 1; 00436 if ((outy+height) > win_limit) height = win_limit - outy; 00437 assert(height > 0); 00438 yofs[wndi]=(unsigned int)outy; 00439 yheight[wndi]=(unsigned int)height; 00440 outy+=height; 00441 00442 if (wnd == DBGBlock::WINI_OUT) 00443 expand_wndi = (int)wndi; 00444 } 00445 else { 00446 yofs[wndi]=0; 00447 yheight[wndi]=0; 00448 } 00449 } 00450 00451 /* last window expands if output not there */ 00452 if (expand_wndi < 0) { 00453 for (int wndi=DBGBlock::WINI_MAX_INDEX-1;wndi >= 0;wndi--) { 00454 if (yheight[wndi] != 0) { 00455 expand_wndi = wndi; 00456 break; 00457 } 00458 } 00459 } 00460 00461 /* we give one window the power to expand to help fill the screen */ 00462 if (outy < win_limit) { 00463 int expand_by = win_limit - outy; 00464 00465 if (expand_wndi >= 0 && expand_wndi < DBGBlock::WINI_MAX_INDEX) { 00466 unsigned int wndi = (unsigned int)expand_wndi; 00467 00468 /* add height to the window */ 00469 yheight[wndi] += (unsigned int)expand_by; 00470 outy += (int)expand_by; 00471 wndi++; 00472 00473 /* move the others down */ 00474 for (;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) 00475 yofs[wndi] += (unsigned int)expand_by; 00476 } 00477 } 00478 00479 for (unsigned int wndi=0;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) { 00480 if (yheight[wndi] != 0) { 00481 unsigned int wnd = dbg.win_order[wndi]; 00482 WINDOW* &ref = dbg.get_win_ref((int)wnd); 00483 assert(ref == NULL); 00484 ref = subwin(dbg.win_main,(int)yheight[wndi],win_main_maxx,(int)yofs[wndi],0); 00485 } 00486 } 00487 00488 if (outy < win_main_maxy) { 00489 // no header 00490 height = 1; 00491 outy = win_main_maxy - height; 00492 dbg.win_inp=subwin(dbg.win_main,height,win_main_maxx,outy,0); 00493 outy += height; 00494 } 00495 00496 DrawBars(); 00497 Draw_RegisterLayout(); 00498 refresh(); 00499 } 00500 00501 void DEBUG_GUI_Rebuild(void) { 00502 DestroySubWindows(); 00503 MakeSubWindows(); 00504 DEBUG_RefreshPage(0); 00505 } 00506 00507 void DEBUG_GUI_OnResize(void) { 00508 DEBUG_GUI_Rebuild(); 00509 } 00510 00511 static void MakePairs(void) { 00512 init_pair(PAIR_BLACK_BLUE, COLOR_BLACK, COLOR_CYAN); 00513 init_pair(PAIR_BYELLOW_BLACK, COLOR_YELLOW /*| FOREGROUND_INTENSITY */, COLOR_BLACK); 00514 init_pair(PAIR_GREEN_BLACK, COLOR_GREEN /*| FOREGROUND_INTENSITY */, COLOR_BLACK); 00515 init_pair(PAIR_BLACK_GREY, COLOR_BLACK /*| FOREGROUND_INTENSITY */, COLOR_WHITE); 00516 init_pair(PAIR_GREY_RED, COLOR_WHITE/*| FOREGROUND_INTENSITY */, COLOR_RED); 00517 init_pair(PAIR_BLACK_GREEN, COLOR_BLACK, COLOR_GREEN); 00518 init_pair(PAIR_WHITE_BLUE, COLOR_WHITE/* | FOREGROUND_INTENSITY*/, COLOR_BLUE); 00519 } 00520 00521 void DBGUI_NextWindow(void) { 00522 dbg.next_window(); 00523 DrawBars(); 00524 DEBUG_DrawScreen(); 00525 } 00526 00527 void DBGUI_NextWindowIfActiveHidden(void) { 00528 if (dbg.get_active_win() == NULL) 00529 DBGUI_NextWindow(); 00530 } 00531 00532 bool DEBUG_IsDebuggerConsoleVisible(void) { 00533 return (dbg.win_main != NULL); 00534 } 00535 00536 void DEBUG_FlushInput(void) { 00537 if (dbg.win_main != NULL) { 00538 while (getch() >= 0); /* remember nodelay() is called to make getch() non-blocking */ 00539 } 00540 } 00541 00542 void DBGUI_StartUp(void) { 00543 mainMenu.get_item("show_console").check(true).refresh_item(mainMenu); 00544 00545 LOG(LOG_MISC,LOG_DEBUG)("DEBUG GUI startup"); 00546 /* Start the main window */ 00547 dbg.win_main=initscr(); 00548 00549 #ifdef WIN32 00550 /* Tell Windows 10 we DONT want a thin tall console window that fills the screen top to bottom. 00551 It's a nuisance especially when the user attempts to move the windwo up to the top to see 00552 the status, only for Windows to auto-maximize. 30 lines is enough, thanks. */ 00553 { 00554 if (dbg.win_main) { 00555 int win_main_maxy, win_main_maxx; getmaxyx(dbg.win_main, win_main_maxy, win_main_maxx); 00556 if (win_main_maxx > 100) win_main_maxy = 100; 00557 if (win_main_maxy > 40) win_main_maxy = 40; 00558 resize_term(win_main_maxy, win_main_maxx); 00559 } 00560 } 00561 #endif 00562 00563 cbreak(); /* take input chars one at a time, no wait for \n */ 00564 noecho(); /* don't echo input */ 00565 scrollok(stdscr,false); 00566 nodelay(dbg.win_main,true); 00567 keypad(dbg.win_main,true); 00568 #ifndef WIN32 00569 touchwin(dbg.win_main); 00570 #endif 00571 old_cursor_state = curs_set(0); 00572 start_color(); 00573 cycle_count=0; 00574 MakePairs(); 00575 MakeSubWindows(); 00576 00577 /* make sure the output window is synced up */ 00578 logBuffPos = logBuff.end(); 00579 DEBUG_RefreshPage(0); 00580 } 00581 00582 #endif 00583 00584 int debugPageCounter = 0; 00585 int debugPageStopAt = 0; 00586 00587 bool DEBUG_IsPagingOutput(void) { 00588 return debugPageStopAt > 0; 00589 } 00590 00591 void DEBUG_DrawInput(void); 00592 00593 void DEBUG_BeginPagedContent(void) { 00594 #if C_DEBUG 00595 int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx); 00596 00597 debugPageCounter = 0; 00598 debugPageStopAt = maxy; 00599 #endif 00600 } 00601 00602 void DEBUG_EndPagedContent(void) { 00603 #if C_DEBUG 00604 debugPageCounter = 0; 00605 debugPageStopAt = 0; 00606 DEBUG_DrawInput(); 00607 #endif 00608 } 00609 00610 extern bool gfx_in_mapper; 00611 00612 bool in_debug_showmsg = false; 00613 00614 bool IsDebuggerActive(void); 00615 00616 void DEBUG_ShowMsg(char const* format,...) { 00617 bool stderrlog = false; 00618 char buf[512]; 00619 va_list msg; 00620 size_t len; 00621 00622 in_debug_showmsg = true; 00623 00624 // in case of runaway error from the CPU core, user responsiveness can be helpful 00625 CPU_CycleLeft += CPU_Cycles; 00626 CPU_Cycles = 0; 00627 00628 if (!gfx_in_mapper && !in_debug_showmsg) { 00629 void GFX_Events(); 00630 GFX_Events(); 00631 } 00632 00633 va_start(msg,format); 00634 len = (size_t)vsnprintf(buf,sizeof(buf)-2u,format,msg); /* <- NTS: Did you know sprintf/vsnprintf returns number of chars written? */ 00635 va_end(msg); 00636 00637 /* remove newlines if present */ 00638 while (len > 0 && buf[len-1] == '\n') buf[--len] = 0; 00639 00640 #if C_DEBUG 00641 if (dbg.win_out != NULL) 00642 stderrlog = false; 00643 else 00644 stderrlog = true; 00645 #else 00646 if (do_LOG_stderr || debuglog == NULL) 00647 stderrlog = true; 00648 #endif 00649 00650 if (debuglog != NULL) { 00651 fprintf(debuglog,"%s\n",buf); 00652 fflush(debuglog); 00653 } 00654 if (stderrlog) { 00655 #if C_EMSCRIPTEN 00656 /* Emscripten routes stderr to the browser console.error() function, and 00657 * stdout to a console window below ours on the browser page. We want the 00658 * user to see our blather, so print to stdout */ 00659 fprintf(stdout,"LOG: %s\n",buf); 00660 fflush(stdout); 00661 #else 00662 fprintf(stderr,"LOG: %s\n",buf); 00663 fflush(stderr); 00664 #endif 00665 } 00666 00667 #if C_DEBUG 00668 if (logBuffPos!=logBuff.end()) { 00669 logBuffPos=logBuff.end(); 00670 DEBUG_RefreshPage(0); 00671 } 00672 logBuff.push_back(buf); 00673 if (logBuff.size() > MAX_LOG_BUFFER) { 00674 logBuffHasDiscarded = true; 00675 if (logBuffPos == logBuff.begin()) ++logBuffPos; /* keep the iterator valid */ 00676 logBuff.pop_front(); 00677 } 00678 00679 logBuffPos = logBuff.end(); 00680 00681 if (dbg.win_out != NULL) { 00682 if (logBuffSuppressConsole) { 00683 logBuffSuppressConsoleNeedUpdate = true; 00684 } 00685 else { 00686 int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx); 00687 00688 scrollok(dbg.win_out,TRUE); 00689 scroll(dbg.win_out); 00690 scrollok(dbg.win_out,FALSE); 00691 00692 DBGUI_DrawDebugOutputLine(maxy-1,buf); 00693 00694 wrefresh(dbg.win_out); 00695 } 00696 } 00697 00698 if (IsDebuggerActive() && debugPageStopAt > 0) { 00699 if (++debugPageCounter >= debugPageStopAt) { 00700 debugPageCounter = 0; 00701 DEBUG_RefreshPage(0); 00702 DEBUG_DrawInput(); 00703 00704 /* pause, wait for input */ 00705 do { 00706 #if defined(WIN32) 00707 int key; 00708 00709 if (kbhit()) 00710 key = getch(); 00711 else 00712 key = -1; 00713 #else 00714 int key = getch(); 00715 #endif 00716 if (key > 0) { 00717 if (key == ' ' || key == 0x0A) { 00718 /* continue */ 00719 break; 00720 } 00721 else if (key == 0x27/*ESC*/ || key == 0x7F/*DEL*/ || key == 0x08/*BKSP*/ || 00722 key == 'q' || key == 'Q') { 00723 /* user wants to stop paging */ 00724 debugPageStopAt = 0; 00725 break; 00726 } 00727 } 00728 00729 #if defined(WIN32) 00730 /* help inspire confidence in users and in Windows by keeping the event loop going. 00731 This is to prevent Windows from graying out the main window during this loop 00732 as "application not responding" */ 00733 SDL_Event ev; 00734 00735 if (SDL_PollEvent(&ev)) { 00736 /* TODO */ 00737 } 00738 #endif 00739 } while (1); 00740 } 00741 } 00742 #endif 00743 00744 in_debug_showmsg = false; 00745 } 00746 00747 /* callback function when DOSBox-X exits */ 00748 void LOG::Exit() { 00749 if (debuglog != NULL) { 00750 fprintf(debuglog,"--END OF LOG--\n"); 00751 fclose(debuglog); 00752 debuglog = NULL; 00753 } 00754 } 00755 00756 void Null_Init(Section *sec); 00757 00758 void LOG::operator() (char const* format, ...){ 00759 const char *s_severity = ""; 00760 char buf[512]; 00761 va_list msg; 00762 00763 switch (d_severity) { 00764 case LOG_DEBUG: s_severity = " DEBUG"; break; 00765 case LOG_NORMAL:s_severity = " "; break; 00766 case LOG_WARN: s_severity = " WARN "; break; 00767 case LOG_ERROR: s_severity = " ERROR"; break; 00768 case LOG_FATAL: s_severity = " FATAL"; break; 00769 default: break; 00770 } 00771 00772 va_start(msg,format); 00773 vsnprintf(buf,sizeof(buf)-1,format,msg); 00774 va_end(msg); 00775 00776 if (d_type>=LOG_MAX) return; 00777 if (d_severity < loggrp[d_type].min_severity) return; 00778 DEBUG_ShowMsg("%10u%s %s:%s\n",static_cast<Bit32u>(cycle_count),s_severity,loggrp[d_type].front,buf); 00779 } 00780 00781 void LOG::ParseEnableSetting(_LogGroup &group,const char *setting) { 00782 if (!strcmp(setting,"true") || !strcmp(setting,"1") || !strcmp(setting,"normal")) 00783 group.min_severity = LOG_NORMAL; /* original code's handling is equivalent to our "normal" setting */ 00784 else if (!strcmp(setting,"false") || !strcmp(setting,"0") || !strcmp(setting,"")) 00785 group.min_severity = LOG_ERROR; /* original code's handling is equivalent to our "error" setting */ 00786 else if (!strcmp(setting,"debug")) 00787 group.min_severity = LOG_DEBUG; 00788 else if (!strcmp(setting,"warn")) 00789 group.min_severity = LOG_WARN; 00790 else if (!strcmp(setting,"error")) 00791 group.min_severity = LOG_ERROR; 00792 else if (!strcmp(setting,"fatal")) 00793 group.min_severity = LOG_FATAL; 00794 else if (!strcmp(setting,"never")) 00795 group.min_severity = LOG_NEVER; 00796 else 00797 group.min_severity = LOG_NORMAL; 00798 } 00799 00800 void LOG::Init() { 00801 char buf[64]; 00802 00803 assert(control != NULL); 00804 00805 /* do not init twice */ 00806 if (has_LOG_Init) return; 00807 has_LOG_Init = true; 00808 00809 /* announce */ 00810 LOG_MSG("Logging init: beginning logging proper. This is the end of the early init logging"); 00811 00812 /* get the [log] section */ 00813 const Section_prop *sect = static_cast<Section_prop *>(control->GetSection("log")); 00814 assert(sect != NULL); 00815 00816 /* do we write to a logfile, or not? */ 00817 const char *blah = sect->Get_string("logfile"); 00818 if (blah != NULL && blah[0] != 0) { 00819 if ((debuglog=fopen(blah,"wt+")) != NULL) { 00820 LOG_MSG("Logging: opened logfile '%s' successfully. All further logging will go to that file.",blah); 00821 setbuf(debuglog,NULL); 00822 } 00823 else { 00824 LOG_MSG("Logging: failed to open logfile '%s'. All further logging will be discarded. Error: %s",blah,strerror(errno)); 00825 } 00826 } 00827 else { 00828 LOG_MSG("Logging: No logfile was given. All further logging will be discarded."); 00829 debuglog=0; 00830 } 00831 00832 log_int21 = sect->Get_bool("int21") || control->opt_logint21; 00833 log_fileio = sect->Get_bool("fileio") || control->opt_logfileio; 00834 00835 /* end of early init logging */ 00836 do_LOG_stderr = false; 00837 00838 /* read settings for each log category, unless the -debug option was given, 00839 * in which case everything is set to debug level */ 00840 for (Bitu i = LOG_ALL + 1;i < LOG_MAX;i++) { //Skip LOG_ALL, it is always enabled 00841 safe_strncpy(buf,loggrp[i].front,sizeof(buf)); 00842 lowcase(buf); 00843 00844 if (control->opt_debug) 00845 ParseEnableSetting(/*&*/loggrp[i],"debug"); 00846 else 00847 ParseEnableSetting(/*&*/loggrp[i],sect->Get_string(buf)); 00848 } 00849 00850 LOG(LOG_MISC,LOG_DEBUG)("Logging init complete"); 00851 } 00852 00853 void LOG::EarlyInit(void) { 00854 assert(control != NULL); 00855 00856 /* do not init twice */ 00857 if (has_LOG_EarlyInit) return; 00858 has_LOG_EarlyInit = true; 00859 00860 /* Setup logging groups */ 00861 loggrp[LOG_ALL].front="ALL"; 00862 loggrp[LOG_VGA].front="VGA"; 00863 loggrp[LOG_VGAGFX].front="VGAGFX"; 00864 loggrp[LOG_VGAMISC].front="VGAMISC"; 00865 loggrp[LOG_INT10].front="INT10"; 00866 loggrp[LOG_SB].front="SBLASTER"; 00867 loggrp[LOG_DMACONTROL].front="DMA_CONTROL"; 00868 00869 loggrp[LOG_FPU].front="FPU"; 00870 loggrp[LOG_CPU].front="CPU"; 00871 loggrp[LOG_PAGING].front="PAGING"; 00872 00873 loggrp[LOG_FCB].front="FCB"; 00874 loggrp[LOG_FILES].front="FILES"; 00875 loggrp[LOG_IOCTL].front="IOCTL"; 00876 loggrp[LOG_EXEC].front="EXEC"; 00877 loggrp[LOG_DOSMISC].front="DOSMISC"; 00878 00879 loggrp[LOG_PIT].front="PIT"; 00880 loggrp[LOG_KEYBOARD].front="KEYBOARD"; 00881 loggrp[LOG_PIC].front="PIC"; 00882 00883 loggrp[LOG_MOUSE].front="MOUSE"; 00884 loggrp[LOG_BIOS].front="BIOS"; 00885 loggrp[LOG_GUI].front="GUI"; 00886 loggrp[LOG_MISC].front="MISC"; 00887 00888 loggrp[LOG_IO].front="IO"; 00889 loggrp[LOG_PCI].front="PCI"; 00890 00891 loggrp[LOG_VOODOO].front="SST"; 00892 00893 do_LOG_stderr = control->opt_earlydebug; 00894 00895 for (Bitu i=1;i<LOG_MAX;i++) { 00896 if (control->opt_earlydebug) 00897 ParseEnableSetting(/*&*/loggrp[i],"debug"); 00898 else 00899 ParseEnableSetting(/*&*/loggrp[i],"warn"); 00900 } 00901 00902 LOG_MSG("Early LOG Init complete"); 00903 } 00904 00905 void LOG::SetupConfigSection(void) { 00906 const char *log_values[] = { 00907 /* compatibility with existing dosbox.conf files */ 00908 "true", "false", 00909 00910 /* log levels */ 00911 "debug", 00912 "normal", 00913 "warn", 00914 "error", 00915 "fatal", 00916 "never", /* <- this means NEVER EVER log anything */ 00917 00918 0}; 00919 00920 /* Register the log section */ 00921 Section_prop * sect=control->AddSection_prop("log",Null_Init); 00922 Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,""); 00923 Pstring->Set_help("file where the log messages will be saved to"); 00924 char buf[64]; 00925 for (Bitu i = LOG_ALL + 1;i < LOG_MAX;i++) { 00926 safe_strncpy(buf,loggrp[i].front, sizeof(buf)); 00927 lowcase(buf); 00928 00929 Pstring = sect->Add_string(buf,Property::Changeable::Always,"false"); 00930 Pstring->Set_values(log_values); 00931 Pstring->Set_help("Enable/Disable logging of this type."); 00932 } 00933 00934 Prop_bool* Pbool; 00935 00936 Pbool = sect->Add_bool("int21",Property::Changeable::Always,false); 00937 Pbool->Set_help("Log all INT 21h calls"); 00938 00939 Pbool = sect->Add_bool("fileio",Property::Changeable::Always,false); 00940 Pbool->Set_help("Log file I/O through INT 21h"); 00941 } 00942