第五章 软件层之AGDI接口(2)

第五章 软件层之AGDI接口(2),第1张

        上一章节我们已经了解了uVision2 IDE平台的大体布局和一些功能(包括下载程序、进入仿真、单步执行、停止运行等等),在这一章节中我们就来详细介绍uVision2提供的这些功能对应的AGDI接口函数。

        AGDI是Keil提供的一组API,里面包含了对上面说到的下载程序和仿真调试的所有接口函数,它是用来驱动在线调试器的。

AGDI接口独立于处理器的体系结构,所以能很方便的实现驱动的开发并且带来很小的开销。

我们通过该接口编写自己的目标驱动dll来连接调试器和Keil IDE。

在工程中的AGDI文件中,所有以AG_开头的函数都需要在目标驱动dll中被定义,不过也可以是一个空函数,他们用于实现了调试器的各项功能。

接口函数功能描述
AG_Init()AGDI的初始化函数
AG_MemAtt()内存访问函数
AG_BpInfo()断点设置/重置/使能/失能/清除
AG_BreakFunc()断点访问函数

AG_GoStep()

运行/停止命令
AG_Serial()读/写窗口函数
AG_MemAcc()访问被调试的单片机的内存函数
AG_RegAcc()读/写单个寄存器的函数
AG_AllReg()读/写所有寄存器的函数
AG_HistFunc()追踪历史 *** 作的函数

AG_Init() :AGDI的初始化,不管是下载程序还是进入仿真,Keil IDE一定会调用AG_Init这个函数很多次来初始化一些指针和参数,AG_Init的部分代码如下:


/*----------- AGDI Basic Functions -----------------*/
/*AGDI-Init Function*/
U32 _EXPO_ AG_Init (U16 nCode, void *vp)  {
  U32     nE;
  nE = 0;
  switch (nCode & 0xFF00)  {
    case AG_INITFEATURES:     // Initialize & start the target
      PlayDead = 0;           // clear some variables...
      //---We don't have special features here, so all variables are cleared.
      //---uVision2 will query these features lateron.
      //---Note: the 'supp' structure is defined in 'AGDI.H'
      supp.MemAccR  = 0;      // memory-access while running
      supp.RegAccR  = 0;      // register-access while running
      supp.hTrace   = 0;      // trace support
      supp.hCover   = 0;      // code coverage support
      supp.hPaLyze  = 0;      // Performance-Analyzer support
      supp.hMemMap  = 0;      // Memory-Map support
      supp.ResetR   = 0;      // Reset while running support
      //---Note: if InitTarget() fails, then set nE=1, else nE=0.
      //         if 1 is returned, then uVision will cancel using this driver
      FlashProg = 0; 
      InitMem();                 // init Cache memory
      bootflag = 1;
      nE       = InitTarget();   // Initialize your target...
      bootflag = 0;
      if (nE == 0)  {            // initialization was Ok.
        InitRegs();              // define register layout for RegWindow
      }

      if (nE == 0)  {            // everything is ok so far
        pCbFunc (AG_CB_EXECCMD, "U $\n");  // force disassemble @curPC
      }
      NumRecs = 0;                // clear 'number of trace records'
      break;

    case AG_INITITEM:             // init item
      switch (nCode & 0x00FF)  {
        case AG_INITMENU:         // init extension menu
          *((DYMENU **) vp) = (DYMENU *) Menu;
          break;
        case AG_INITEXTDLGUPD:    // init modeless extesion dlg update function
          *((UC8 **) vp) = (UC8 *) DlgUpdate;
          break;
        case AG_INITMHANDLEP:     // setup ptr to HWND of active modeless dlg
          pHwnd = (HWND *) vp;
          break;
        case AG_INITPHANDLEP:     // pointer to parent handle (MainFrame)
          hMfrm = (HWND) vp;
          break;
        case AG_INITINSTHANDLE:   // pointer to Agdi-instance handle
          hInst = (HMODULE) vp;
          break;
        case AG_INITBPHEAD:       // pointer to head of Bp-List
          pBhead = (AG_BP **) vp;
          break;
        case AG_INITCURPC:        // pointer to program counter
          pCURPC = (UL32 *) vp;
          break;
        case AG_INITDOEVENTS:     // DoEvents function pointer
        //--- this is no longer relevant due to uVision's threading.
          break;
        case AG_INITUSRMSG:       // Registered Message for SendMessage
          Uv2Msg = (DWORD) vp;    // (Serial-Window, TextOut messages)
          break;
        case AG_INITCALLBACK:     // pointer to callback function
          pCbFunc = (pCBF) vp;    // call-back function of s166
          break;
        case AG_INITFLASHLOAD:    // Prepare for Flash Download
          nE = InitProgrammer ();
          if (nE) break;
          nE = InitDevFlash ();   // Initialize Flash, Erase
          break;
        case AG_STARTFLASHLOAD:   // S8051 says 'Ready for Flash DownLoad'
          nE = FlashLoad();       // Flash-Download, write, verify
          break;
      }
      break;

    case AG_GETFEATURE:           // uVision2 want's details about features...
      switch (nCode & 0x00FF)  {
        case AG_F_MEMACCR:    nE = supp.MemAccR; break;
        case AG_F_REGACCR:    nE = supp.RegAccR; break;
        case AG_F_TRACE:      nE = supp.hTrace;  break;
        case AG_F_COVERAGE:   nE = supp.hCover;  break;
        case AG_F_PALYZE:     nE = supp.hPaLyze; break;
        case AG_F_MEMMAP:     nE = supp.hMemMap; break;
        case AG_F_RESETR:     nE = supp.ResetR;  break;
      }
      break;

    case AG_EXECITEM:             // execute various commands
      switch (nCode & 0x00FF)  {
        case AG_UNINIT:           // Clean up target system settings
          ExitProgrammer ();      // Stop Programmer
          PlayDead = 1;           // mark target as disconnected.
          CloseAllDlg();          // close all open dialogs
          StopTarget();           // shutdown target & communication
          WriteMonParms (pdbg->TargArgs);  // update argument string
          break;

        case AG_RESET:            // perform a reset on the target system
          if (PlayDead || hMfrm == NULL) break;  // target is disconnected
          ResetTarget();          // reset the target
          pCbFunc (AG_CB_EXECCMD, "U $\n");      // dasm $
          NumRecs = 0;            // clear 'number of trace records'
          break;
      }
      break;
  }

  if (PlayDead)  {       // driver is disconnected
    StopTarget();        // shut down driver
    PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);  // unload this driver
  }

  return (nE);
}

        根据小编在开发的过程中发现Keil在下载程序和进入仿真这两个过程调用AG_Init时参数nCode的值和顺序均不同,两者的调用顺序分别为:

        在下载的过程中,AGDI会通过上面这些调用顺序分别调用函数 “InitProgramme()”、“InitDevFlash()”、“FlashLoad()” 这三个函数需要我们来实现的,主要是对目标单片机中的主控芯片进行程序的烧录的初始化和少些过程。

        在进入仿真的过程中,AGDI会通过上面这些调用顺序,调用 ResetTarget() 函数,这个函数和我们后面点击程序Reset时候调用的函数接口是同一个,所以在 ResetTarget() 函数里面要做好区分这两者的标志。

        AG_BpInfo() :  这个接口是做断点的设置/重置/使能/失能/清除的 *** 作。

