10.20
任务目标 // 进度:
工作结果:
学习笔记:
SMBus(System Management Bus)
SMBus:双线通信专利技术,与I2C串行总线兼容,是I2C的子集。SMBUS可获取内存的SPD参数。0xA0, 0xA2, 0xA4, 0xA6 读取SPD。
工作频率:10-100khz
应答信息:ACK / NACK
读操作:接收器->发送器; 写操作:发送器->接收器
总线协议:数据线SDA(双向),时钟线SCL(双向)
SMBus接口:主方式;从方式
基地址:从设备,SPD前四位1010,后三位硬件电路决定。ARP功能的主控制器0001000,默认设备地址1100001.
SPD:串行存在检测,包括内存芯片,模组厂商,工作频率,工作电压,速度,容量
读取方式:字节读取 / 块读取
字节读取寄存器:HST_STS,HST_CNT,HST_CMD,XMIT_SLVA,HST_D0
块读取寄存器:HST_STS,HST_CNT,HST_CMD,XMIT_SLVA,HST_D0,HOST_BLOCK_DB和AUX_CTL。
读取数据的过程:采用RU软件测试;采用汇编代码。
1.RU软件测试验证过程
1.1.Byte Data读取方式测试
第一步,将HST_STS(00H)设置为1EH(清掉各个标志位),可以看到HST_STS数值变成了40H。
第二步,将XMIT_SLVA(04H)设置为A1H,说明是要读取SPD。
第三步,将HST_CMD(03H)设置为00H,说明读取SPD中的00H处的数据,若要读取01H处的数据,就设置为01H。
第四步,将HST_CNT(02H)设置为48H(采用字节读取方式,并START),这是可以看到HST_CNT变成了08H,HST_STS变成了42H(说明INTR并且!HOST_BUSY,对比前面的英文描述),HST_D0变成了7F(这个是本测试电脑的SPD的第一个数据),也说明读取数据的目的已经达到。
1.2.Block 读取方式测试
该种读取方式比之于Byte Data方式要复杂一些,首先要特别关注AUX_CTL(0DH)中的E32B位。该为置1与否,也决定了读取过程。以下分别进行测试。
1.2.1 AUX_CTL设置为02H
第一步,将HST_STS(00H)设置为1EH(清掉各个标志位),可以看到HST_STS数值变成了40H。
第二步,将XMIT_SLVA(04H)设置为A1H,说明是要读取SPD。
第三步,将HST_CMD(03H)设置为00H,其实也是设置偏移地址的,如果设置成10H,那么就从10H位置读取数据。
第四步, 将HST_CNT(02H)设置为54H(采用Block读取方式,并START),这是可以看到HST_CNT变成14H,HST_STS变成了41H(说明!INTR并且HOST_BUSY),HST_D0变为7FH(说明该寄存器保存第一个数据,并且这第一个数据也是后面的COUNT。),HOST_BLOCK_DB在不停的闪动(说明该寄存器每被读取一次就变化一次,也就是E32B被置1的效果)。
1.2.2 AUX_CTL设置为00H
前面三步同上。
第四步,将HST_CNT(02H)设置为54H,HST_CNT变成14H,HST_STS变成C1H(注意这里是C1H,即DS被置1),HST_D0变成1F,HOST_BLOCK_DB变成了08H(注意它并没有闪),本机的SPD数据序列为:7F,08,08,0E,…。这明显的说明HST_D0保存的是第一个数据,它也是count。
第五步,将HST_STS设置为C1H(注意HST_STS的数据本来就是C1H,用这种方式清掉DS位,清掉之后发现HST_STS还是C1,没有变化,其实这一过程是有变化的,HST_STS先变成41,然后再变成C1,速度太快,看不到变化,在后面程序中用这一过程作为判断),发现HST_D0变为08H(这里还不能说是变化,值一样)。
第六步,继续第五步,发现HST_D0变为0EH。接着重复,可以把这个Block缓存数据读取完。以什么作为结束标志呢?INTR和!HOST_BUS。另外也可以将KILL位(在HST_CNT中)置1,中断传输。这种方式下可以读取超过32个字节的数据,有多少就能读多少。
2.汇编代码验证过程
2.1 Byte读取方式的代码过程
第一步:通过HST_STS判断SMBus是否空闲可用(HST_STS&1EH == 0?不为0则1EH=>HST_STS。HST_STS&01H == 0?不为0则循环)
第二步:设置XMIT_SLVA (SMBA+04h), 选择设备地址,以及读写设为01h
第三步:设置HST_CMD(SMBASE+03h),选择设备对应的Register Index
第四步:设置HST_CNT(SMBASE+02h),写48h表Byte Data形式读取
第五步:循环判断HST_STS(HST_STS&1CH不为0则报错,HOST_BUSY应为0且INTR应为1否则循环)
第六步:从HST_D0(SMBASE+05h)中读取数据低字节;从HST_D1(SMBASE+06h)中读取数据高字节。(第四步前写入低字节在HST_D0(SMBASE+05h),写入高字节在HST_D1(SMBASE+06h))
2.2Block读取方式
第一步:通过HST_STS判断SMBus是否空闲可用(参考:HST_STS&9EH == 0?不为0则HST_STS=>HST_STS。HST_STS&01H == 0?)
第二步:设置XMIT_SLVA(SMBASE+04h), 选择设备地址,以及读写设为01h
第三步:设置HST_CMD(SMBASE+03h)为00H,index清0
第四步:设置AUX_CTL(SMBASE+0DH)为00H,将E32B置0
第五步:设置HST_CNT(SMBASE+02h),写54H表Block Data形式读取
第六步:判断HST_STS,循环直到DS(bit7)被置1
第七步:HST_D0(SMBASE+05h)读取第一个数据,也将该数据作为后面总字节数
第八步:HOST_BLOCK_DB(SMBASE+07h)中读取一个字节
第九步:HST_STS的DS位置1清0
第十步:循环判断HST_STS,直到DS(bit7)被置1,再重复第七步,直到读完所以字节数
第十一步:设置HST_CNT为16H,KILL命令过程
第十二步:用HST_STS的数据清掉HST_STS
I2C:
工作频率:0-400khz
应答信息:无
SMBUS的访问过程
IO访问:F040 MMIO访问:F722A000
Base:F040
0:写入FE清零,地址为base address+0x00
read byte:
1:写入slave+1,写入地址为base address+0x04 ,bit 0 置一表示读操作
2:选择需要的寄存器,设置其偏移量(1~256),地址为base address+0x03
3:写入操作方式0x48,写入地址为base address+0x02
4:读出数据,读数据的地址为base address +0x05
HST_STS(00H) | XMIT_SLVA(04H) | HST_CMD(03H) | HST_CNT(02H) | HST_D0(05H) | HST_D1(06H) |
状态位 | 寄存器 | 从地址 | 数据方式 | 低字节 | 高字节 |
FE(清零) | A1(A0) | 0 | 48(字节读取) | 返回值 | |
41(忙) | A3(A2) | 1 | 4C(块读取) | 返回值 | 返回值 |
42(可用) | A5(A4) | 2 | |||
A7(A6) | … | ||||
注:末位置1表示读操作 | 255 |
read Word:
1:写入slave+1,写入地址为base address+0x04,bit 0 置一表示读操作
2:选择需要的寄存器,设置其偏移量,地址为base address+0x03
3:写入操作方式0x4C,写入地址为base address+0x02
4:读出数据,读数据的地址为base address +0x05,base address +0x06
write byte:
1:写入slave,写入地址为base address+0x04,bit 0 置零表示写操作
2:选择需要的寄存器,设置其偏移量,地址为base address+0x03
3:写入需要的数据:一个byte,写数据的地址为base address +0x05
4:设置byte的工作方式0x48,写入地址为base address +0x02
write Word:
1:写入slave,写入地址为base address+0x04,bit 0 置零表示写操作
2:选择需要的寄存器,设置其偏移量,地址为base address+0x03
3:写入需要的数据:一个word,写数据的地址为base address +0x05,base address +0x06
4:设置word的工作方式0x4C,写入地址为base address +0x02
汇编语言:
1.物理地址/绝对地址
2.逻辑地址:段地址+偏移地址。段地址左移一位加偏移地址就是物理地址。
3.中断地址:中断的首地址保存到一个地址,这段地址是内存的绝对零地址(0000:0000)。
中断地址的4个字节,数据是由低向高的。比如12 34 56 78所表示的地址是:7856:3412。
4.共享数据区:在绝对地址(0040:0000)开始。
5.可执行文件内存映像
DOS下可执行文件有两种(BAT是批处理文件,他只是简单调用DOS内部命令或其它程序,所以此处不认为它是可执行文件):一种是COM文件,一种是EXE文件。从程序内部看,前者程序的四个段重合,所以最大长度只等于一个段,用前面段地址的理解就是com文件最大只能是64K,所以com文件只适合小的程序。而exe,四个段可任何分配,并可扩充段,而且每个段的段地址可以任何改动,因此exe的访问内存能力大多了。这种格式访问能力只受地址结构的限制了。 用户程序所占的内存大小完全由程序本身决定,但最大,只能到640K。
说到这里,还要提到段。每个段64K。段的作用就是数据组织单位。段的类型有三种:代码段(Code Segment,简称CS)、数据段(Data Segment,简称DS)、栈段(Stack Segment,简称SS),另外还有一个附加数据段(Extra Segment,简称ES),它的用与数据段DS可以认为完全一样,当数据段的64K不够用,或你就需要把数据放到两个段中以便移动、复制、比较时,才用到附加数据段ES。(当然,移动、复制、比较操作在一个段中也可以完成)。
5.1.段的作用
5.1.1 代码段(CS):程序装入内存中,DOS怎么知道是从哪里执行呢?答案就是系统自动从代码段指定位置开始执行,并且始终在代码段中执行。因此代码段CS的作用就是保存所有的指令。这里所说的代码也就是汇编指令了。所以编写汇编程序也就主要是编写代码段中的代码。
5.1.2 数据段(DS)、附加段(ES):顾名思义,数据段中存的就是数据,这些数据供代码段的程序调用。附加段就是附加数据段。作用与数据段相同。
5.1.3 栈段(SS):这个段非常重要,但实际上,你在使用中,似乎用不着这个段,但实际上,这是黑客编程中最重要的一部分,而且系统会不停地“偷偷地”使用这个段,正是这个偷偷地用,使得系统的很多动作被记录到这个段中。还有两点,你必须记住:一是如果你使用了这个栈,比如你把数据存到这个栈中,则必须有相应的出栈命令,并且入几个数据,就得出几个数据,多一个或少一个,你的程序就可能导致死机或异常;二是你要把握操作时机,比如你不能在系统使用栈的前后使用栈,比如你在调用子程序之前入栈,而在子程序中出栈,而在系统调用子程序时,系统也要使用栈,这种也将导致出错。
栈就是一种先入后出(也有称为后入先出)的结构,有地址由小到大的增加栈,有地址由大到小的逆向减栈。
5.2.段重叠
从上面,我们可以看到,CS,DS,SS三者作用各不相同,内存就是象录音磁带,录新歌,则旧歌被删,带子上存的始终是最后录的那段音乐。因此,如果重叠则必然相互冲突。那还能重叠吗?
这里所说的重叠不是指内容重叠,而是指概念上的重叠,即数据相互放到一个段中,但相互可以区分开。比如某一段既有数据也有代码,则代码在每要执行到数据之前加一个跳转指令跳过这段代码。这个跳转指令要求用户在编程的时候加上。
而栈段呢?栈段有自己的特殊性,特殊就在于系统也会自动地使用,而用户则又在不知道系统在使用的情况下使用。避免这种冲突的方法就是采用逆向的栈段。
5.3 .COM文件内存映象
COM文件被读到内存中后,该文件的前100H个字节被操作系统使用,操作系统使用这256个字节保存一些系统要使用的数据,汇编语言编程者不能在这里存自己的数据,但在知道这此数据的作用后可以使用其中的数据。从100H开始,就是程序的开始了。COM文件之所以最大只能有64K,其原因是COM文件的四个段是相互重叠的。也就是说,CS、DS、SS、ES四个段的地址都指向这个COM文件的100H处。程序代码、数据、栈都在由100H到64K的区域内。如何把三者分开呢?栈段采用逆向栈,这个栈由64K开始,随着数据入栈,则地址就减小。这样作的好处是,栈段由高端向低端进展,可以详细与数据、代码分开;坏处也不言而喻,假如一个COM程序大量用到栈(比如是个递归程序)因此栈就不停地降低,而程序代码本身也很多,甚至不停地申请新空间,这样数据和栈就会在中间碰头,导致程序被破坏。
区分开数据代码段与栈段后,下面讨论把数据段和代码段也分开。这个简单的多,只要逻辑上分开就可以。不过一般的方法就是:在100H处放一个跳转指令,随后放数据,然后再放置其它的代码。而100H处的跳转指令就跳到这里。
因此,COM文件内存映象就是:
CS:0000 (由于COM的CS,DS,SS,ES三段重叠,因此此行前CS,写成DS,SS,ES都一样)
CS:0100 一个跳转到YYYY地址的跳转指令。
CS:0101 本程序所需要用到的数据
CS:XXXX 数据结束处。
CS:YYYY 程序代码保存处。
CS:ZZZZ 程序代码结束处。
CS:FFFF 栈段开始处(注意栈是地址越来越小,所以这里是开始而不是结束处),也是程序的结束处。另外,此处FFFF与前面XXXX,YYYY,ZZZZ不一样,这里是十六进制的64K。
5.4.EXE文件
比起COM文件,EXE文件要复杂一些,他的复杂就在于COM文件前面规定了100H个字节用于系统使用,而EXE文件则有个文件头,文件头的大小看具体内容多少。文件头的内容使得EXE看起来复杂了,但也更灵活了。更重要的是,对于病毒设计者,这个文件头使他们如鱼得水。因为文件头处
EXE文件的内存映象为:
XXXX:0000 文件头
XXXX:YYYY 文件头结束处
CS:0000 代码段开始处
CS:ZZZZ 代码段结束处
DS:0000 数据码段开始处
DS:WWWW 数据码段结束处
SS:0000 栈段开始处
SS:UUUU 栈段结束处
ES:0000 附加段开始处
ES:VVVV 附加段结束处
说明:
- 上述ES可以没有,要看实际需要
- CS,DS,ES,SS的顺序也是看编程者是怎么安排的,好在用户不必关心他的具体位置。
- 由上可见,CS,DS,ES,SS的段地址肯定保存到了文件头中。
- 由上可见,实际执行的只是CS,因此DS,ES,SS的首地址,CS肯定要想办法知道。:)
6.汇编指令
6.1数据传输指令
mov a,b ;这个指令是把B中的数据复制给A,(B中仍保存原状)。这里的A和B可以是寄存器,可以是内存。但可以同时是寄存器,不能同时是内存。
6.2算术运算指令
ADD A,N ;寄存器A加上N,把和仍存在A中。类似于高级语言中的let a=a+n/a:=a+n/a+=n。
6.3逻辑运算指令
6.4串指令
从一个地址到另一个地址的复制需要注意的是:
*)把源串段地址给DS。
*)把源串编址给SI。
*)把目的串段址给ES。
*)把目的串偏址给DI。
*)把要复制的个数给CX,这里可不考虑$了。
*)把FLAG中的方向标志标志你要的方向,一个是顺向,另一个是逆向。
*)发送loop movs,scans等命令。
6.5程序转移指令
记住:无条件转移指令 jmp。等于转 jz,不等于时转jnz
6.6伪指令
string DB '这是我的第一个汇编语言程序$' ;DB就是伪指令,他的作用就是告诉编译程序,把后面一些数据或字符串放到内存中。当然对于exe来说,已在内存中了,就不用“告诉”了。(这就是为什么叫伪指令)。string是你给这段内存起的名字,如果你不需要这段内存,不起名字也可以,但如果后面要用,当然要加上这个名字。'这是我的第一个汇编语言程序$'这个就是要处理的数据,当然你也可以换成别的内容,但需要注意的是,要以'$'结尾,这是汇编的约写,即:只是到了$,就认为字符串结束,否则就一直向下找,直到找到一个$为止。所以这就要求你的字符串中不能有'$'。`
`Lea A,string ;前面已经定义了string,后面要把地址找到,就要用到lea指令。lea是把字符串的地址给A这个寄存器中,A当然可以上前面提到的任意寄存器。
7.编写程序
开始编写我们的第一个程序。
程序要求:显示一个“Hello,Mr.286.”怎么样?
思路:
7.1 要显示一个字符串,根据前面我让你们记的七八个指令够吗?答案是:不仅够,而且还用不完。
首先定义一下总可以吧。
hellostr db 'Hello,Mr.286.不要忘了。
7.2 首先要考虑的问题就是找中断,找到合适的中断,该中断就能帮我们完成这个显示任务。我找到(在哪找到的,怎么找到的,别问我,到网上或书上都能找到):
中断INT 21H功能09H
功能描述: 输出一个字符串到标准输出设备上。如果输出操作被重定向,那么,将无法判断磁盘已满
入口参数: AH=09H
DS:DX=待输出字符的地址
说明:待显示的字符串以’$'作为其结束标志
出口参数: 无
由上面看到,我们所需要作的就是把DS指向数据段,DX指向字符串的地址,AH等于9H,调用21h中断。
mov ds,数据段地址
lea dx,hellostr 'hellostr已在前面7.1中定义了。
mov ah,9h
int 21h
由于只要在调用int 21h之前把准备的东西准备齐就行了,所以int 21h前面三行的顺序并不重要。
7.3 退出程序,运行完总要退出呀。再查中断手册
中断INT 21H功能4CH
功能描述: 终止程序的执行,并可返回一个代码
入口参数: AH=4CH
AL=返回的代码
出口参数: 无
mov ah,4Ch
mov al,0
int 21h
或
mov ax,4c00h
int 21h
这里需要说明的是返回代码有什么用,返回给谁?返回给操作系统,因为是操作系统DOS调用的这个程序,这个返回值可以通过批处理中的errorlevel得到,这里不多说明,实际上操作系统很少处理这一值,因此al你随便写什么值影响都不大。
8.程序实现
data SEGMENT
msg DB 'Hello, Mr.286.$'
data ENDS
code SEGMENT
ASSUME CS:code,DS:data
start:MOV AX,data
MOV DS,AX
lea dx,msg
mov ah,9h
int 21h
MOV AX,4C00h
INT 21h
code ENDS
END start
9.编译运行
把上面程序保存成hello286.asm后,就可以编译运行了。进入DOS,进入汇编目录,如果还没下载,到前面找下载地址。
E:\Download\Masm>masm hello286.asm**
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.
Object filename [hello286.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:
50408 + 415320 Bytes symbol space free
0 Warning Errors
0 Severe Errors
说明:上面连续三个回车,表示我要的都是默认值。下面是零个警告,零个严重错误,(当然了,我的程序还敢错吗?)
**E:\Download\Masm>link hello286**
Microsoft (R) Overlay Linker Version 3.60
Copyright (C) Microsoft Corp 1983-1987. All rights reserved.
Run File [HELLO286.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment
说明:三个回车仍要默认,后面有个警告,没有栈段,这个没关系,没有的话系统会自动给一个。
**E:\Download\Masm>hello286**
Hello, Mr.286.
说明:运行成功。
E:\Download\Masm>
;颜色:蓝1,绿2,红4