/**
 * TrueReality - debug.c
 * Copyright (C) 1998 Niki W. Waibel
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 * Information about me (the author):
 *   Niki W. Waibel, Reichenau 20, 6890 Lustenau, Austria - EUROPE
 *   niki.waibel@gmx.net
**/





#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "n64/type_sizes.h"
#include "n64/memory.h"
#include "n64/mnemonic.h"
#include "n64/registers.h"
#include "n64/cpu.h"
#include "dispatch.h"
#include "parser_extern.h"
#include "romimage.h"
#include "debug.h"



static  WORD    breakp[256];



void n64_cpu_debugger()
{
        char    line[82];
        int     i, j, n;
        WORD    addr;
        HWORD   hword;
        WORD    word;
        DWORD   dword;



        breakp[0] = 0;
   
        do
        {
                printf("tr debug> ");
      
                fgets(line, 80, stdin);
      
                switch(line[0])
                {
                    case 'R':
                        clear_n64_mem();
                        rs4300i_Reset();
                        copy_bootcode_of_n64_image();
                        dispatch(DISPLAY_REFRESH, 0);
                        break;
                        

                        
                    case 'h':
                    case 'H':
                        fputs("n64_cpu_debugger: For help have a look at TrueReality/Documentation/Debugger\n"
                             "(Exit debugger with '!')\n\n", stderr);
                        break;

                    
                    
                    case 'd':
                        if(sscanf(line+1, "%llx", &dword) == 1)
                        {
                                prefs.debug = dword;
                                
                        }
                        else
                        {
                                fputs("n64_cpu_debugger: Use 'd n' set debugging to level n\n"
                                      "          Currently supported n values (don't type '0x' and '_'!)\n"
                                      "\n"
                                      "          DBG_CPU_PC            0x0000_0000_0000_0001\n"
                                      "          DBG_CPU_REG           0x0000_0000_0000_0002\n"
                                      "          DBG_CPU_INSTR         0x0000_0000_0000_0004\n"
                                      "          DBG_CPU_COUNT         0x0000_0000_0000_0008\n"
                                      "          DBG_CP0_INSTR         0x0000_0000_0000_0010\n"
                                      "          DBG_CP1_INSTR         0x0000_0000_0000_0020\n"
                                      "          DBG_CP2_INSTR         0x0000_0000_0000_0040\n"
                                      "\n"
                                      "          DBG_INTR_COMP         0x0000_0000_0000_0100\n"
                                      "          DBG_INTR_VI           0x0000_0000_0000_0200\n"
                                      "          DBG_INTR_SI           0x0000_0000_0000_0400\n"
                                      "          DBG_INTR_SP           0x0000_0000_0000_0800\n"
                                      "          DBG_INTR_PI           0x0000_0000_0000_1000\n"
                                      "\n"
                                      "          DBG_MEMORY            0x0000_0000_0001_0000\n"
                                      "          DBG_RD_RAM            0x0000_0000_0002_0000\n"
                                      "          DBG_RD_REG            0x0000_0000_0004_0000\n"
                                      "          DBG_SP_DMEM           0x0000_0000_0008_0000\n"
                                      "          DBG_SP_IMEM           0x0000_0000_0010_0000\n"
                                      "          DBG_SP_REG            0x0000_0000_0020_0000\n"
                                      "          DBG_SP_PC_REG         0x0000_0000_0040_0000\n"
                                      "          DBG_SP_IBIST_REG      0x0000_0000_0080_0000\n"
                                      "          DBG_DPC_REG           0x0000_0000_0100_0000\n"
                                      "          DBG_DPS_REG           0x0000_0000_0200_0000\n"
                                      "          DBG_MI_REG            0x0000_0000_0400_0000\n"
                                      "          DBG_VI_REG            0x0000_0000_0800_0000\n"
                                      "          DBG_AI_REG            0x0000_0000_1000_0000\n"
                                      "          DBG_PI_REG            0x0000_0000_2000_0000\n"
                                      "          DBG_RI_REG            0x0000_0000_4000_0000\n"
                                      "          DBG_SI_REG            0x0000_0000_8000_0000\n"
                                      "          DBG_PI_DOM2_ADDR1     0x0000_0001_0000_0000\n"
                                      "          DBG_PI_DOM1_ADDR1     0x0000_0002_0000_0000\n"
                                      "          DBG_PI_DOM2_ADDR2     0x0000_0004_0000_0000\n"
                                      "          DBG_PI_DOM1_ADDR2     0x0000_0008_0000_0000\n"
                                      "          DBG_PI_ROM            0x0000_0010_0000_0000\n"
                                      "          DBG_PI_RAM            0x0000_0020_0000_0000\n"
                                      "          DBG_PI_DOM1_ADDR3     0x0000_0040_0000_0000\n"
                                      "\n"
                                      "          DBG_DISP_REFRESH      0x0000_1000_0000_0000\n"
                                      "          DBG_DISP_MODE         0x0000_2000_0000_0000\n"
                                      "\n"
                                      "          DBG_CONTR_PIF         0x0001_0000_0000_0000\n"
                                      "          DBG_CONTR_BUTTONS     0x0002_0000_0000_0000\n"
                                      "\n"
                                      "          DBG_EXC_OV            0x0010_0000_0000_0000\n"
                                      "          DBG_EXC_FPE           0x0020_0000_0000_0000\n"
                                      "\n", stderr);
                        }
                        break;
                        
            
            
                    case '.':
                        if(sscanf(line+1, "%x", &n) == 1)
                        {
                                switch(n)
                                {
                                    case 0:
                                        reg.halt = 0;
                                        break;

                                    case 1:
                                        reg.halt = 1;
                                        break;
                                
                                } /* switch(n) */
                                break;
                                
                        } /* if(sscanf(line+1, "%x", &n) == 1) */
                        
                        fputs("n64_cpu_debugger: Use '. n' set/reset halt state\n"
                              "          n = 0: cpu run\n"
                              "          n = 1: cpu halt\n\n", stderr);
                        
                        break;
                        
            
            
                    case 'G':
                        for(;;)
                        {
#ifdef HALT
                                if(reg.halt)
                                {
                                        reg.halt = 0;
                                        break;
                                }
#endif
                                rs4300i_doCode();
                        }
                        break;


            
#if DISPLAY_SUPPORT
                    case 'r':
                        dispatch(DISPLAY_REFRESH, 0);
                        break;
#endif
            

            
                    case 'b':
                        if(sscanf(line+1, "%lx", &addr) == 1)
                        {
                                breakp[0] += 1;
                                breakp[ breakp[0] ] = addr;
               
                                printf("Breakpoint #%d set @ %08lx\n", (int)breakp[0], breakp[ breakp[0] ]);
                        }
                        else if(line[1] == '-')
                        {
                                if(breakp[0] > 0)
                                {
                                        breakp[0] -= 1;

                                        printf("Breakpoint #%d removed\n", (int)breakp[0] + 1);
                                }
                                else
                                        printf("There is no breakpoint to remove (set one!)\n");
                  
                        }
                        else if(line[1] == 'l')
                        {
                                for(i=1; i<=breakp[0]; i++)
                                        printf("Breakpoint #%d: %08lx\n", i, breakp[i]);
                                putchar('\n');
                        }
                        else
                        {
                                fputs("n64_cpu_debugger: 'b pos' ... sets breakpoint @ pos\n"
                                      "          'b-'    ... remove last breakpoint\n"
                                      "          'bl'    ... lists all breakpoints\n\n'", stderr);
                        }
                        break;
      
      
                    
                    case 't':
                        rs4300i_doCode();
                        break;
            
            
            
                    case 'g':
                        if(line[1] == '-')
                        {
                                if(sscanf(line + 2, "%llx", &dword) == 1)
                                {
                                        while(reg.cpr[0][9] < dword)
                                        {
#ifdef HALT
                                                if(reg.halt)
                                                {
                                                        reg.halt = 0;
                                                        break;
                                                }
#endif
                                                rs4300i_doCode();
                                                for(j=1; j<=breakp[0]; j++)
                                                        if(reg.pc == breakp[j])
                                                        {
                                                                printf("\nBreakpoint #%d @ %08lx ([count]=%016llx) reached\n\n", j, breakp[j], reg.cpr[0][COUNT]);
                                                                dword = 0;
                                                                break;
                           
                                                        } /* if(reg.pc == breakp[j]) */
                        
                                        } /* while(reg.cpr[0][9] < dword) */
                  
                                } /* if(sscanf(line + 2, "%llx", &dword) == 1) */
               
                                else
                                        fputs("n64_cpu_debugger: g  x ... executes x instructions\n"  
                                              "          g- x ... executes instructions until count == x\n\n", stderr);
                        }
            
                        else
                        {
                                if(sscanf(line + 1, "%llx", &dword) == 1)
                                {
                                        for(i=0; i<dword; i++)
                                        {
#ifdef HALT
                                                if(reg.halt)
                                                {
                                                        reg.halt = 0;
                                                        break;
                                                }
#endif
                                                rs4300i_doCode();
                                                for(j=1; j<=breakp[0]; j++)
                                                        if(reg.pc == breakp[j])
                                                        {
                                                                printf("\nBreakpoint #%d @ %08lx ([count]=%016llx) reached\n\n", j, breakp[j], reg.cpr[0][COUNT]);
                                                                dword = 0;
                                                                break;
                           
                                                        } /* if(reg.pc == breakp[j]) */
                                
                                        } /* for(i=0; i<dword; i++) */

                                } /* if(sscanf(line + 1, "%llx", &dword) == 1) */
                                else
                                        fputs("n64_cpu_debugger: g  x ... executes x instructions\n"  
                                              "          g- x ... executes instructions until count == x\n\n", stderr);

                        } /* if(line[1] != '-') */
            
                        break;
            
                    
                    
                    case 'l':
                        switch(line[1])
                        {
                            case 'w':
                                if(sscanf(line + 2, "%lx", &addr) == 1)
                                {
                                        printf("%lx: %08lx\n", addr, doReadMemWord(addr));
                                }
                                break;
                            case 'd':
                                if(sscanf(line + 2, "%lx", &addr) == 1)
                                {
                                        printf("%lx: %016llx\n", addr, doReadMemDoubleWord(addr));
                                }
                                break;
                            case 'h':
                                if(sscanf(line + 2, "%lx", &addr) == 1)
                                {
                                        printf("%lx: %04hx\n", addr, doReadMemHalfWord(addr));
                                }
                                break;
                            case 'b':
                                if(sscanf(line + 2, "%lx", &addr) == 1)
                                {
                                        printf("%lx: %02x\n", addr, doReadMemByte(addr));
                                }
                                break;
                        }
                        break;
                    

                    
                    case 's':
                        switch(line[1])
                        {
                            case 'w':
                                if(sscanf(line+2, "%lx %lx", &addr, &word) == 2)
                                {
                                        printf("%08lx <- %08lx\n", addr, word);
                                        doWriteMemWord(word, addr);
                                }
                                break;
                    
                            case 'd':
                                if(sscanf(line+2, "%lx %llx", &addr, &dword) == 2)
                                {
                                        printf("%08lx <- %016llx\n", addr, dword);
                                        doWriteMemDoubleWord(dword, addr);
                                }
                                break;
                    
                            case 'h':
                                if(sscanf(line+2, "%lx %hx", &addr, &hword) == 2)
                                {
                                        printf("%08lx <- %04hx\n", addr, hword);
                                        doWriteMemHalfWord(hword, addr);
                                }
                                break;
                    
                            case 'b':
                                if(sscanf(line+2, "%lx %hx", &addr, &hword) == 2)
                                {
                                        printf("%08lx <- %02x\n", addr, (BYTE)hword);
                                        doWriteMemByte((BYTE)hword, addr);
                                }
                                break;
                        }

                        break;
            

            
         
                    case 'm':
                        if(sscanf(line + 1, "%lx %d", &addr, &n) == 2)
                        {
                                for(i=0; i<n; i++)
                                {
                                        printf("%02x%s", doReadMemByte(addr + i), ((i%16) == 15)?"\n":((i%4) == 3)?" ":"");
                                }
                                
                                if((n%16) != 0)
                                        putchar('\n');
                                
                        }
                        else
                                puts("n64_cpu_debugger: 'm addr length'\n"
                                     "                  prints length bytes of n64 mem starting @ addr\n");
                        break;


         

         
         case 'x':
            switch(line[1])
            {
               case 't':
                  puts("TLB:\n");
                  for(i=0; i<32; i++)
                  {
                     printf("reg.tlb[%02d].hh = %08lx\n", i, reg.tlb[i].hh);
                     printf("reg.tlb[%02d].hl = %08lx\n", i, reg.tlb[i].hl);
                     printf("reg.tlb[%02d].lh = %08lx\n", i, reg.tlb[i].lh);
                     printf("reg.tlb[%02d].ll = %08lx\n", i, reg.tlb[i].ll);
                     printf("reg.tlb[%02d].g  = %1x\n\n",   i, reg.tlb[i].g);
                  }
                  break;
               case 'p':
                  printf("[pc] = %08lx\n", reg.pc);
                  break;
               case 'r':
                  rs4300i_printCPURegister();
                  break;
               case '0':
                  rs4300i_printCP0Register();
                  break;
               case '1':
                  rs4300i_printCP1Register();
                  break;
               case '2':
                  rs4300i_printCP2Register();
                  break;
               case 'm':   /* all 'doReadMemXXXX()' have to be changed to access the mem direct! */
                  puts("RDRAM Registers (0x03f0_0000 .. 0x03ff_ffff)");
                  for(i=0; i<=0x24; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff03f00000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  puts("SP CP0 Registers (0x04040000)");
                  for(i=0; i<=0x1c; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04040000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  putchar('\n');
                  puts("DP Command Registers (0x04100000)");
                  for(i=0; i<=0x1c; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04100000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  putchar('\n');
                  puts("DP Span Registers (0x04200000)");
                  for(i=0; i<=0x0c; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04100000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  puts("MIPS Interface (MI) Registers (0x04300000)");
                  for(i=0; i<=0x0c; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04300000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  puts("Video Interface (VI) Registers (0x04400000)");
                  for(i=0; i<=0x34; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04400000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  puts("Audio Interface (AI) Registers (0x04500000)");
                  for(i=0; i<=0x14; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04500000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  puts("Peripheral Interface (PI) Registers (0x04600000)");
                  for(i=0; i<=0x30; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04600000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  puts("RDRAM Interface (RI) Registers (0x04700000)");
                  for(i=0; i<=0x1c; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04700000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  putchar('\n');
                  puts("Serial Interface (SI) Registers (0x04800000)");
                  for(i=0; i<=0x18; i+=4)
                  {
                     printf("%08lx ", doReadMemWord(0xffffffff04800000ULL+i));
                     if(!((i+4)%32))
                        putchar('\n');
                  }
                  puts("\n");
                  break;
               default:
                  printf("use: xr, x0, x1, x2, xm or xp!\n");
                  break;
                        }
                        break;
         
                }
        }
        while(line[0] != '!');



        dispatch(SHUTDOWN, 0);

} /* void n64_cpu_debugger() */





void rs4300i_printCPURegister()
{
        int i;


        for(i=0; i<16; i++)
        {
                printf("r[%2d]=%016llx  ", i,    reg.r[i]);
                printf("r[%2d]=%016llx\n", i+16, reg.r[i+16]);
        }

        printf(" [hi]=%016llx   [lo]=%016llx\n", reg.hi, reg.lo);

        printf(" [pc]=%08lx\n\n", reg.pc);
}





void rs4300i_printCP0Register()
{
        int i;


        for(i=0; i<16; i++)
        {
                printf("cpr[0][%2d]=%016llx  ", i,    reg.cpr[0][i]);
                printf("cpr[0][%2d]=%016llx\n", i+16, reg.cpr[0][i+16]);
        }
}





void rs4300i_printCP1Register()
{
        int i;


        for(i=0; i<16; i++)
        {
                printf("f[%02d]=%016llx  ", i,    reg.cpr[1][i]);
                printf("f[%02d]=%016llx\n", i+16, reg.cpr[1][i+16]);
        }

        printf("ccr[1][00] =  %08lx  ccr[1][31] =  %08lx\n", reg.ccr[1][0], reg.ccr[1][31]);
}





void rs4300i_printCP2Register()
{
        int i;


        for(i=0; i<16; i++)
        {
                printf("cpr[2][%2d]=%016llx  ", i,    reg.cpr[2][i]);
                printf("cpr[2][%2d]=%016llx\n", i+16, reg.cpr[2][i+16]);
        }
}