当我们在要调试的工程中设置断点或者清除断点的时候均会调用到这个接口,并且nCode的参数会根据我们的 *** 作进行赋值。


U32 _EXPO_ AG_BpInfo (U16 nCode, void *vp)  {
  U32     nA, nAdr;
  U16    *pB;
  AG_BP  *pK;

  if (PlayDead) return (0);
  switch (nCode)  {
    case AG_BPDISALL:                  // disable all Exec-Bp's
    case AG_BPKILLALL:                 // kill all Exec-Bp's
      break;
    default:                           // need attribute pointer anyway, set it up here.
      nAdr = ((GADR *) vp)->Adr;       // the referred address
      pB   = MGetAttr (nAdr);          // get attribute location for 'nAdr'
      if (!pB) return (0x80000000);    // failed: unmapped address
      break;
  }

  nA = 0;
  switch (nCode)  {                    // Function code
    case AG_BPQUERY:                   // Query Break
    case AG_BPEXQUERY:                 // Query for 'executed' attribute
      nA = (UL32) (*pB & __BPQX);      // Attributes are similar to uVision
      break;

    case AG_BPENABLE:                               // enable breakpoint
      if (*pB & AG_ATR_BPDIS)  {                    // is Bp disabled ?
        *pB = (*pB & ~AG_ATR_BPDIS) | AG_ATR_BREAK; // enable it
      }
      break;

    case AG_BPDISABLE:                              // disable breakpoint
      if (*pB & AG_ATR_BREAK)  {                    // is Bp enabled ?
        *pB = (*pB & ~AG_ATR_BREAK) | AG_ATR_BPDIS; // disable it
        break;
      }
      break;

    case AG_BPKILL:                                 // kill breakpoint
      *pB = *pB & ~(AG_ATR_BREAK | AG_ATR_BPDIS);
      break;

    case AG_BPSET:                                  // Set breakpoint
      *pB = (*pB & ~AG_ATR_BPDIS) | AG_ATR_BREAK;
      break;

    case AG_BPDISALL:                  // disable all Bp's
      pK = *pBhead;                    // head of breakpoint list
      for ( ; pK ; pK = pK->next )  {
        if (pK->type != AG_ABREAK || !pK->enabled)  {
          continue;
        }
        ++nA;                          // count number of changed Bp's
        pB = MGetAttr (pK->Adr);             // get attribute location
        if (pB && (*pB & AG_ATR_BREAK))  {   // is Bp enabled ?
          *pB = (*pB & ~AG_ATR_BREAK) | AG_ATR_BPDIS; // disable it
        }
      }
      break;

    case AG_BPKILLALL:                 // kill all Bp's
      pK = *pBhead;                    // head of breakpoint list
      for ( ; pK ; pK = pK->next )  {
        if (pK->type != AG_ABREAK) continue;
        ++nA;                          // count number of changed Bp's
        pB = MGetAttr (pK->Adr);       // get attribute location
        if (pB)  {
          *pB = *pB & ~(AG_ATR_BREAK | AG_ATR_BPDIS);
        }
      }
      break;
  }

  if (PlayDead == 1)  {
    StopTarget();
    PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);
  }
  return (nA);
}

        AG_GoStep()  : 这个接口主要是实现单步执行、多步执行、运行断点、跳出函数、单行运行、运行到光标处、停止调试等 *** 作。

