1 问题的提出
随着经济的高速发展 ,社会信息化程度越来越高 ,互联网和多媒体技术的进步 ,人们对便携式移动信息处理设备的需求也日益迫切。掌上电脑和PDA(PersonalDigitalAssistant)等手持设备的出现正迎合了这种需求。因此 ,近年来手持设备正在迎来它的高速成长期 ,这就使得在这些手持设备上开发应用程序成为软件开发领域的又一热点。程序员越来越多的在PDA设备上做程序开发 ,需要有一个好的调试器的支持 ,否则 ,整个系统的开发将极其困难。一般来说 ,每一类手持设备有其特定的微处理器 ,其上运行的应用程序要根据这种微处理器的指令系统来设计。所以 ,针对不同的微处理器的PDA设备 ,需要不同的调试器。目前 ,广泛采用的是硬件仿真调试器。这一类仿真器是针对不同的微处理器专门设计的。价格昂贵 ,通用性差 ,如果需要多人同时开发程序的话 ,代价是很大的。于是 ,我们很自然地想到使用软件调试器。但是 ,PDA设备上资源有限 ,应用程序的调试工作不可能在这类设备上完成。而PC机相对于PDA设备而言 ,资源丰富 ,操作简单 ,可以作为一个非常好的PDA程序的调试平台。所以 ,我们最终决定在PC机上实现PDA设备的仿真调试器。
本文介绍的仿真调试器是直接在PC机上建立的一个软仿真调试平台。较之许多微处理器生成厂家提供的由硬件和软件共同构成的仿真执行平台 ,使用简便 ,运行稳定 ,开发成本低。经过分析和测试 ,这种“纯软件”的仿真调试器 ,可以很方便地完成大部分的应用程序的开发调试工作。这为高效的开发PDA上的应用软件提供了可能。
2 总体设计思想
我们需要的仿真调试器不是单纯的应用逻辑的调试 ,而是整个设备运转情况的调试 ,所以 ,在这个调试器中 ,除了要能仿真目标程序的运算逻辑的执行 ,还要能仿真输入和输出。运算逻辑仿真的主要任务是模拟指令系统对寄存器、内存数据和CPU标志的影响 ,其核心是跨硬件平台的指令系统的仿真。输入和输出是与具体的外部设备相关的操作 ,在一般的计算机设备中 ,这类设备的运行和整个系统运行的协调工作大多是通过微处理器的中断机制来实现的。所以 ,要完成输入和输入的仿真 ,其核心是实现中断机制的仿真。

从图 1可以看出 ,整个仿真调试器的核心是由指令仿真执行单元和外部事件处理单元组成的。其中 ,指令仿真执行单元主要完成普通运算逻辑仿真 ,它主要影响寄存器、CPU标志、主存储器内容、视频缓冲区内容、外设端口数据等。外部事件处理单元主要完成中断机制仿真 ,它负责把有关的外部事件映射成 PDA的某一类型中断 ,并调用中断处理程序执行 ,以模拟PDA设备自身产生的中断。
3 关键技术的实现方法
3 1 指令系统的仿真
指令系统的仿真是整个仿真调试器的关键 ,它需要真实地模拟出指令执行的过程和由此引起的各种变化。其中包括指令反汇编、寄存器仿真、主存储器仿真、外设端口仿真、标志位仿真以及后续指令地址的计算等等。在所述这些问题中 ,对于数据变化的仿真相对较容易 ,对控制过程的仿真相对较困难。另外 ,本文是以ADZ80芯片的仿真为例的 ,如果更换一种芯片 ,只需将指令仿真执行单元中的机器指令解码函数进行修改就可以了。这个函数实际上只是一个查表分支函数 ,编程复杂性较低 ,我们已经针对几种常用的芯片设计了相应的替换函数。实践证明 ,靠替换这个函数生成其他芯片的仿真系统的方法是便捷可行的。
3 1 1 目标代码的正向和逆向反汇编
要调试目标程序 ,首先应对二进制目标代码进行反汇编 ,其目的是让程序员能以汇编形式来阅读、分析和调试程序。反汇编的实现过程如图 2所示。
反汇编过程的入口参数是要反汇编的机器指令所在代码段的地址 ,出口参数是反汇编缓冲区内的一串字符 ,其中包含了该条指令的地址、机器指令和汇编指令。指令解释函数由一个大的分支语句组成 ,每一个分支代表一条或一组相似的机器指令的代码解释函数。反汇编函数取得指令首字节后 ,便执行一个选择过程 ,在这个过程中解释一条完整的指令 ,实现程序的反汇编。代码解释函数。反汇编函数取得指令首字节后 ,便执行一个选择过程 ,在这个过程中解释一条完整的指令 ,实现程序的反汇编。

