// flatbug.b                       1200                          9/16/98

// NOTE: This is a simple debugger for use with "flat real" mode programs
// with my "B-Flat" compiler (pub/simtelnet/msdos/misclang/bflat???.zip,
// where the "???" is the version number).
// See the file "flatbug.doc" for more information.

// FlatBug is copyright (C) 1998, by Ken Martwick.

// Version 0.10 9/14/98

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#lib flatlib;

char databuff[160];           // temp storage for display data
char filename[20];
char inchar;
char inprog;                  // set while debugging is in progress
char instring[60];
char *sline[5000];
char star = 42;
int botline = 25;
int datastart = 0x100;        // starting offset of display data
int debseg;                   // segment of debugger (CS = DS = SS)
int delta;
int flagreg;                  // program flags
int ipval = 0x100;            // instruction pointer for next instruction
int lastline;
int line;
int linecount;
int old_offset;               // original Int 03 offset
int old_segment;              // original Int 03 segment
int outfile;                  // "dump" file
int progfile;
int progseg;                  // segment where program is loaded
int progsize;                 // byte count of program
int result;
int symbase;
int symfile;
int termadd;
int topline;

// Break variables:

char breakcode[5];            // code byte replaced by "Int3" byte
char brkcode;                 // temp variable for fix_break() and set_break()
int breakline[5];             // line number of break
int breakadd[5];              // offset of break in program segment
int brkadd;                   // temp variable for fix_break() and set_break()
int breakcount;               // number of breakpoints set
int oldbreak;                 // offset of last (fixed) break

// Break variables for "single-stepping":

char stepcode[2];             // code bytes replaced by breaks
int instep;                   // set when "single-stepping"
int retadd;                   // "return" address for function
int stepadd[2];               // addresses of "stepping" breaks
int stepline[2];              // line numbers of breaks
int stepinto;                 // set to "step into" functions
int twoset;                   // set if two breaks set for branch

// box drawing "characters":

char hline = 205;
char ll = 200;
char lr = 188;
char ul = 201;
char ur = 187;
char vline = 186;

// Register Storage:

int eaxval;
int ebxval;
int ecxval;
int edxval;
int edival;
int esival;
int bpval;
int spval;
int csval;
int dsval;
int esval;
int fsval;
int gsval;
int ssval;

int atox(char *src);          // convert "src" to hexadecimal number
void call_program(void);      // jump to start of program to debug
void clean_end(void);         // get rid of <cr><lf>
void clear_code(void);        // just clear "code" window
void clear_instring(void);    // clear "instring"
void do_step(void);           // advance one instruction
int find_breakline(void);     // find line of breakpoint at "ipval"
int find_jumpline(int addval); // find destination line
int find_stepline(void);      // find line of step breakpoint at "ipval"
void fix_break(void);         // replace original byte at breakpoint
void fix_step(void);          // delete "stepping" break(s)
void fix_term(void);          // change exit code to Int 0x60
int get_address(int offset);  // get "JMP" destination
void get_data(void);          // get data to display
void get_input(void);         // get new "datastart" address
void get_regs(void);          // get current register values
void get_rest(void);          // get register values not saved by "int"
void getvect3(void);          // get vector for interrupt 3
void init_sym_pointers(void); // initialise "sline" pointers for "symbols"
void int03(void);             // breakpoint interrupt handler
void int60(void);             // interrupt 60 handler
int isbreak(int line);        // return 1 if break set at "line"
void load_program(void);      // load program to debug
void make_no_tail(void);      // clear first four bytes of "command tail"
void make_tail(void);         // copy "command tail" args to progseg:0x0080
void read_sym_file(void);     // read "symbol" file
void resetvect3(void);        // reset vector for interrupt 3
void resetvect60(void);       // reset vector for interrupt 60
void resume(void);            // resume execution at "oldbreak"
void set_break(int step);     // set debugging breakpoint
void set_step(int which);     // set "stepping" break "0" or "1"
void setvect3(void);          // set vector for interrupt 3
void setvect60(void);         // set vector for interrupt 60
void set_video_page(int page); // set video display to "page"
void show_breaks(void);       // show current break points
void show_code_screen(int all); // show "code" window
void show_data(void);         // display current data area
void show_flags(void);        // display "flag" values
void show_head(void);         // show "title" line
void show_help(void);         // display help screen
void show_line(int high);     // show code line (highlight if "high")
void show_regs(void);         // display register values at top right
void show_screen(int local);  // show current screen (get_regs() if "local")
void show_star(void);         // show breakpoint set on current line
void test_key(void);          // get keyboard input

