]> icculus.org git repositories - taylor/freespace2.git/blob - src/debugconsole/console.cpp
added copyright header
[taylor/freespace2.git] / src / debugconsole / console.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/DebugConsole/Console.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Routines for managing the debug console window.
16  *
17  * $Log$
18  * Revision 1.3  2002/06/09 04:41:16  relnev
19  * added copyright header
20  *
21  * Revision 1.2  2002/05/07 03:16:43  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:08  root
25  * Initial import.
26  *
27  * 
28  * 4     6/04/99 10:35a Dave
29  * 
30  * 3     10/13/98 9:28a Dave
31  * Started neatening up freespace.h. Many variables renamed and
32  * reorganized. Added AlphaColors.[h,cpp]
33  * 
34  * 2     10/07/98 10:52a Dave
35  * Initial checkin.
36  * 
37  * 1     10/07/98 10:48a Dave
38  * 
39  * 17    2/05/98 11:43a Allender
40  * enhcanced network statistic collection.  Made callback in debug console
41  * to do networking if player is in the console
42  * 
43  * 16    1/22/98 6:42p John
44  * Move game_flush out of debug console into freespace.     Made object
45  * pair code throw out some weapons that turn.   Added stats for how many
46  * object pair are actually checked.
47  * 
48  * 15    1/16/98 11:56a Allender
49  * ability to scroll back in debug console, and call game_flush() when
50  * leaving
51  * 
52  * 14    1/10/98 1:14p John
53  * Added explanation to debug console commands
54  * 
55  * 13    12/21/97 4:39p John
56  * fixed bug in name compare
57  * 
58  * 12    12/21/97 4:33p John
59  * Made debug console functions a class that registers itself
60  * automatically, so you don't need to add the function to
61  * debugfunctions.cpp.  
62  * 
63  * 11    9/13/97 9:31a Lawrance
64  * if playing a demo, clear key filter then reset it once done with debug
65  * console
66  * 
67  * 10    9/09/97 3:39p Sandeep
68  * warning level 4 bugs
69  * 
70  * 9     6/13/97 3:50p John
71  * sped up debug console printf
72  * 
73  * 8     6/13/97 3:27p John
74  * made debug console pretty
75  * 
76  * 7     6/09/97 9:24a John
77  * Changed the way fonts are set.
78  * 
79  * 6     5/29/97 3:09p John
80  * Took out debug menu.  
81  * Made software scaler draw larger bitmaps.
82  * Optimized Direct3D some.
83  * 
84  * 5     5/13/97 4:07p John
85  * made numbers also be marked as strings.
86  * 
87  * 4     4/28/97 5:24p John
88  * made so : and \ don't get parsed out in debug console.
89  * 
90  * 3     4/24/97 11:49a John
91  * added new lighting commands to console.
92  * 
93  * 2     4/24/97 10:36a John
94  * moved the debug console stuff into it's own lib... made it compile with
95  * Fred.
96  * 
97  * 1     4/24/97 10:16a John
98  * Initial rev
99  *
100  * $NoKeywords: $
101  */
102
103 #include <stdlib.h>
104 #include <stdio.h>
105 #include <stdarg.h>
106 #include <setjmp.h>
107 #include <string.h>
108
109 #include "pstypes.h"
110 #include "freespace.h"
111 #include "font.h"
112 #include "timer.h"
113 #include "2d.h"
114 #include "key.h"
115 #include "alphacolors.h"
116 #include "osapi.h"
117
118 #define MAX_COMMANDS 300
119
120 static int Num_debug_commands = 0;
121 static debug_command *Debug_command[MAX_COMMANDS];
122
123
124 debug_command::debug_command(char *_name, char *_help, void (*_func)() )
125 {
126         int i;
127
128         if ( Num_debug_commands >= MAX_COMMANDS )       {
129                 Int3();                 // Too many debug console commands!! Increase MAX_COMMANDS!!
130                 return;
131         }
132
133         for (i=0; i<Num_debug_commands; i++ )   {
134                 int ret  = stricmp( Debug_command[i]->name, _name );
135
136                 if ( ret == 0)  {
137                         Int3();         // This debug console command already exists!!!! 
138                         return;
139                 } else if ( ret > 0 )   {
140                         break;          // Insert it here
141
142                 } else if ( ret < 0 )   {
143                         // do nothing
144                 }
145         }
146
147         if ( i < Num_debug_commands )   {
148                 // Insert it at element i
149                 int j;
150                 for (j=Num_debug_commands; j>i; j-- )   {
151                         Debug_command[j] = Debug_command[j-1];
152                 }
153                 Debug_command[i] = this;                
154                 Num_debug_commands++;
155         } else {
156                 Debug_command[Num_debug_commands] = this;               
157                 Num_debug_commands++;
158         }
159
160         name = _name;
161         help = _help;
162         func = _func;
163 }
164
165 // some global variables
166 int Dc_command; // If this is set, then process the command
167 int Dc_help;            // If this is set, then print out the help text in the form, "usage: ... \nLong description\n" );
168 int Dc_status;          // If this is set, then print out the current status of the command.
169 char *Dc_arg;           // The (lowercased) string value of the argument retrieved from dc_arg
170 char *Dc_arg_org;       // Dc_arg before it got converted to lowercase
171 uint Dc_arg_type;       // The type of dc_arg.
172 char *Dc_command_line;          // The rest of the command line, from the end of the last processed arg on.
173 int Dc_arg_int;         // If Dc_arg_type & ARG_INT is set, then this is the value
174 float Dc_arg_float;     // If Dc_arg_type & ARG_FLOAT is set, then this is the value
175
176 int scroll_times = 0;           // incremented each time display scrolls
177
178 int debug_inited = 0;
179
180 #define DROWS 25
181 #define DCOLS 80
182
183 int debug_x=0, debug_y=0;
184 char debug_text[DROWS][DCOLS];
185
186
187 static char command_line[1024];
188 static int command_line_pos = 0;
189 #define DEBUG_HISTORY 16
190 static char oldcommand_line[DEBUG_HISTORY][1024];
191 int last_oldcommand=-1;
192 int command_scroll = 0;
193
194 ///=========================== SCANNER =======================
195 typedef enum {
196         LETTER, QUOTE, SPECIAL, EOF_CODE, DIGIT,
197 } CHAR_CODE;
198
199 typedef enum {
200         NO_TOKEN, IDENTIFIER, NUMBER, STRING, 
201 } TOKEN_CODE;
202
203
204 #define MAX_TOKEN_STRING_LENGTH 128
205
206 char                    scanner_ch;
207 TOKEN_CODE      scanner_token;
208
209 char scanner_token_string[MAX_TOKEN_STRING_LENGTH];
210 char scanner_word_string[MAX_TOKEN_STRING_LENGTH];
211 char * scanner_bufferp = "";
212 char * scanner_tokenp = scanner_token_string;
213
214 CHAR_CODE scanner_char_table[256];
215
216 #define scanner_char_code(x) scanner_char_table[x]
217
218 void scanner_get_char()
219 {
220         if ( *scanner_bufferp == '\0' ) {
221                 scanner_ch = 0;
222                 return;
223         }
224         scanner_ch = *scanner_bufferp++;
225 }
226
227 void scanner_init()
228 {
229         int ch;
230         for (ch=0; ch<256; ++ch) scanner_char_table[ch] = SPECIAL;
231         for (ch='0'; ch<='9'; ++ch) scanner_char_table[ch] = DIGIT;
232         for (ch='A'; ch<='Z'; ++ch) scanner_char_table[ch] = LETTER;
233         for (ch='a'; ch<='z'; ++ch) scanner_char_table[ch] = LETTER;
234
235         scanner_char_table['.'] = DIGIT;
236         scanner_char_table['-'] = DIGIT;
237         scanner_char_table['+'] = DIGIT;
238         
239         scanner_char_table['_'] = LETTER;
240         scanner_char_table[34] = QUOTE;
241         scanner_char_table[0] = EOF_CODE;
242
243
244         scanner_char_table[':'] = LETTER;
245         scanner_char_table['\\'] = LETTER;
246
247         scanner_ch = 0;
248 }
249
250
251 void scanner_skip_blanks()
252 {
253         while( (scanner_ch ==' ') || (scanner_ch =='\t') ) 
254                 scanner_get_char();
255 }
256
257
258 void scanner_downshift_word()
259 {
260         int offset = 'a' - 'A';
261         char * tp;
262
263         strcpy( scanner_word_string, scanner_token_string );
264         
265         tp = scanner_word_string;
266         do {
267                 *tp = (char)((*tp>='A') && (*tp <='Z') ? *tp + offset : *tp) ;
268                 tp++;
269         } while (*tp != '\0' );
270 }
271
272
273 void scanner_get_word()
274 {
275         while( (scanner_char_code(scanner_ch)==LETTER) || (scanner_char_code(scanner_ch)==DIGIT)  )     {
276                 *scanner_tokenp++ = scanner_ch;
277                 scanner_get_char();
278         }
279         *scanner_tokenp = '\0';
280
281         scanner_token = IDENTIFIER;
282 }
283
284
285 void scanner_get_string()
286 {
287         *scanner_tokenp++ = 34;
288         scanner_get_char();
289
290         while(scanner_ch != 34 )        {
291                 *scanner_tokenp++ = scanner_ch;
292                 scanner_get_char();
293         }
294         scanner_get_char();
295         *scanner_tokenp++ = 34;
296         *scanner_tokenp = '\0';
297         scanner_token = STRING;
298 }
299
300
301
302 void scanner_get_token()
303 {
304         scanner_skip_blanks();
305         scanner_tokenp = scanner_token_string;
306         *scanner_tokenp = 0;
307
308
309         switch( scanner_char_code(scanner_ch) ) {
310         case QUOTE:             scanner_get_string();   break;
311         case EOF_CODE:  scanner_token = NO_TOKEN;       break;
312         case DIGIT:
313         case LETTER:    scanner_get_word(); break;
314         default:                        
315                 *scanner_tokenp++ = scanner_ch;
316                 *scanner_tokenp = '\0';
317                 scanner_get_char();
318                 scanner_token = IDENTIFIER;
319                 break;
320         }
321
322         scanner_downshift_word();
323 }
324
325 void scanner_start_command( char * s )
326 {
327         scanner_bufferp = s;
328         scanner_get_char();
329 }
330
331
332 int Dc_debug_on = 0;
333 jmp_buf dc_bad_arg;
334
335 void dc_get_arg(uint type)
336 {
337         scanner_get_token();
338
339         Dc_command_line = scanner_bufferp;      
340         Dc_arg_org = scanner_token_string;
341         Dc_arg = scanner_word_string;
342
343         if (Dc_debug_on)        {
344                 dc_printf( "next arg is '%s', was originally '%s'\n", Dc_arg, Dc_arg_org );
345                 dc_printf( "Rest of the command line is '%s'\n", Dc_command_line );
346         }
347         
348         if ( scanner_token == NO_TOKEN )        {
349                 Dc_arg_type = ARG_NONE;
350         } else if ( scanner_token == IDENTIFIER )       {
351                 Dc_arg_type = ARG_STRING;
352         } else if ( scanner_token == STRING )   {
353                 Dc_arg_type = ARG_QUOTE;
354         } else {
355                 Dc_arg_type = ARG_STRING;
356         }
357
358         if ( Dc_arg_type & ARG_STRING ) {
359                 int i, num_digits, len;
360
361                 len = strlen(Dc_arg);
362                 num_digits = 0;
363
364                 for (i=0; i<len; i++)
365                         if ( scanner_char_table[Dc_arg[i]] == DIGIT ) num_digits++;
366
367                 if ( num_digits==len )  {
368                         Dc_arg_type |= ARG_FLOAT;
369                         Dc_arg_float = (float)atof(Dc_arg);
370                         if ( !strchr( Dc_arg, '.' ))    {
371                                 Dc_arg_type |= ARG_INT;
372                                 Dc_arg_int = atoi(Dc_arg);
373                         }
374                 } else {
375                         if ( (Dc_arg[0] == '0') && (Dc_arg[1] == 'x') ) {
376                                 char *p;
377                                 int n;
378                                 n = strtol(Dc_arg,&p,0);
379                                 if ( *p == 0 )  {
380                                         Dc_arg_type |= ARG_INT|ARG_HEX;
381                                         Dc_arg_int = n;
382                                 }
383                         } 
384                 }
385
386                 if (Dc_debug_on)        {
387                         if ( Dc_arg_type & ARG_FLOAT )
388                                 dc_printf( "Found float number! %f\n", Dc_arg_float );
389
390                         if ( Dc_arg_type & ARG_INT )
391                                 dc_printf( "Found int number! %d\n", Dc_arg_int );
392
393                         if ( Dc_arg_type & ARG_HEX )
394                                 dc_printf( "Found hex number! 0x%x\n", Dc_arg_int );
395                 }
396
397                 if ( !stricmp( Dc_arg, "on" ))
398                         Dc_arg_type |= ARG_TRUE;
399                 if ( !stricmp( Dc_arg, "true" ))
400                         Dc_arg_type |= ARG_TRUE;
401                 if ( !stricmp( Dc_arg, "off" ))
402                         Dc_arg_type |= ARG_FALSE;
403                 if ( !stricmp( Dc_arg, "false" ))
404                         Dc_arg_type |= ARG_FALSE;
405
406                 if ( !stricmp( Dc_arg, "+" ))
407                         Dc_arg_type |= ARG_PLUS;
408
409                 if ( !stricmp( Dc_arg, "-" ))
410                         Dc_arg_type |= ARG_MINUS;
411
412                 if ( !stricmp( Dc_arg, "," ))
413                         Dc_arg_type |= ARG_COMMA;
414         }
415
416         if ( Dc_arg_type & ARG_INT)     {
417                 if ( Dc_arg_int )       
418                         Dc_arg_type |= ARG_TRUE;
419                 else
420                         Dc_arg_type |= ARG_FALSE;
421         }
422
423         if ( !(Dc_arg_type&type) )      {
424                 if ( (Dc_arg_type & ARG_NONE) && !(type & ARG_NONE))    
425                         dc_printf( "Error: Not enough parameters.\n" );
426                 else
427                         dc_printf( "Error: '%s' invalid type\n", Dc_arg );
428                 longjmp(dc_bad_arg,1);
429         }
430
431 }
432
433 void debug_help();
434
435 void debug_do_command(char * command)
436 {
437
438         int i;
439         int mode = 0;
440
441         if ( strlen(command) < 1 ) return;
442         
443         Dc_debug_on = 0;
444         Dc_command_line = command;
445         scanner_start_command(command);
446
447         if (setjmp(dc_bad_arg) )        {
448                 return;
449         }
450         
451         dc_get_arg( ARG_ANY );
452
453         if ( !strcmp( Dc_arg, "debug" ) )       {
454                 Dc_debug_on = 1;
455                 dc_printf( "Command line: '%s'\n", Dc_command_line );
456                 dc_get_arg( ARG_ANY );
457         }
458
459         if ( !strcmp( Dc_arg, "?" ) )   {
460                 mode = 1;
461                 dc_get_arg( ARG_ANY );
462
463                 if ( Dc_arg_type&ARG_NONE )     {
464                         debug_help();           
465                         return;
466                 }
467         }
468
469         if ( !strcmp( Dc_arg, "help" ) || !strcmp( Dc_arg, "man" ) )    {
470                 mode = 2;
471                 dc_get_arg( ARG_ANY );
472                 if ( Dc_arg_type&ARG_NONE )     {
473                         debug_help();           
474                         return;
475                 }
476         }
477
478         if ( strstr( Dc_command_line, "?" ) )   {
479                 mode = 2;
480         }
481
482         if ( !(Dc_arg_type&ARG_STRING) )        {
483                 dc_printf( "Invalid keyword '%s'\n", Dc_arg );
484                 return;
485         }
486
487
488         if (Dc_debug_on)        {
489                 dc_printf( "Searching for command '%s'\n", Dc_arg );
490         }
491
492         for (i=0; i<Num_debug_commands; i++ )   {
493                 if ( !stricmp( Debug_command[i]->name, Dc_arg ))        {
494                 
495                         if (mode==0)    {
496                                 if (Dc_debug_on)        
497                                         dc_printf( "Calling function '%s'\n", Dc_arg );
498                                 Dc_command = 1;
499                                 Dc_help = 0;
500                                 Dc_status = 1;
501                         } else if (mode==1) {
502                                 if (Dc_debug_on)        
503                                         dc_printf( "Checking status for '%s'\n", Dc_arg );
504                                 Dc_command = 0;
505                                 Dc_help = 0;
506                                 Dc_status = 1;
507                         } else {
508                                 if (Dc_debug_on)        
509                                         dc_printf( "Doing help for '%s'\n", Dc_arg );
510                                 Dc_command = 0;
511                                 Dc_help = 1;
512                                 Dc_status = 0;
513                         }
514
515                         (*Debug_command[i]->func)();
516
517                         if (mode==0)    {
518                                 dc_get_arg(ARG_ANY);
519                                 if (!(Dc_arg_type&ARG_NONE))    {
520                                         dc_printf( "Ignoring the unused command line tail '%s %s'\n", Dc_arg_org, Dc_command_line );
521                                 }
522                         }
523
524                         return;
525                 }
526         }
527
528         dc_printf( "Unknown command '%s'\n", Dc_arg );
529 }
530
531 void debug_draw()
532 {
533         int i;
534         
535         gr_clear();
536         gr_set_font(FONT1);
537         gr_set_color_fast( &Color_bright );
538         gr_string( 0x8000, 3, "Debug Console" );
539
540         gr_set_color_fast( &Color_normal );
541
542         for (i=0; i<DROWS; i++ )        {
543                 gr_string( 0, i*16+16, debug_text[i] );
544         }
545
546         int t = timer_get_fixed_seconds() / (F1_0/3);
547         if ( t & 1 )    {
548                 int w,h;
549                 char c;
550
551                 c = debug_text[debug_y][command_line_pos+1];
552                 debug_text[debug_y][command_line_pos+1] = 0;
553
554                 gr_get_string_size( &w, &h, debug_text[debug_y] );
555                 
556                 //gr_string( w, debug_y*16, "_" );
557                 gr_rect(w+1,debug_y*16+1+16,2,14);
558         
559                 debug_text[debug_y][command_line_pos+1] = c;
560         }
561
562         gr_flip();
563 }
564
565
566 void debug_output( char c )
567 {
568         if ( c == '\t' )        {
569                 int next_tab = ((debug_x/28)+1)*28;
570
571                 if ( next_tab >= DCOLS-1 )      {
572                         debug_x=0;
573                         debug_y++;
574                         scroll_times++;
575                         if ( debug_y >= DROWS ) {
576                                 int i;
577                                 for (i=1; i<DROWS; i++ )
578                                         strcpy( debug_text[i-1], debug_text[i] );
579                                 debug_y = DROWS-1;
580                                 debug_x = 0;
581                                 debug_text[debug_y][debug_x] = 0;
582                         }
583                         debug_text[debug_y][debug_x] = 0;
584                         return;
585                 }
586         
587                 for ( ; debug_x < next_tab; )
588                         debug_text[debug_y][debug_x++] = ' ';
589                 debug_text[debug_y][debug_x] = 0;
590                 return;
591         }
592
593         if ( (c == '\n') || (debug_x >= DCOLS-1) )      {
594                 debug_x=0;
595                 debug_y++;
596                 scroll_times++;
597                 if ( debug_y >= DROWS ) {
598                         int i;
599                         for (i=1; i<DROWS; i++ )
600                                 strcpy( debug_text[i-1], debug_text[i] );
601                         debug_y = DROWS-1;
602                         debug_x = 0;
603                         debug_text[debug_y][debug_x] = 0;
604                 }
605                 debug_text[debug_y][debug_x] = 0;
606                 if ( c == '\n' ) return;
607         }
608
609         debug_text[debug_y][debug_x++] = c;
610         debug_text[debug_y][debug_x] = 0;
611 }
612
613 void dc_printf(char *format, ...)
614 {
615         char tmp[DCOLS*2];
616         va_list args;
617         
618         va_start(args, format);
619         vsprintf(tmp, format, args);
620         va_end(args);
621
622         char *p = tmp;
623         while( *p )     {
624                 debug_output(*p);
625                 p++;
626         } 
627 }
628
629
630
631 void debug_init()
632 {
633         int i;
634         if ( debug_inited ) return;
635
636         debug_inited = 1;
637
638         debug_x=0;
639         debug_y=0;
640
641         for (i=0; i<DROWS; i++ )        {
642                 debug_text[i][0] = 0;
643         }
644         
645         dc_printf("Debug console started.\n" );
646 }
647
648
649 void debug_console( void (*_func)() )
650 {
651         int done = 0;
652
653         scanner_init();
654
655         while( key_inkey() ){
656                 os_poll();
657         }
658
659         if ( !debug_inited ) debug_init();
660
661
662         debug_draw();
663         
664         while (!done)   {
665                 // poll the os
666                 os_poll();
667
668                 int k = key_inkey();
669                 switch( k )     {
670
671                 case KEY_SHIFTED+KEY_ENTER:
672                 case KEY_ESC:   
673                         done=1; break;
674
675                 case KEY_BACKSP:
676                         if ( command_line_pos > 0 )     {
677                                 command_line[--command_line_pos] = 0;
678                         }
679                         break;
680
681                 case KEY_F3:
682                         if ( last_oldcommand > -1 )     {
683                                 strcpy( command_line, oldcommand_line[last_oldcommand] );
684                                 command_line_pos = strlen(command_line);
685                                 command_line[command_line_pos] = 0;
686                         }
687                         break;
688
689                 case KEY_UP:
690                         command_scroll--;
691                         if (command_scroll<0) 
692                                 command_scroll = last_oldcommand;
693
694                         if ( command_scroll > -1 )      {
695                                 strcpy( command_line, oldcommand_line[command_scroll] );
696                                 command_line_pos = strlen(command_line);
697                                 command_line[command_line_pos] = 0;
698                         }
699                         break;
700
701                 case KEY_DOWN:
702                         command_scroll++;
703                         if (command_scroll>last_oldcommand) 
704                                 command_scroll = 0;
705                         if (command_scroll>last_oldcommand) 
706                                 command_scroll = -1;
707                         if ( command_scroll > -1 )      {
708                                 strcpy( command_line, oldcommand_line[command_scroll] );
709                                 command_line_pos = strlen(command_line);
710                                 command_line[command_line_pos] = 0;
711                         }
712                         break;
713
714                 case KEY_ENTER: {
715                         debug_output( '\n' );
716                         debug_draw();
717
718                         debug_do_command(command_line);
719
720                         int i, found = 0;
721                         for (i=0; i<=last_oldcommand; i++ )     {
722                                 if (!stricmp( oldcommand_line[i], command_line ))       {
723                                         found = 1;
724                                 }
725                         }
726                         if ( !found )   {
727                                 if ( last_oldcommand < DEBUG_HISTORY-1 )        {
728                                         last_oldcommand++;
729                                         strcpy( oldcommand_line[last_oldcommand], command_line);
730                                 } else {
731                                         int i;
732                                         for (i=0; i<last_oldcommand; i++ )      {
733                                                 strcpy( oldcommand_line[i], oldcommand_line[i+1] );
734                                         }
735                                         strcpy( oldcommand_line[last_oldcommand], command_line);
736                                 }
737                         }
738 //                      int i;
739 //                      for (i=0; i<=last_oldcommand; i++ )     {
740 //                              dc_printf( "OC %d. %s\n", i, oldcommand_line[i] );
741 //                      }
742         
743                         debug_output( '\n' );
744                         command_line_pos = 0;
745                         command_line[command_line_pos] = 0;
746
747                         command_scroll = 0;
748
749                         } 
750                         break;
751                 default:        {
752                                 ubyte c = (ubyte)key_to_ascii(k);
753                                 if ( c != 255 ) {
754                                         command_line[command_line_pos++] = c;
755                                         command_line[command_line_pos] = 0;
756                                 }
757                         }
758                 }
759
760                 strcpy( debug_text[debug_y], ">" );
761                 strcat( debug_text[debug_y], command_line );
762                 debug_draw();
763
764                 if ( _func ){
765                         _func();
766                 }
767         }
768
769         while( key_inkey() ){
770                 os_poll();
771         }
772 }
773
774 void debug_help()
775 {
776         int s, i;
777
778         dc_printf( "Available functions:\n\n" );        
779
780         s = scroll_times;
781         for (i=0; i<Num_debug_commands; i++ )   {
782                 dc_printf( " %s - %s\n", Debug_command[i]->name, Debug_command[i]->help );
783                 //mprintf(( "Scroll times %d\n", scroll_times - s ));
784                 if ( scroll_times - s > DROWS - 3 )     {
785                         int k;
786                         dc_printf( "       Press a key...B for back\n" );
787                         debug_draw();
788                         k = key_getch();
789                         s = scroll_times;
790                         if ( k == KEY_B )  {
791                                 i -= ((DROWS-3)*2);
792                                 if ( i <= 0 )
793                                         i = -1;
794                         }
795                 } 
796                 debug_draw();
797         }
798         dc_printf( "\n" );
799
800         dc_printf( "Typing '? function_name' will give the current status.\n" );
801         dc_printf( "Typing 'function_name ?' will give help on the function.\n" );
802         dc_printf( "Typing ? or help will give you help.\n");
803         dc_printf( "F3 selects last command line.\n" );
804 }
805