上述的反汇编过程是按照目标代码字节流从前往后的顺序进行的 ,称为正向反汇编。我们为了减小系统的开销 ,没有保留反汇编的结果 ,而是实行动态反汇编。所以 ,当调试过程中需要往回查看某些代码时 (也即逆向反汇编 ) ,就存在机器代码解释歧义性的问题。为了快速、高效地实现逆向反汇编 ,我们加入了一张索引表 ,记录每条机器指令首字节在整个代码段中的偏移。逆向反汇编时 ,只要得到目标位置的下标量 ,就可以直接定位到该条指令的首字节在代码段中的偏移。由此彻底解决了逆向反汇编歧义性的问题。
3 1 2 断点的控制
为了方便用户调试程序 ,仿真器可以让用户设置断点。断点的类型可以分为指令型和数据型。所谓指令型断点即程序执行到某条指令时停止运行 ,等待用户干预 ;所谓数据型断点即程序对某一地址的存储器或外设端口进行读、写访问时 ,根据用户条件决定是否停止运行。用户设置的指令型断点信息记录在Break数组中 ,有数据型断点条件信息记录在这样一个结构数组中 :
structBREAKTYPE{
ByteCodeType ;∥ 0:读访问 ;1 写访问 ;2 读写访问
ByteAddrType ;∥ 0:数据段访问 ;1 端口访问 ;2 两者均有
WordAddr1;∥访问的数据段地址
WordAddr2;∥访问的端口地址
} ;
无条件的断点无需额外的检查 ,在每条指令执行之前 ,如果Break数组对应偏移做过设置 ,则停止当前指令的执行。条件断点需要额外的检查 ,但要检查的并不是所有指令 ,而是一些牵涉到数据段访问和端口访问的指令。用户每做一个条件断点都被记录在上述结构数组中 ,用一个布尔变量来控制此条指令的执行是否要进行条件断点的检查 ,为真的时候检查 ,否则正常地执行指令。检查后确认为条件断点的指令 ,立即返回 ,并停止指令的执行。
3 1 3 标志位的改变
大多数指令会修改标志寄存器的值 ,如果人工操作标志寄存器值 ,繁琐且容易出错。这个仿真调试器运行在 8 0x8 6平台上 ,被仿真的是ADZ8 0。使用 8 0x8 6指令仿真ADX8 0指令 ,指令的执行自然地影响到 8 0x8 6的标志寄存器 ,而且 8 0x8 6的标志是ADZ8 0标志的超集。因此类似的定义一个 8 0x8 6标志寄存器结构类型和联合类型 ,将 8 0x8 6标志寄存器的值映射到ADZ8 0的有关标志位。 在仿真过程中 ,每条指令执行前将ADZ8 0标志寄存器结构变量的值赋给 8 0x8 6标志寄存器结构变量 ,指令执行完毕 ,再反过来将 8 0x8 6标志寄存器结构变量的值赋给ADZ8 0标志寄存器结构变量。这样就很方便、准确地实现了标志位的改变。
3 2 显示输出的仿真
对于大多数手持设备而言 ,LCD是主要的显示部件。一般PDA设备的CPU的都是通过一组寄存器来控制LCD的显示 ,其中主要包括LCD视频缓冲区首地址寄存器、视频行数寄存器、视频列数寄存器、LCD刷新频率寄存器、灰度映像寄存器等。通过改变上述寄存器的值和视频缓冲区中的数据 ,可以使程序员 控制LCD产生所需的画面。图 3显示了 4层灰度显示模式下 ,视频缓冲区中的数据与屏幕画面之间的关系。