void main(int argc, char *argv[])
     {
     if(argc < 2)
          {
          printf("\n\n\tUSAGE: flatbug <filename> [commmand tail]\n\n");
          exit(1);
          }

     sptr = argv[1];
     sprintf(filename, "%s.sym", sptr);
     symfile = fopen(filename, 0);
     if(!symfile)
          {
          printf("\n\n\tCannot open %s!\n\n", filename);
          exit(1);
          }

     _asm XOR  EAX,EAX
     _asm MOV  AX,CS
     _asm MOV  [debseg],EAX
     _asm MOV  EAX,stacktop
     _asm ADD  EAX,16
     _asm MOV  [symbase],EAX
     _asm PUSHF
     _asm XOR  EAX,EAX
     _asm POP  AX
     _asm MOV  [flagreg],EAX
     
     _asm XOR  EAX,EAX
     _asm MOV  AX,[debseg]
     _asm SHL  EAX,4
     _asm ADD  EAX,[symbase]
     _asm ADD  EAX,250004
     _asm SHR  EAX,4
     _asm MOV  [progseg],EAX
     _asm MOV  [jseg],AX
     _asm MOV  [jseg1],AX               

     if(argc > 2)
          {
          make_tail();        // copy "command tail" args to progseg:0x0080
          }
     else
          {
          make_no_tail();     // clear first four bytes of "command tail"
          }     

     init_sym_pointers();     // initialise "sline" pointers for "symbols"

     read_sym_file();         // read "symbol" file
     fclose(symfile);
     clear();
     load_program();          // load program to debug

     getvect3();              // get vector for interrupt 3
     setvect3();              // set vector for interrupt 3
     fix_term();              // change exit code to Int 0x60
     setvect60();             // set vector for interrupt 60

     set_video_page(1);       // set video display to "page 1"

     lastline = linecount - 1; // last line number

     nocur();

     line = 0;
     show_screen(1);          // show current screen
     
     _asm again:

     while(1)
          {
          test_key();         // get keyboard input
          }
     }

int atox(char *src)           // convert "src" to hexadecimal number
     {
     int numval;

     numval = 0;

     while(1)
          {
          chval = *_src;
          if(chval < 48)
               {
               return(numval);
               }
          if(chval > 70)
               {
               return(numval);
               }
          if(chval < 58)
               {
               chval = chval - 48; // "ASCII" correction (numeric)
               }
          else
               {
               chval = chval - 55; // "ASCII" correction (alpha)
               }
          _asm XOR  EAX,EAX
          _asm MOV  AL,[chval]
          _asm MOV  [digit],EAX
          numval = numval << 4;
          numval = numval + digit;
          _src++;
          }
     }

void call_program(void)       // jump to start of program to debug
     {
     set_video_page(0);

     _asm MOV  AX,[progseg]
     _asm MOV  SS,AX
     _asm DB   0xEA
     _asm DW   0x100
     _asm jseg: DW 0
     }

void clean_end(void)          // get rid of <cr><lf>
     {
     char tchar;
     int n;

     for(n = 49; n > 0; n--)
          {
          tchar = instring[n]; // get a byte to test
          if(tchar > 32)      // "graphic" character
               {
               break;
               }
          else
               {
               instring[n] = 0;
               }
          }
     }

void clear_code(void)         // just clear "code" window
     {
     _asm MOV  AH,6
     _asm MOV  AL,45
     _asm MOV  BH,0x1E
     _asm MOV  CH,2
     _asm MOV  CL,0
     _asm MOV  DH,27
     _asm MOV  DL,40
     _asm INT  0x10
     }

void clear_instring(void)     // clear "instring"
     {
     int k;

     for(k = 0; k < 50; k++)
          {
          instring[k] = 0;
          }
     }

void do_step(void)            // advance one instruction
     {
     char code;
     int destadd;
     int relval;
     
     _asm MOV  EAX,[progseg]
     _asm MOV  ES,AX
     _asm MOV  ESI,[ipval]
     _asm MOV  AL,[ES:ESI]
     _asm MOV  [chval],AL
     _asm INC  ESI
     _asm XOR  EAX,EAX
     _asm MOV  AX,[ES:ESI]
     _asm MOV  [delta],EAX 
     
     relval = delta;          // only valid if opcode is "E8" or "E9"
     code = chval;
     
     instep = 1;              // show "single-stepping" in progress

//     putcur(20, 29);
//     cprintf("The code byte at %lx is %x", ipval, code);
//     while(1)
//          {
//          getkey();
//          if(chval == 'g')
//               {
//               break;
//               }
//          }

     if(code == 0xC3)
          {
          destadd = retadd;   // saved by int03 handler!
          line = find_jumpline(destadd); // find destination line		
          set_step(0);
          resume();
          }
     
     if(stepinto)
          {
          if(code == 0xE8)		// "CALL" instruction	
			{	 
			destadd = get_address(relval); // get "JMP" destination
			line = find_jumpline(destadd); // find destination line
			set_step(0);
			resume();
			}
          }     
     
     if(code == 0xE9)         // "JMP" instruction
          {
          destadd = get_address(relval); // get "JMP" destination
          line = find_jumpline(destadd); // find destination line
          set_step(0);
          resume();
          }

     if(code > 0x71)
          {
          if(code < 0x78)     // "Jcc" instruction
               {
               line++;
               set_step(0);   // set "test failed" break
               line++;
               set_step(1);   // set "test passed" break
               twoset = 1;
               resume();      // must be in program!
               }
          }

     if(line < 4)
          {
          line = 4;
          }
     else
          {     
          line++;
          }
          
     set_step(0)              // set break on next line
               
     if(inprog)
          {
          resume();
          }
     else
          {
          call_program();
          }     
     }     

int find_breakline(void)      // find line of breakpoint at "ipval"
     {
     int lineval;
     
     if(instep)               // if "single-stepping"
          {
          lineval = find_stepline();
          return(lineval);
          }
     
     for(n = 0; n < 5; n++)
          {
          if(breakadd[n] == ipval)
               {
               lineval = breakline[n];
               return(lineval);
               }
          }     
     return(0);               // should never happen!
     }     

