|
主题 : : 【砖头】综合研究试验3---使用内存空间(1) [待解决] |
回复[ 10次 ]
点击[ 1938次 ] | |
|
|
|
|
[帖 主]
[ 发表时间:2009-03-20 14:59 ]
[引用]
[回复]
[ top ] | |
荣誉值:61
信誉值:4
注册日期:2008-10-14 16:29 |
今天进入“综合研究”中的研究试验3的学习。
在数据操作中,我们除了可以使用寄存器之外,就是内存空间了。我们在以前的汇编语言的学习过程中,也体会了直接利用内存空间存放和处理数据的编程做法。C语言中对寄存器的使用我们做了学习了,这个试验就是去体会C语言中另外一个数据处理方式--使用内存空间。更好的体会一下没有变量的情况下,对数据处理本质的探索。
其实,我们知道,数据肯定是要放到内存的,即使是变量也是定位到内存,然后操作数据的。所以,内存的使用才是变量等的本质操作形式。
以我们以前的汇编基础,我们知道,使用内存空间存储数据,需要指定数据的类型(比如,字节型、字型等)和数据的值。就像我们在汇编中定一个数据段中的数据,我们做如下的操作:db 1,2,....,其中db是指明数据的类型;1,2等数值是数据的值。包含这两个元素才可以准确的说明内存空间的数据的实质。
我们为什么要了解内存空间的使用?首先这涉及到变量操作的本质,再有就是,寄存器毕竟是有限的,那么几个寄存器倒来倒去的,使得有些简单的操作却变得很繁复。而内存空间要大的多,使用灵活性也比较好。
好了,开始进入实践阶段。
首先,我们要知道,C语言中使用指针来表示内存空间的地址和数据类型。比如说 char * p,指针变量p是一个字符型的指针,这个指针变量p的值指一个内存的内存地址。学习汇编的时候,我们知道物理地址是段地址和偏移地址构成的,在C中,char * p这样定义的指针变量代表的地址是偏移地址,复制的时候,它会使用对应默认的段地址。当然,教材中给出了我们以段地址和偏移地址构成物理地址的指针声明方式,那就是 char far * p。这样,我们就能够更自主的去操作内存了(但是要注意的是,某些敏感内存空间使不能随便访问的啊)。比如说,我们向内存中写入一个数据,我们可以如下操作
*(char *)0x1000 = ‘a’;0x为C中16进制标识,段地址没有给出,使用默认段地址。
*(char far *)0x10001000=‘b’;段地址和偏移地址构成一个物理地址,更自主的操作内存。
行了,有了前面的这点基础,我们来看实践(1)。
debug加载该程序后,先看一下加载后每个寄存器的状态,如下
AX=0000 BX=0000 CX=0EF3 DX=0000 SP=0080 BP=0000 SI=0000 DI=0000
DS=1532 ES=1532 SS=15BA CS=1542 IP=0000 NV UP EI PL NZ NA PO NC
然后,我们根据前面的知识,找到程序对应的汇编代码,如下
1542:01FA 55 PUSH BP
1542:01FB 8BEC MOV BP,SP
1542:01FD C606002061 MOV BYTE PTR [2000],61 ;对应*(char *)0x2000=‘a’,ds:[2000]=61
1542:0202 C70600200F00 MOV WORD PTR [2000],000F;对应*(int *)0x2000=0xf,ds:[2000]=f
1542:0208 BB0020 MOV BX,2000
1542:020B 8EC3 MOV ES,BX
1542:020D BB0010 MOV BX,1000
1542:0210 26 ES:
1542:0211 C60761 MOV BYTE PTR [BX],61;以上5行对应*(char far *)0x20001000=‘a’,我们看到段地址使用es来临时存储,偏移地址放到了bx中。运行后es:[bx]=61
1542:0214 B80020 MOV AX,2000 ;对应_AX=0x2000,使用寄存器存储数据。
1542:0217 8BD8 MOV BX,AX
1542:0219 C60762 MOV BYTE PTR [BX],62;以上两行对应*(char *)_AX='b',用寄存器AX中的值当作偏移地址,段地址默认ds,运行后ds:[2000]=62
1542:021C BB0010 MOV BX,1000 ;对应_BX=0x1000,使用寄存器存储数据。
1542:021F 03DB ADD BX,BX
1542:0221 C60761 MOV BYTE PTR [BX],61 ;以上两行对应*(char *)(_BX+_BX)。第一行先将寄存器BX中的值自加,然后使用默认段地址ds定位内存,运行后 ds:[bx]=ds:[2000]=61
1542:0224 8BD8 MOV BX,AX *
1542:0226 8A07 MOV AL,[BX] *
1542:0228 33C9 XOR CX,CX *
1542:022A 81C30010 ADD BX,1000 *
1542:022E 81D10020 ADC CX,2000
1542:0232 8EC1 MOV ES,CX
1542:0234 26 ES:
1542:0235 8807 MOV [BX],AL ;以上8行对应*(char far *)(0x20001000+_BX)=*(char *)_AX。从这里我们也可以体会到,一条内存定位指令,在转成汇编指令后,就变成了很多条,其中一个主要原因就是寄存器的多次使用,有限的寄存器使得这个过程比较繁复了,这应该也算是我们直接使用内存空间对于源程序代码的一个好处吧,省了不少事。这条指令,由于有段地址和偏移地址,所以,我们看到,汇编代码中由用到了es来临时当段地址寄存器。执行后,es:[bx] = 2000:3000 =62
1542:0237 5D POP BP
1542:0238 C3 RET
在上面的汇编代码中,我发现一个奇怪的现象,就是我标注*号的那几条指令。前两条是将AX的值赋给BX,然后,通过BX寻址,去得AX中的值表示的地址处的数据,也就是,这两条指令应该对应的是*(char *)_AX,之后,在进行计算(0x20001000+_BX)这句指令的时候,这里的BX应该是前面*(char *)(_BX+_BX)这条指令对应汇编代码add BX,BX之后的值。但是,我们看到,这两条指令对应的汇编代码中,在mov BX,AX之前并没有保存BX的值,这样的逻辑应该不合理的,巧合的是,此时BX=AX,所以,结果就没有任何问题。难道是因为BX和AX正好相等,才导致了没有进行保存BX值的这个处理方式么?那我们把AX的值给他改一下看看,验证一下我们的猜想。
将源代码中_AX=0x2000 改成 _AX=0x3000,查看汇编代码如下:
1542:021C BB0010 MOV BX,1000
1542:021F 03DB ADD BX,BX
1542:0221 C60761 MOV BYTE PTR [BX],61
1542:0224 8BD8 MOV BX,AX
1542:0226 8A07 MOV AL,[BX]
1542:0228 33C9 XOR CX,CX
1542:022A 81C30010 ADD BX,1000
1542:022E 81D10020 ADC CX,2000
1542:0232 8EC1 MOV ES,CX
1542:0234 26 ES:
1542:0235 8807 MOV [BX],AL
可以看出,我们的结论是不对的。BX值就是不会在mov BX,AX之前进行保存。那就是说,在*(char far *)(0x20001000+_BX)=*(char *)_AX这条指令执行过程中,由于后半部分指令对BX进行了修改,所以,0x20001000+_BX最终的值是要视AX的值而定的,并不是继承前面指令中的BX值的。(这是我的推断,大家如果有比的看法,欢迎指正)
=========================
这节要说的东西感觉还不少,先写下前半部分的学习总结,第(2)题是编程题,有了前面的地址定位,这个不是问题,就不多说了。后面两个实践学习后再添加上来。
继续学习中... | | |
|
|
|
|
[第1楼]
[ 回复时间:2009-03-20 15:02 ]
[引用]
[回复]
[ top ] | |
荣誉值:152
信誉值:3
注册日期:2008-01-24 21:26 |
|
|
|
|
|
[第2楼]
[ 回复时间:2009-03-23 13:37 ]
[引用]
[回复]
[ top ] | |
荣誉值:169
信誉值:0
注册日期:2008-08-19 16:07 |
|
|
|
|
|
[第3楼]
[ 回复时间:2009-04-19 13:20 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2009-04-15 18:50 |
看不懂,不知道这一个实验到底干什么,就楼主说的 0x20001000+_BX最终的值是要视AX的值而定的 ,那何不直接写成 0x20001000+_AX。 | | |
|
|
|
|
[第4楼]
[ 回复时间:2009-04-27 11:56 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:1
注册日期:2009-04-20 20:16 |
楼主:
1542:0214 B80020 MOV AX,2000 ;对应_AX=0x2000,使用寄存器存储数据,这里开始后面都没有对AX寄存器进行修改过,还有我也看不懂你后面的(引用楼主:难道是因为BX和AX正好相等,才导致了没有进行保存BX值的这个处理方式么?那我们把AX的值给他改一下看看,验证一下我们的猜想。) | | |
|
|
|
|
[第5楼]
[ 回复时间:2009-04-27 12:22 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:1
注册日期:2009-04-20 20:16 |
-u 1416:1fa
1416:01FA 55 PUSH BP
1416:01FB 8BEC MOV BP,SP
1416:01FD C606002061 MOV BYTE PTR [2000],61 ;对应*(char *) ox2000='a';
1416:0202 C70600200F00 MOV WORD PTR [2000],000F ;对应*(int *) ox2000=oxf;
1416:0208 BB0020 MOV BX,2000
1416:020B 8EC3 MOV ES,BX
1416:020D BB0010 MOV BX,1000
1416:0210 26 ES:
1416:0211 C60761 MOV BYTE PTR [BX],61 ;上面5条是*(char far *) ox20001000='a';
1416:0214 B80020 MOV AX,2000 ;对应_AX=ox2000;
1416:0217 8BD8 MOV BX,AX
1416:0219 C60762 MOV BYTE PTR [BX],62 ;上面三条对应*(char *)_AX='b';
1416:021C BB0010 MOV BX,1000 ;对应 _BX=ox1000;
1416:021F 03DB ADD BX,BX
1416:0221 C60761 MOV BYTE PTR [BX],61 ;上面三条对应*(char *)(_BX+_BX)='a';
1416:0224 8BD8 MOV BX,AX ;AX表示了偏移地址
1416:0226 8A07 MOV AL,[BX] ;求得*(char *)_AX内容,把AX表示的偏移地址对应的内存单元内容传给AL,只要8个字节
1416:0228 33C9 XOR CX,CX ;使得CX为0000H
1416:022A 81C30010 ADD BX,1000 ;使得BX为偏移地址
1416:022E 81D10020 ADC CX,2000 ;使得CX为段地址
1416:0232 8EC1 MOV ES,CX ;使得ES为前缀段地址
1416:0234 26 ES:
1416:0235 8807 MOV [BX],AL ;上面八条对应*(char far *) (ox20001000+_BX)=*(char *)_AX;
1416:0237 5D POP BP
1416:0238 C3 RET
1416:0239 C3 RET
1416:023A 55 PUSH BP
1416:023B 8BEC MOV BP,SP | | |
|
|
|
|
[第6楼]
[ 回复时间:2010-01-06 23:22 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2009-04-15 18:50 |
楼主,学习了,编译器就是这么翻译的。我们自己写的C程序,一般都不会直接用什么寄存器的,否则就象楼主做的实验那样,得不到想要的结果 | | |
|
|
|
|
[第7楼]
[ 回复时间:2011-01-07 23:34 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2011-01-07 22:45 |
|
|
|
|
|
[第8楼]
[ 回复时间:2018-01-30 16:19 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2018-01-30 13:44 |
估计编译器就是这么翻译的,反正后面也没有用到BX的时候,所以优先用BX腾来腾去(减少开销)。以上是我不负责任瞎猜的 | | |
|
|
|
|
[第9楼]
[ 回复时间:2018-01-30 16:22 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2018-01-30 13:44 |
回复:[第8楼]
------------------
第一个BX漏了一个“值”字 | | |
|
|
|
|
[第10楼]
[ 回复时间:2018-01-30 16:37 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2018-01-30 13:44 |
回复:[第9楼]
------------------
不对,我搞不懂 555 | | |