图 3 视频缓冲区数据与屏幕画面的映射关系
从图 3可以看出 , 4层灰度模式下 ,视频缓冲区中每 2位对应屏幕上 1点。如果是单色模式的话 ,视频缓冲区中每 1位就对应屏幕上 1点 ,这样视频缓冲区的容量可以减少 50%。在我们的仿真器中 ,根据视频缓冲区的尺寸 ,准备了一个数据缓冲区 ,并把缓冲区的首地址定义为LCD视频缓冲区首地址寄存器的值。如果用户程序中出现某些指令向视频缓冲区输出内容的话 ,数据就会写入到我们准备的模拟缓冲区中。同时 ,我们编写了一个根据相关参数 ,将视频缓冲区内容描绘到一个对话框上的通用显示函数。在一般的PDA中 ,对屏幕画面的刷新主要有两种情况 :一是由PDA的时钟信号触发的定时刷新 ;二是由用户程序产生的强制刷新。第一种情况是通过系统产生的定时器信号来模拟的 ,一旦产生定时消息 ,我们就调用显示函数 ,刷新对话框上的画面。第二种情况下 ,用户强制刷新信号一般是通过修改某一个特殊的端口寄存器来产生的。于是 ,我们使用指令系统仿真中的加入断点的手段 ,为这个特殊的端口寄存器加入一个访问断点 ,一旦用户程序访问这个寄存器 ,我们就根据寄存器中的最新数据决定是否刷新屏幕画面。
3 3 输入的仿真
PDA设备由于受体积、功耗、重量和成本等因素的影响 ,不能配备复杂的通用输入设备。如鼠标、键盘等 ,这就决定了PDA设备不可能采用和普通PC计算机一样的输入方式来输入信息。现在的PDA设备一般都采用触摸屏加手写笔的方法来实现信息的输入。用户通过手写笔在安装有触摸屏的LCD表面上进行点击、拖动等简单的动作 ,然后PDA再通过有关识别软件来识别用户的输入 ,以此完成用户向PDA的输入过程。从上面的叙述中可以看出 ,要完成输入的仿真 ,主要是完成PDA对手写笔的点击、拖动等动作所作出的反应的仿真。PDA的硬件系统一般会为触摸屏提供一个或多个专门的中断。就我们使用的ADZ80而言 ,它提供了两个中断 ,一个用于表示手写笔点击到触摸屏上 ,另一个用于表示手写笔离开触摸屏。同时 ,它还提供了两个寄存器 ,用于记录手写笔当前位置的坐标。现在的关键是中断的产生如何来仿真 ?我们通过分析发现 ,手写笔所完成的动作与PC计算机的鼠标所完成的动作十分相似 ,而由手写笔所产生的中断与Windows中的消息机制十分类似。所以 ,我们通过实验 ,最后采用了如下的方案 :
一、把用于仿真显示的对话框 ,用作模拟的触摸屏 ,把发生在它上面的鼠标事件看作发生在真实触摸屏上的手写笔中断 ;
二、为模拟屏上的鼠标LeftButtonDown事件编写一个事件处理函数 ,这个函数负责在系统中作一个手写笔点击到触摸屏上的标记 ;
三、为模拟屏上的鼠标LeftButtonUp事件编写一个事件处理函数 ,这个函数负责在系统中作一个手写笔离开触摸屏的标记 ;
四、为模拟屏上的鼠标MouseMove事件编写一个事件处理函数 ,如果鼠标在模拟屏范围内移动 ,则该函数负责把鼠标的当前位置记录外设端口模拟数组的相应位置中 ;
五、在仿真指令执行的过程中 ,当每完成一条指令 ,就查看系统中的手写笔中断标记是否有效 ,如果有效 ,则暂时中断当前程序的执行 ,转向中断处理程序执行。中断处理程序就可以从模拟的端口上得到当前手写笔的位置信息了。
通过上述的方法 ,可以成功地模拟PDA设备上手写笔和触摸屏通过中断与PDA核心程序配合工作的情况。
4 小 结
本文介绍了一个完全通过软件模拟的方法设计一个用于PDA等手持设备开发的仿真调试器的方案。对它的需求产生于我们对实际产品开发过程中 ,对它的设计完全出于可靠和实用的角度 ,对它的检验则是将它运用于产品开发过程中的大量程序的调试。实践证明 ,这一方案是简便的、可行的、高效的 ,同时 这一系统具有通用性强、使用成本低的优点。随着手持设备的普及 ,手持设备产品的开发会成为IT产业发展的又一个热点。我们相信这篇文章对于跨平台的仿真系统的设计以及手持设备产品的开发都具有重要的实践意义。 |