int find_jumpline(int addval) // find destination line
     {
     char *testptr;
     int ndx;
     int testval;
     
     for(ndx = 0; ndx < lastline; ndx++) // "brute force" search
          {
          testptr = sline[ndx];
          testval = atox(testptr);
          if(testval == _addval)
               {
//               putcur(20, 31);
//               cprintf("The jump destination line is: %ld", ndx);
//               while(1)
//                    {
//                    getkey();
//                    if(chval == 'g')
//                         {
//                         break;
//                         }
//                    }     
               return(ndx);
               }
          }
          
     return(0);
     }          

int find_stepline(void)       // find line of breakpoint at "ipval"
     {
     int lineval;
     
     for(n = 0; n < 2; n++)
          {
          if(stepadd[n] == ipval)
               {
               lineval = stepline[n];
               return(lineval);
               }
          }     
     return(0);               // should never happen!
     }     

void fix_break(void)          // replace original byte at breakpoint
     {
     int found;
     
     if(instep)               // if "single-stepping"
          {
          fix_step();
          return;
          }
     
     found = 0;
     
     for(n = 0; n < 5; n++)
          {
          brkadd = breakadd[n];
          if(brkadd == ipval)
               {
               found = 1;
               break;
               }
          }
          
     if(!found)
          {
          attrib = 0x8C;
          nocur();
          putcur(10, 30);
          cprintf("Error!!  Cannot find breakpoint!!");
          getkey();
          set_video_page(0);
          attrib = 7;
          clear();
          exit(1);
          }     
          
     brkcode = breakcode[n];          
     
     _asm MOV  AX,[progseg]
     _asm MOV  ES,AX
     _asm MOV  DI,[brkadd]
     _asm MOV  AL,[brkcode]
     _asm MOV  byte [ES:DI],AL
     
     breakadd[n] = 0;
     breakcode[n] = 0;
     breakline[n] = 0;
     
     breakcount--;            // decrement break count
     oldbreak = brkadd;       // save address
     }     

void fix_step(void)           // delete "stepping" break(s)
     {
     brkcode = stepcode[0];
     brkadd = stepadd[0];
     
     _asm MOV  AX,[progseg]
     _asm MOV  ES,AX
     _asm MOV  DI,[brkadd]
     _asm MOV  AL,[brkcode]
     _asm MOV  byte [ES:DI],AL
     
     stepadd[0] = 0;
     stepcode[0] = 0;
     stepline[0] = 0;
     
     if(twoset)
          {
          brkcode = stepcode[1];
          brkadd = stepadd[1];          
          
          _asm MOV  AX,[progseg]
          _asm MOV  ES,AX
          _asm MOV  DI,[brkadd]
          _asm MOV  AL,[brkcode]
          _asm MOV  byte [ES:DI],AL
          
          stepadd[1] = 0;
          stepcode[1] = 0;
          stepline[1] = 0;
          twoset = 0;
          }
     }

void fix_term(void)           // change exit code to Int 0x60
     {
     _asm MOV  EAX,[progseg]
     _asm MOV  ES,AX
     _asm MOV  EDI,[termadd]
     _asm INC  EDI
     _asm MOV  byte [ES:DI],0x60
     }

int get_address(int offset)   // get "JMP" destination
     {
     _asm MOV  EAX,[_offset]
     _asm CWDE
     _asm MOV  EBX,[ipval]
     _asm ADD  EAX,EBX
     _asm ADD  EAX,3
     _asm MOV  [delta],EAX
     
//     putcur(20, 30);
//     cprintf("The jump destination address is: %lx", delta);
     return(delta);
     }

void get_data(void)           // get data to display
     {
     _asm MOV  GS,[progseg]
     _asm MOV  ESI,[datastart]
     _asm MOV  EDI,databuff
     _asm MOV  CX,40
     _asm gdloop: JCXZ gddone
     _asm MOV  EAX,[GS:ESI]
     _asm MOV  [EDI],EAX
     _asm ADD  ESI,4
     _asm ADD  EDI,4
     _asm DEC  CX
     _asm JMP  gdloop
     _asm gddone: NOP
     }

void get_input(void)          // get new "datastart" address
     {
     char inch;
     char *stptr;
     int m;
     
     putcur(41, 24);     
     cprintf("Enter hex starting address: ");
     
     stptr = &instring;
     
     for(m = 0; m < 20; m++)
          {
          inch = getche();
          if(inch < 48)
               {
               break;
               }
          inch = toupper(inch);
          *stptr = inch;
          stptr++;
          }
          
     putcur(41, 24);     
     cprintf("                                   ");

     stptr = &instring;
     datastart = atox(stptr);
     }

