MON51通信协议和实现分析v1.2
---------基于SST SoftICE代码
(大约是08年的工作和文章, 今天整理翻出来, 贴上)
写在前面:
本文是个人在阅读MON51代码后整理所得,由于调试器实现知识不足,加之有猜测之处,因此肯定有很多错误,仅供对MON51有兴趣的朋友参考。任何人可以引用本文无需作者同意(当然注明出处欢迎),但是所做修改需要注明,本人不对由本文衍生出的文章的错误负责。
下面这段是我在看SST SoftICE代码之前对MON51做的想象,为了使对MON51的认识有个大致的轮廓,该段直接引用在此。
**************************************************************************
首先,通过串口能够将程序下载进51单片机。
在下载之前,或下载完成后,在main处做隐式断点,使其跳转到SoftICE代码,这样SoftICE可以通过串口向PC发回反馈,然后在其中循环监测串口,等待主机命令。
PC在下位USER程序不执行时,要么发送继续执行命令,要么查看数据,设置断点,然后下达执行命令,总之这些都是以命令格式通过串口下达给下位,这些使用MON51协议,当下达继续执行的命令后,51继续执行,直至遇到被替换的所谓的软件断点,然后执行权重新到SoftICE代码,只要进入了SoftICE,SoftICE发回命中断点及断点信息的反馈,然后可以默认发回PC,REG等信息,或者PC收到反馈后主动发送获取这些信息的命令,从而被监听在串口上的SoftICE监听到,从而可以反馈回PC。如此往复。
51一旦执行后,不遇断点是不能停止的。这一切都不需要串口的中断使能。因为SoftICE使用查询来做这些,在正常的用户程序中,可以使用与SoftICE相同的串口配置,甚至可以使用串口的中断功能。
但是一旦要求主机能够主动中断51的执行,那么就需要用到串口的中断了,SoftICE,应该修改串口中断向量,指向SoftICE某处,使其能够向主机发回程序停止反馈并串口检测循环,从而表现为程序停止。
断点采用LCALL指令,将原指令地址压栈或保存,在monitor里面可以知道命中断点的地址。
这是基本的实现框架,有了这个框架,剩下的基本就是MON51通信数据结构,以及debugger如何命令序列的次序,51在中断命中后发送数据序列的次序等细节问题了。
*************************************************************************
以后将分章节以SST89E58RD单片机的SoftICE为例介绍MON51的工作原理.
SST SoftICE初始化部分代码分析
本章节介绍MON51代码的初始化部分。
SST89E58RD单片机内部FLASH分两个区,Block0容量32Kbytes,地址空间0x0000-0x7FFF; Block1容量8Kbytes,地址空间0xE000-0xFFFF,根据控制位Block1可以重映射到0x0000-0x1FFF。
SoftICE烧写到Block1的0xE000-0xEFFF 4Kbytes空间,SC[1:0]=0b00,从而使Block1重映射到0x0000-0x1FFF,上电复位后即开始执行SoftICE代码。这便是SoftICE用户指南上说的占用的Block1的4Kbytes,另外占据的Block0的0x7C00-0x7FFF 1Kbytes是运行时占用的,不是可执行代码占据的空间。
SoftICE启动后,顺序执行如下:
1. 将内部256Bytes IRAM拷贝到Block0的0x7D00-0x7DFF空间,这个空间作为USER程序代码IRAM的保存空间;
2. 将Block0的0x7E00-0x7EFF初始化为零;
3. 将Block1的0xE054(+0x5F)-0xE0B2的代码拷贝到Block0的0x7F00(+0x5F)-0x7F5F处;这些代码包括四个部分:MON51入口(0x7F00),MON51串口中断服务程序(0x7F19),FALSH字节编程函数(0x7F2C)和FLASH扇区擦除函数(0x7F48)。当编程MON51空间时使用这两个FLASH IAP函数,编程USER空间时使用MON51空间的FLASH IAP函数。
4. 跳转至波特率自适应计算的代码(0xE0B3)执行:PC会向51发送0x11(XON),MON51根据这个值计算出波特率;
5. 设置MON51的堆栈SP = 0x07,然后初始化MON51的部分寄存器,已知的MON51的这部分寄存器在地址0x7C88-0x7CA1处,后面会列出这部分寄存器。然后使能所有因单步禁能的断点,如果SERIAL_ISR被修改,则恢复为原来的SERIAL_ISR;
6. 然后进入MON51 LOOP接收PC发来的字符,并进行相应的处理。
MON51 LOOP接收的字符及处理方式列出如下:
0x11(XON)通信同步信号 MON51采用0x00和0xFF交替回复PC
0x01 PC要求得到RAM_24(STEP_MODE)的值,与单步执行模式有关,初始化为1,具体作用不详,未从串口收发数据中捕捉到。
上面这两个作为单独字符的消息(暂且将通信数据结构成为消息),下面列出是PC到MON51 Firmware消息开头字符,构成MON51的主要处理的消息。他们的消息处理入口表格在0xE919处,将(Code-2)A作为索引实现跳转(LJMP @A+DPTR)。
0x02 修改用户程序内存和寄存器 E919[0] = 0xE5DF
0x04 读用户程序内存和寄存器 E919[2] = 0xE649
0x06 断点管理 E919[4] = 0xE692
0x08 RUN & RUN UNTIL E919[6] = 0xE6DE
0x0A 用某个特定数值填充内存块 E919[8] = 0xE7DD
0x0C 机器指令(汇编指令)单步 E919[A] = 0xE81C
0x0E 设置RAM_24,未捕捉到 E919[C] = 0xE851
0x10 串口中断执行功能和返回MON51 Version功能 E919[E] = 0xE8BB
MON51使用的0x7C00-0x7FFF地址空间列出如下:
7C00(+0xC*6)- 断点管理池
7C70 Saved MON51’s SP
7C71 RCAP2H \ Saved MON51’s
7C72 RCAP2L / Adaptive Baud
7C73 If Serial Interrupt User Code. 0 or 1
7C74 If SERISR has been Modified. 0 or 1
7C75=>23 \
7C76=>24 |-Mon51’s SERISR: [7C75-7C77] = LJMP 0x7F19
7C77=>25 /
7C88 A \
7C89 PSW \
7C8A IE1 |
7C8B DPH |
7C8C DPL |
7C8D PCH \
7C8E PCL / Can Get From the User’s Stack, Pushed by LCALL to mon51
7C8F SP |
7c90 B \
7c91 SCON \
7c92 T2CON / Saved User’s SFRs(7C88-7CA1), Used for Context Switch
7c93 RCAP2H /
7c94 RCAP2L |
7c95 TH2 |
7c96 TL2 |
7C97 SFDT |
7C98 SFAL |
7C99 SFAH |
7CA0 SFCM /
7CA1 SFCF /
7CF0-7CF8 机器指令单步执行缓冲区
7D00-7DFF User’s IRAM 256 Bytes
7E00-7EFF Filled with zeros (NOPs)
7F00-7F5F some Mon51 code copy to here
7F81-7FFF Saved MON51’s first 0x80 Bytes IRAM
MON51对用户内存的读写
51单片机因中断或单步进入SoftICE monitor后,MON51驱动DLL通过串口发送给SoftICE firmware的读写内存的命令代码分别为4和2。下面分别叙述。
1内存写和修改PC
上位发出命令序列如图
2 |
0(ram26) |
PCH |
PCL |
Checksum(ram25) |
2 |
type(ram26) |
DPH |