例如当我们点击单步执行的时候,这个接口中的nCode参数就会设置为AG_NSTEP,并且nSteps参数会被设置为当前指令的条数(也就是当前C语言语句编译成汇编语言的指令条数)。


/*
 * Go/Step/Stop commands
 */

_EXPO_ U32 AG_GoStep (U16 nCode, U32 nSteps, GADR *pA)  {
  U32     nE;

  if (PlayDead) return (0);           // driver is disconnected.

  nE = 0;                             // clear error code
  switch (nCode)  {
    case AG_STOPRUN:                  // Stop Go/Step.
      StopRun = 1;
      nE = StopExec();                // nE:  1=Stopped, 0=executing
      if (nE == 1) iRun = 0;          // stopped.
      if (!iRun)  {
        Invalidate();                 // registers, caches, etc.
      }
      NumRecs = 0;                    // clear 'number of trace records'
      break;

    case AG_NSTEP:                    // execute 'nSteps' instruction steps
      iRun = 1;                       // 'executing'
      for ( ; nSteps != 0 ; --nSteps )  {
        if (StopRun) break;           // Stop-Button was pressed
        if (Step() != 0x01) break;    // 0x01 means 'Ok.'
      }
      StopRun = 0;                    // clear Stop-Button flag.
      Invalidate();                   // registers, caches, etc.
      iRun = 0;                       // clear 'executing' flag
      NumRecs = 0;                    // clear 'number of trace records'
      break;

    case AG_GOTILADR:                 // run til 'pA->Adr' or some Bp,
      iRun = 1;                       // whichever comes first
      GoUntil (pA->Adr);
      StopRun = 0;
      Invalidate();                   // registers, caches, etc.
      iRun = 0;                       // clear 'executing' flag
      NumRecs = 0;                    // clear 'number of trace records'
      break;

    case AG_GOFORBRK:                 // run forever or till some Bp reached.
      iRun = 1;
      GoUntil (0xFFFFFFFF);           // means 'go forever'
      StopRun = 0;
      Invalidate();                   // registers, caches, etc.
      iRun = 0;                       // clear 'executing' flag
      NumRecs = 0;                    // clear 'number of trace records'
      break;
  }

  if (PlayDead)  {                    // target not connected
    StopTarget();                     // shut it down
    PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0); // terminate ourselves
  }
  else  {
    GetRegs();                        // get target registers
    *pCURPC = REG51.nPC;              // let uVision2 know about PC...
  }
  return (nE);
}

        AG_MemAcc() : 这个接口实现了我们在调试的过程中读取/修改某个变量的值,或者读取整个数据存储区的数据。

从代码中可以看到,在读取或者写入的函数参数都是三个,分别是nA、pB、nMany,其中nA指的是要读取/写入的变量的地址,pB指的是要存放读取变量的空间(或者修改该变量的新的数值),nMany指的该变量数值的字节数。