void get_regs(void)           // get current register values
     {
     _asm MOV  [eaxval],EAX          
     _asm MOV  [ebxval],EBX     
     _asm MOV  [ecxval],ECX     
     _asm MOV  [edxval],EDX     
     _asm MOV  [edival],EDI     
     _asm MOV  [esival],ESI     
     _asm MOV  [bpval],BP     
     _asm MOV  [spval],SP     
     _asm XOR  EAX,EAX     
     _asm MOV  AX,CS     
     _asm MOV  [csval],EAX     
     _asm MOV  AX,DS     
     _asm MOV  [dsval],EAX     
     _asm MOV  AX,ES     
     _asm MOV  [esval],EAX     
     _asm MOV  AX,FS     
     _asm MOV  [fsval],EAX     
     _asm MOV  AX,GS     
      _asm MOV  [gsval],EAX     
     _asm MOV  AX,SS     
     _asm MOV  [ssval],EAX     
     }
          
void get_rest(void)           // get register values not saved by "int"
     {          
     _asm MOV  [ebxval],EBX     
     _asm MOV  [ecxval],ECX     
     _asm MOV  [edxval],EDX     
     _asm MOV  [edival],EDI     
     _asm MOV  [esival],ESI     
     _asm XOR  EAX,EAX     
     _asm MOV  AX,ES     
     _asm MOV  [esval],EAX     
     _asm MOV  AX,FS     
     _asm MOV  [fsval],EAX     
     _asm MOV  AX,GS     
     _asm MOV  [gsval],EAX     
     }     

void getvect3(void)           // get vector for interrupt 3
     {
     _asm MOV  AH,0x35
     _asm MOV  AL,3
     _asm INT  0x21
     _asm XOR  EAX,EAX
     _asm MOV  AX,ES
     _asm MOV  [old_segment],EAX
     _asm XOR  EAX,EAX
     _asm MOV  AX,BX
     _asm MOV  [old_offset],EAX     
     }

void init_sym_pointers(void)  // initialise "sline" pointers for "symbols"
     {
     n = 0;
     sline[n] = symbase;

     for(line = 1; line < 5000; line++)
          {
          sline[line] = sline[n] + 50;
          n++;
          }
     }

void int03(void)              // breakpoint interrupt handler
     {
     _asm PUSH DS
     _asm PUSH CS
     _asm POP  DS
     _asm POP  word [dsval]
     _asm MOV  [eaxval],EAX
     _asm XOR  EAX,EAX
     _asm POP  AX
     _asm SUB  AX,1
     _asm MOV  [ipval],EAX
     _asm POP  AX     
     _asm MOV  [csval],EAX
     _asm MOV  [ssval],EAX
     _asm POP  AX
     _asm OR   AX,0x200
     _asm MOV  [flagreg],EAX
     _asm POP  AX
     _asm MOV  [retadd],EAX
     _asm PUSH AX
     _asm MOV  [spval],SP
     _asm MOV  [bpval],BP     
     _asm PUSH CS
     _asm POP  SS 
     _asm MOV  SP,stacktop
     _asm MOV  byte [inprog],1
// finally, we're back to our own stack - we can safely make calls!

     set_video_page(1);       // debugger video page

     get_rest();              // save the rest of the registers
     line = find_breakline(); // find line of breakpoint at "ipval"
     fix_break();             // replace old byte at "ipval"     

     show_screen(0);
     
     _asm JMP  again
     }

void int60(void)              // interrupt 60 handler
     {
     int k;

     _asm PUSH DS
     _asm PUSH CS
     _asm POP  DS
     _asm MOV  [exitcode],AL     
     _asm POP  word [dsval]
     _asm MOV  [spval],SP
     _asm MOV  [bpval],BP
     _asm MOV  [eaxval],EAX
     _asm XOR  EAX,EAX
     _asm POP  AX
     _asm MOV  [ipval],EAX
     _asm MOV  AX,SS
     _asm MOV  [ssval],AX
     _asm MOV  [csval],AX
     _asm POP  word [flagreg]
     _asm PUSH CS
     _asm POP  SS     
     _asm MOV  SP,stacktop
     _asm STI
     set_video_page(1);
     nocur()

     get_rest();              // save the rest of the registers
     show_screen(0);
     inprog = 0;              // no longer in the program!

     attrib = 0x8C;
     nocur();
     putcur(10, 30);
     cprintf("%c", ul);
     for(k = 0; k < 38; k++)
          {
          cprintf("%c", hline);
          }
     cprintf("%c", ur);
     putcur(10, 31);
     cprintf("%c", vline);
     cprintf(" Program terminated...  Exit code: %d. ", exitcode);
     cprintf("%c", vline);
     putcur(10, 32);
     cprintf("%c", ll);
     for(k = 0; k < 38; k++)
          {
          cprintf("%c", hline);
          }
     cprintf("%c", lr);
     getkey();

     attrib = 0x1e;
     clear();
     ipval = 0x100;
     for(n = 0; n < breakcount; n++) // zero "break" variables
          {
          breakcode[n] = 0;
          breakline[n] = 0;
          breakadd[n] = 0;
          }
     breakcount = 0;
     oldbreak = 0;
     

     show_screen(0);     

     _asm JMP  again
     }

int isbreak(int line)         // return 1 if break set at "line"
     {
     int num;
     
     for(num = 0; num < breakcount; num++)
          {
          if(breakline[breakcount] == _line)
               {
               return(1);     // break set
               }
          }
     return(0);               // break not set
     }          

void load_program(void)       // load program to debug
     {
     sptr = argv[1];
     sprintf(filename, "%s.com", sptr);
     progfile = fopen(filename, 0);
     if(!progfile)
          {
          printf("\n\n\tCannot open %s!\n\n", filename);
          exit(1);
          }

     progsize = segread(progseg, 0x100, 65500, progfile); // read to "progseg"

     if(!progsize)
          {
          fclose(progfile);
          printf("\n\n\tError reading %s!", filename);
          printf("\n\tThe error code was 0x%lx.\n\n", ercode);
          exit(1);
          }

     fclose(progfile);
     }

void make_no_tail(void)       // clear first four bytes of "command tail"
     {
     _asm XOR  EAX,EAX
     _asm PUSH DS
     _asm POP  AX
     _asm SHL  EAX,4
     _asm MOV  EBX,[progseg]
     _asm SHL  EBX,4
     _asm SUB  EBX,EAX
     _asm ADD  EBX,0x80
     _asm MOV  EDI,EBX
     _asm XOR  EAX,EAX
     _asm MOV  [EDI],EAX
     }     

void make_tail(void)          // copy "command tail" args to progseg:0x0080
     {
     char *count;
     char *destptr;
     char *srcptr;
     int tlength;
     
     tlength = 0;
     
     _asm XOR  EAX,EAX
     _asm PUSH DS
     _asm POP  AX
     _asm SHL  EAX,4
     _asm MOV  EBX,[progseg]
     _asm SHL  EBX,4
     _asm SUB  EBX,EAX
     _asm ADD  EBX,0x80     
     _asm MOV  [dptr],EBX
     
     count = dptr;            // save pointer to "count" byte
     destptr = dptr + 1;      // point to first "text" byte
     
     for(n = 2; n < argc; n++)
          {
          srcptr = argv[n];
          result = sprintf(destptr, " %s ", srcptr);
          destptr = destptr + result;
          tlength = tlength + result;
          }
          
     *count = tlength;        // we just need the LS byte     
     }     

void read_sym_file(void)      // read "symbol" file
     {
     int check;
     int foundend;

     foundend = 10000;        // impossible value
     line = 0;
     linecount = 0;

     while(1)
          {
          clear_instring();   // clear "instring"
          check = fgets(instring, 50, symfile);
          if(!check) break;
          clean_end();        // get rid of <cr> <lf>
          dptr = sline[line];
          strcpy(dptr, instring);
          line++;
          linecount++;
          check = strstr(instring, "MAINEND:"); // exit label
          if(check)
               {
               foundend = line + 2; // exit point is two lines past "MAINEND"
               }
          if(line == foundend)
               {
               termadd = atox(instring);
               }
          }
     }

void resetvect3(void)         // reset vector for interrupt 3
     {
     _asm PUSH DS
     _asm MOV  EAX,[old_segment]
     _asm MOV  EDX,[old_offset]     
     _asm MOV  DS,AX
     _asm MOV  AH,0x25
     _asm MOV  AL,3
     _asm INT  0x21
     _asm POP  DS
     }

void resetvect60(void)        // reset vector for interrupt 60
     {
     _asm PUSH DS
     _asm MOV  EAX,0
     _asm MOV  DS,AX
     _asm MOV  DX,0
     _asm MOV  AH,0x25
     _asm MOV  AL,0x60
     _asm INT  0x21
     _asm POP  DS
    }

void resume(void)             // resume execution at "oldbreak"
     {
     char code;
     
     _asm MOV  ES,[progseg]
     _asm MOV  DI,[oldbreak]
     _asm MOV  AL,[ES:DI]
     _asm MOV  [chval],AL

     code = chval;     
     
//     putcur(10, 30);
//     cprintf("Jump to %lx?  Code is %x.", oldbreak, code);
//     getkey()
//     inchar = toupper(chval);
//     putcur(10, 30);
//     cprintf("                                   ");
//     if(inchar != 'Y')
//          {
//          return;
//          }
     
     set_video_page(0);
     
     _asm POP  AX
     _asm MOV  AX,[oldbreak]
     _asm MOV  [joff],AX
     _asm MOV  EAX,[bpval]
     _asm MOV  BP,AX
     _asm MOV  EAX,[spval]
     _asm MOV  SP,AX
     _asm MOV  EAX,[ssval]
     _asm MOV  SS,AX
     _asm MOV  EAX,[esval]
     _asm MOV  ES,AX
     _asm MOV  EAX,[fsval]
     _asm MOV  FS,AX
     _asm MOV  EAX,[gsval]
     _asm MOV  GS,AX
     _asm PUSH word [dsval]
     _asm PUSH word [flagreg]
     _asm MOV  EAX,[eaxval]
     _asm MOV  EBX,[ebxval]
     _asm MOV  ECX,[ecxval]
     _asm MOV  EDX,[edxval]
     _asm POPF
     _asm POP  DS
     
     _asm DB   0xEA
     _asm joff: DW 0
     _asm jseg1: DW 0
     }     