例如,当我们要读取代码中变量 i 的值,可以将鼠标移动到变量 i 的位置,这时候其实就已经触发到这个接口了,这个接口会将变量 i 的地址nA和这个变量的字节数nMany(假如变量 i 定义为UNIT32的话,就是四个字节,这时候nMany就等于4),然后将读取后的数值放置到pB管道中,Keil会读取这管道显示到界面中。

/*
 * Access target memory
 */

U32 _EXPO_ AG_MemAcc (U16 nCode, UC8 *pB, GADR *pA, UL32 nMany)  {
  U16     nErr;
  UL32      nA;

  if (iRun && !supp.MemAccR)  {     // currently running, can't access.
    return (AG_NOACCESS);           // error: can't access register
  }
  if (PlayDead)  {                  // driver disconnected.
    return (AG_NOACCESS);
  }

  nErr = 0;
  if (hMfrm == NULL) return (nErr);

  nA = pA->Adr;
  switch (nCode)  {
    case AG_READ:                   // need 'read' permission
      pA->ErrAdr = ReadMem (nA, pB, nMany);
      if (pA->ErrAdr) nErr = AG_RDFAILED;
      break;

    case AG_WRITE:                  // need 'write' permission
      pA->ErrAdr = WriteMem (nA, pB, nMany);
      if (pA->ErrAdr) nErr = AG_WRFAILED;
      break;
                                    // used for Program download
    case AG_WROPC:                  // need 'Read/Execute' permissions
      nA |= (amCODE << 24);
      pA->ErrAdr = WriteMem (nA, pB, nMany);
      if (pA->ErrAdr) nErr = AG_WRFAILED;
      break;
                                    // used for disassembly etc.
    case AG_RDOPC:                  // need 'Read/Execute' permissions
      nA |= (amCODE << 24);
      pA->ErrAdr = ReadMem (nA, pB, nMany);
      if (pA->ErrAdr) nErr = AG_RDFAILED;
      break;

    default:
      nErr = AG_INVALOP;            // invalid Operation
      break;
  }

  if (PlayDead == 1) {   // disconnected...
    StopTarget();        // shut down driver
    PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);
  }
  return (nErr);
}

        AG_RegAcc() : 这个接口是读取单片机芯片的内置寄存器的数值,包括R0~R7、psw等等。

/*
 * Read/Write a single Register
 */

U32 _EXPO_ AG_RegAcc (U16 nCode, U32 nReg, GVAL *pV)  {
  U16    nErr;

  if (iRun && !supp.RegAccR)  {     // currently running, can't access.
    return (AG_NOACCESS);           // error: can't access register
  }
  if (PlayDead) return (AG_NOACCESS);  // driver is disconnected.

  nErr = 0;
  switch (nCode)  {                 // which Opcode
    case AG_READ:                   // read register
      GetRegs();                    // make sure REG51 is up to date
      switch (nReg)  {
        case nnR0: case nnR1: case nnR2: case nnR3:
        case nnR4: case nnR5: case nnR6: case nnR7:
          pV->uc = REG51.Rn [nReg & 0x07];
          break;
        case nrA:
          pV->uc = REG51.acc;
          break;
        case nrDPTR:
          pV->u16 = (((WORD16) REG51.dph) << 8) | (WORD16) REG51.dpl;
          break;
        case mPC:
          pV->u32 = REG51.nPC;      // Note: requires 'amCODE << 24' selector.
          break;
      }
      break;

    case AG_WRITE:
      switch (nReg)  {
        case nnR0: case nnR1: case nnR2: case nnR3:
        case nnR4: case nnR5: case nnR6: case nnR7:
          REG51.Rn [nReg & 0x07] = pV->uc;
x:        SetRegs (®51);
          break;
        case nrA:
          REG51.acc = pV->uc;
          goto x;
        case nrDPTR:
          REG51.dpl = (BYTE) (pV->u16 & 0xFF);
          REG51.dph = (BYTE) ((pV->u16 >> 8) & 0xFF);
          goto x;
        case mPC:
          curPC = pV->u32;
          WritePC (curPC);          // to target
          break;
      }
      break;

    default:
      nErr = AG_INVALOP;            // invalid Operation
      break;
  }
  if (PlayDead == 1)  {
    StopTarget();
    PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);
  }
  return (nErr);
}

        上面讲的就是AGDI常用的接口啦。

        OK啦,这一章就介绍到这里了~~

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/673521.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存