void set_break(int step)      // set debugging breakpoint
     {
     int found;
     
     for(n = 0; n < 5; n++)
          {
          brkadd = breakadd[n];
          if(brkadd == 0)
               {
               found = 1;
               break;
               }
          }
     
     if(!found)
          {
          return;             // only five allowed!
          }
     
     breakline[n] = line;
     dptr = sline[line];
     brkadd = atox(dptr);     // get offset into program
     
     _asm MOV  AX,[progseg]
     _asm MOV  ES,AX
     _asm MOV  DI,[brkadd]
     _asm MOV  AL,[ES:DI]
     _asm MOV  [brkcode],AL
     _asm MOV  byte [ES:DI],0xCC
     
     breakadd[n] = brkadd;
     breakcode[n] = brkcode;     
     
     breakcount++;
//     if(_step == 0)  
//          {
//          getxy();
//          putcur(4, yval);
//          cprintf("%c", star);
//          show_breaks();      // show current break points          
//          }
     yval = line - topline;
     yval = yval + 2;
     putcur(4, yval);
     cprintf("%c", star);
     show_breaks();           // show current break points          
     }      

void set_step(int which)      // set "stepping" break "0" or "1"
     {
     n = _which;
     
     stepline[n] = line;
     dptr = sline[line];
     brkadd = atox(dptr);     // get offset into program
     
     _asm MOV  AX,[progseg]
     _asm MOV  ES,AX
     _asm MOV  DI,[brkadd]
     _asm MOV  AL,[ES:DI]
     _asm MOV  [brkcode],AL
     _asm MOV  byte [ES:DI],0xCC
     
     stepadd[n] = brkadd;
     stepcode[n] = brkcode;          
     }

void setvect3(void)           // set vector for interrupt 3
     {
     _asm MOV  AH,0x25
     _asm MOV  AL,3
     _asm MOV  DX,int03     
     _asm INT  0x21
     }

void setvect60(void)          // set vector for interrupt 60
     {
     _asm MOV  AH,0x25
     _asm MOV  AL,0x60
     _asm MOV  DX,int60
     _asm INT  0x21
     }

void set_video_page(int page) // set video display to "page"
     {
     page = _page;

     _asm MOV  AH,5
     _asm MOV  AL,[_page]
     _asm INT  0x10
     }

void show_breaks(void)        // show current break points
     {
     int bnum;
     int boff;
     
     row = 20;
     
     for(bnum = 0; bnum < 5; bnum++)
          {
          boff = breakadd[bnum];
          if(boff)
               {
               putcur(41, row);
               cprintf("%c Breakpoint set at 0x%lx", vline, boff);
               row++;
               }
          }          
     }     

void show_code_screen(int all) // show "code" window
     {
     int limit;
     int btest;

     if(_all)
          {
          clear();            // clear whole screen
          }
     else
          {
          clear_code();       // just clear "code" window
          }

     if(line < topline)
          {
          topline = line - 2;
          botline = topline + 25;
          }
     else
          {
          if(line > botline)
               {
               topline = line - 2;
               botline = topline + 25
               if(botline > lastline)
                    {
                    botline = lastline;
                    if(botline >= 25)
                         {
                         topline = botline - 25;
                         }
                    }
               }
          }          

     limit = botline + 1;
     row = 2;

     for(n = topline; n < limit; n++)
          {
          putcur(0, row);
          sptr = sline[n];
          if(n == line)
               {
               attrib = 0x6A;
               cprintf("%s", sptr);
               attrib = 0x1E;
               }
          else
               {
               cprintf("%s", sptr);
               }
          btest = isbreak(n); // return 1 if break set at "n"
          if(btest)
               {
               putcur(4, row);
               cprintf("%c", star); // show break set
               }
          row++;
          }
     }

void show_data(void)          // display current data area
     {
     char codval;
     char codrow;
     char *codptr;
     char *texptr;
     int j;
     int k;
     int startadd;
     
     
     putcur(0, 28);
     for(j = 0; j < 79; j++)
          {
          cprintf("%c", hline);
          }
          
     get_data();              // get data to display
     
     codrow = 29;
     startadd = datastart;
     codptr = &databuff;
     texptr = &databuff;
     
     for(j = 0; j < 10; j++)
          {
          putcur(0, codrow);
          cprintf("%0lx ", startadd);
          putcur(0, codrow);
          cprintf(" DS:");
          putcur(8, codrow);
          for(k = 0; k < 16; k++)
               {
               codval = *codptr;
               if(codval < 16)
                    {
                    cprintf(" 0%x", codval);
                    }
               else
                    {
                    cprintf(" %x", codval);
                    }
               codptr++;
               }

          putcur(57, codrow);

          for(k = 0; k < 16; k++)
               {
               codval = *texptr;
               if(codval < 32)
                    {
                    cprintf(".");
                    texptr++;
                    k++;
                    continue;
                    }

               if(codval > 127)
                    {
                    cprintf(".");
                    texptr++;
                    k++;
                    continue;
                    }

               cprintf("%c", codval);
               texptr++;
               }

          codrow++;
          startadd = startadd + 16;
          }     
     }     

void show_flags(void)         // display "flag" values
     {
     char cflag;
     char pflag;
     char aflag;
     char zflag;
     char sflag;
     char intflag;
     char dflag;
     char oflag;
     char flagval;
     int value;

     putcur(60, 2);
     value = flagreg & 1;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != cflag)
          {
          attrib = 0x6A;
          cprintf("%c  Carry       %d", vline, flagval)
          attrib = 0x1E;
          cflag = flagval;
          }
     else
          {
          cprintf("%c  Carry       %d", vline, flagval)
          }
     putcur(60, 3);
     flagval = flagreg & 4;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != pflag)
          {
          attrib = 0x6A;
          cprintf("%c  Parity      %d", vline, flagval)
          attrib = 0x1E;
          pflag = flagval;
          }
     else
          {
          cprintf("%c  Parity      %d", vline, flagval)
          }
     putcur(60, 4);
     flagval = flagreg & 16;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != aflag)
          {
          attrib = 0x6A;
          cprintf("%c  Auxiliary   %d", vline, flagval)
          attrib = 0x1E;
          aflag = flagval;
          }
     else
          {
          cprintf("%c  Auxiliary   %d", vline, flagval)
          }
     putcur(60, 5);
     value = flagreg & 64;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != zflag)
          {
          attrib = 0x6A;
          cprintf("%c  Zero        %d", vline, flagval)
          attrib = 0x1E;
          zflag = flagval;
          }
     else
          {
          cprintf("%c  Zero        %d", vline, flagval)
          }
     putcur(60, 6);
     value = flagreg & 128;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != sflag)
          {
          attrib = 0x6A;
          cprintf("%c  Sign        %d", vline, flagval)
          attrib = 0x1E;
          sflag = flagval;
          }
     else
          {
          cprintf("%c  Sign        %d", vline, flagval)
          }
     putcur(60, 7);
     value = flagreg & 512;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != intflag)
          {
          attrib = 0x6A;
          cprintf("%c  Interrupt   %d", vline, flagval)
          attrib = 0x1E;
          intflag = flagval;
          }
     else
          {
          cprintf("%c  Interrupt   %d", vline, flagval)
          }
     putcur(60, 8);
     value = flagreg & 1024;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != dflag)
          {
          attrib = 0x6A;
          cprintf("%c  Direction   %d", vline, flagval)
          attrib = 0x1E;
          dflag = flagval;
          }
     else
          {
          cprintf("%c  Direction   %d", vline, flagval)
          }
     putcur(60, 9);
     value = flagreg & 2048;
     if(value)
          {
          flagval = 1;
          }
     else
          {
          flagval = 0;
          }
     if(flagval != oflag)
          {
          attrib = 0x6A;
          cprintf("%c  Overflow    %d", vline, flagval)
          attrib = 0x1E;
          oflag = flagval;
          }
     else
          {
          cprintf("%c  Overflow    %d", vline, flagval)
          }
     }
     
void show_head(void)          // show "title" line
     {
     int num;
     
     putcur(0, 0);
     attrib = 0x6A;
     
     for(num = 0; num < 80; num++)
            {
            cprintf(" ");
            }
            
     putcur(2, 0);
     cprintf("Help:F1  FlatBug(C) Debugging: %s", filename);
     
     attrib = 0x1E;
     }               

void show_help(void)          // display help screen
     {
     set_video_page(2);
     clear();
     putcur(22, 3);
     show_text("Cursor Movement:");
     putcur(22, 5);     
     show_text("UpAro - up one line");
     putcur(22, 6);
     show_text("DownAro - down one line");
     putcur(22, 7);
     show_text("PageDown - down 23 lines");
     putcur(22, 8);
     show_text("PageUp - up 23 lines");
     putcur(22, 9);
     show_text("AltZ - end of code");
     putcur(22, 10);
     show_text("AltA - top of code");
     putcur(22, 12);
     show_text("Debugging:");
     putcur(22, 14);
     show_text("F2 - set breakpoint");
     putcur(22, 15);
     show_text("F5 - show user screen");
     putcur(22, 16);
     show_text("F6 - show debugger screen");
     putcur(22, 17);
     show_text("F7 - single step - step into functions");
     putcur(22, 18);
     show_text("F8 - single step - step past functions");
     putcur(22, 19);
     show_text("F9 - run program");
     putcur(22, 21);
     show_text("Miscellaneous:");
     putcur(22, 23);
     show_text("AltD - set data display starting address");
     putcur(22, 24);
     show_text("F1 - show this help screen");
     putcur(22, 25);
     show_text("AltX - exit");
     
     getkey();
     set_video_page(1);
     }

void show_line(int high)      // show code line (highlight if "high")
     {
     int srow;

     srow = line - topline;
     srow = srow + 2;
     putcur(0, srow);

     sptr = sline[line];

     if(_high)                // show line highlighted
          {
          attrib = 0x6A;
          cprintf("%s", sptr);
          attrib = 0x1E;
          }
     else
          {
          cprintf("%s", sptr);
          }
     }

void show_regs(void)          // display register values at top right
     {
     putcur(41, 2);
     cprintf("%c  EAX %0lx", vline, eaxval);
     putcur(41, 3);
     cprintf("%c  EBX %0lx", vline, ebxval);
     putcur(41, 4);
     cprintf("%c  ECX %0lx", vline, ecxval);
     putcur(41, 5);
     cprintf("%c  EDX %0lx", vline, edxval);
     putcur(41, 6);
     cprintf("%c  EDI %0lx", vline, edival);
     putcur(41, 7);
     cprintf("%c  ESI %0lx", vline, esival);
     putcur(41, 8);
     cprintf("%c", vline);
     putcur(45, 9);
     cprintf("%0lx", bpval);
     putcur(41, 9);
     cprintf("%c    BP ", vline);
     putcur(45, 10);
     cprintf("%0lx", spval);
     putcur(41, 10);
     cprintf("%c    SP ", vline);
     putcur(45, 11);
     cprintf("%0lx", csval);
     putcur(41, 11);
     cprintf("%c    CS ", vline);
     putcur(45, 12);
     cprintf("%0lx", dsval);
     putcur(41, 12);
     cprintf("%c    DS ", vline);
     putcur(45, 13);
     cprintf("%0lx", esval);
     putcur(41, 13);
     cprintf("%c    ES ", vline);
     putcur(45, 14);
     cprintf("%0lx", fsval);
     putcur(41, 14);
     cprintf("%c    FS ", vline);
     putcur(45, 15);
     cprintf("%0lx", gsval);
     putcur(41, 15);
     cprintf("%c    GS ", vline);
     putcur(45, 16);
     cprintf("%0lx", ssval);
     putcur(41, 16);
     cprintf("%c    SS ", vline);
     putcur(41, 17);
     cprintf("%c", vline);
     putcur(45, 18);
     cprintf("%0lx", ipval);
     putcur(41, 18);
     cprintf("%c    IP ", vline);
     }

void show_screen(int local)   // show current screen (get_regs() if "local")
     {
     show_code_screen(1);     // show "code" window

     show_head();             // show "title" line
     
     if(_local)
          {
          get_regs();         // get current register values
          }
          
     show_regs();             // display register values at top right
     show_flags();
     show_breaks();           // show current break points

     show_data();             // display current data area
     }

void show_star(void)          // show breakpoint set on current line
     {
     getxy();
     putcur(4, yval);
     cprintf("%c", star);
     }

void test_key(void)           // get keyboard input
     {
     int retval;
     
     getkey();

     switch(keyval)
          {
          case 0x2D00:        // AltX
               set_video_page(0); // set video display to "page 0"
               resetvect3();  // reset vector for interrupt 3               
               resetvect60(); // reset vector for interrupt 60
               attrib = 7;
               clear();
               exit(0);

          case 0x5100;        // PageDown
               if(botline < lastline) // if not at bottom
                    {
                    topline = topline + 23;
                    botline = botline + 23;
                    if(botline > lastline)
                         {
                         botline = lastline;
                         if(botline >= 25)
                              {
                              topline = botline - 25;
                              }
                         }
                    line = topline + 2;
                    show_code_screen(0); // show "code" window
                    }
               break;

          case 0x2C00;        // AltZ (end of file)
               botline = lastline;
               if(botline > 25) // if not a very short file
                    {
                    topline = botline - 25;
                    }
               else
                    {
                    topline = 0;
                    }
               line = botline;
               show_code_screen(0); // show "code" window
               break;

          case 0x4900;        // PageUp
               if(topline > 0) // if not at top
                    {
                    botline = botline - 23;
                    if(botline >= 25)
                         {
                         topline = botline - 25;
                         }
                    else
                         {
                         topline = 0;
                         botline = 25;
                         }
                    line = botline - 2;
                    show_code_screen(0); // show "code" window
                    }
               break;

          case 0x1E00;        // AltA (top of code)
               topline = 0;
               if(lastline > 25) // not a very short file
                    {
                    botline = 25;
                    }
               else
                    {
                    botline = lastline;
                    }
               line = topline;
               show_code_screen(0); // show "code" window
               break;

          case 0x4800;        // UpAro (Line Up)
               if(line > topline)
                    {
                    show_line(0);
                    retval = isbreak(line);
                    if(retval)
                         {
                         show_star();
                         }                    
                    line--;
                    show_line(1);
                    retval = isbreak(line);
                    if(retval)
                         {
                         show_star();
                         }                    
                    }
               break;

          case 0x5000;        // DownAro (Line Down)
               if(line < botline)
                    {
                    show_line(0);
                    retval = isbreak(line);
                    if(retval)
                         {
                         show_star();
                         }
                    line++;
                    show_line(1);
                    retval = isbreak(line);
                    if(retval)
                         {
                         show_star();
                         }                    
                    }
               break;

          case 0x3B00:        // F1 (show help screen)
               show_help();   // display help screen
               break;          

          case 0x3F00;        // F5 (show user screen)
               set_video_page(0);
               break;

          case 0x4000;        // F6 (show debugger screen)
               set_video_page(1);
               break;

          case 0x4100;        // F7 (single step - step into functions)
               stepinto = 1;
               do_step();     // advance one instruction
               break;

          case 0x4200;        // F8 (single step - step past functions)
               stepinto = 0;
               do_step();     // advance one instruction
               break;

          case 0x4300;        // F9 (run program)
               if(inprog)     // already debugging
                    {
                    putcur(41, 24);
                    cprintf("Continue from last breakpoint? ");
                    getkey()
                    inchar = toupper(chval);
                    putcur(41, 24);
                    cprintf("                                   ");
                    if(inchar == 'Y')
                         {
                         resume(); // resume execution at "oldbreak"                         
                         }
                    }
               call_program(); // jump to start of program to debug
               break;
          
          case 0x3C00;        // F2 (set breakpoint)
               set_break(0);  // set debugging breakpoint
               break;

          case 0x2000;        // AltD (select data display address)
               get_input();   // get new "datastart" address
               show_data();   // display data
               break;
               
          default:
               break;
          }
     }
