序:《Orange‘s一个操作系统的实现》的学习笔记已经好久都没写了,不是我放弃了而是我看不懂了,可能是基础实在是太差了,所以我开始学习王爽的《汇编语言》,我准备先为《Oranges》打个基础,我相信,这个我一定能看下去,如果不能,我想我可以放弃这条路了。。。以下内容仅仅是学习过程中的一些琐碎知识的摘抄,和一些个人理解,希望有兴趣的朋友一起讨论!
2012年12月31日
家里网速不给力,一个masm下载了好久,今天终于下载下来了。
我用的是Bochs虚拟机环境,是前面学写《Oranges》弄的dos系统,刚好可以用来学习汇编语言,下载下来的masm和link用虚拟软盘拷贝到dos,感觉特别麻烦,不知道Bochs是否可以关联到本地路径让我可以不用这么麻烦拷来拷去的,哪位大侠要是知道,来指点一下下!!折腾这么长时间,其实自己貌似没做什么。
dos启动后,完成初始化,然后运行command.com程序,这个就是dos下的shell。在执行一个可执行程序的时候,由command程序将准备执行的程序调如内存,并设置cs:ip指向程序的入口。
psp(程序段的前缀)的起始段地址SA,256(100H)个字节,地址范围是SA:0 ~ SA+10H:0(注意段地址需要左移4位)
在实验三中出现一个问题,执行第一句 mov ax,2000h,ax没有改变,但是执行最后一句mov ax,4c00h时,ax就被改变了,为什么?怎么好像是内存越界造成的。
2013年1月2日
loop指令格式:loop 标号
cpu执行loop时,要进行两步操作(1)cx = cx - 1(2)判断cx不为0则跳转至标号处执行
之前没有注意,前面出现的问题很严重啊,导致我程序都没办法正确运行。
2013年1月13日
前两天玩的太high,没学习啊。
虽然程序运行有些问题,但是原因还没找到,继续向下学习
今天看到一个错误 must be index or base register 百度一下得到的结果是
16位汇编不能使用ax间址寻址,只有BX、BP、SI、DI才可以。32位程序才可以用任意32位寄存器间址寻址。
这是我前面学习过程中所忽略掉的 为什么是loop和[bx]一起使用,而不是和[ax],[dx]。因为不用bx会报错!
在第五章实验4的最后一个问那段代码中,我编译都有错误???
p10.asm(3): error A2008: syntax error : ,
p10.asm(8): error A2008: syntax error : ,
p10.asm(12): error A2006: undefined symbol : s
这是为什么呢?哪位大仙能给我解答一下下? 2013年1月14日 今天在学第六章,实验五前两个实验没有出现错误代码的情况,但是做到第三个小实验的时候出现了之前出现过的,代码运行错误的情况C:\MASM>debug ex5_3.exe
=u
1AD1:0000 B8D51A MOV AX,1AD5
1AD1:0003 8ED0 MOV SS,AX
1AD1:0005 BC1000 MOV SP,0010
1AD1:0008 B8D41A MOV AX,1AD4
1AD1:000B 8ED8 MOV DS,AX
1AD1:000D FF360000 PUSH WORD PTR [0000]
1AD1:0011 FF360200 PUSH WORD PTR [0002]
1AD1:0015 8F060200 POP WORD PTR [0002]
1AD1:0019 8F060000 POP WORD PTR [0000]
1AD1:001D B8004C MOV AX,4C00
=t
AX=D5B8 BX=0000 CX=0244 DX=0000 SP=0002 BP=0000 SI=0000 DI=0000
DS=1AC1 ES=1AC1 SS=1AD1 CS=1AD1 IP=0002 NV UP DI NG NZ NA PE NC
1AD1:0002 1A8ED0BC SBB CL,[BP+BCD0] SS:BCD0=18
第三个小实验是把代码段放到最上面,数据段放到了下面,这就导致程序错误,什么原因呢?
之前猜测是越界的问题,执行完第一条的时候ip应该指向3,为什么是2呢?
下面是第二个小实验运行的情况
C:\MASM>debug ex5_2.exe
=r
AX=0123 BX=0000 CX=0242 DX=0000 SP=0002 BP=0000 SI=0000 DI=0000
DS=1AC1 ES=1AC1 SS=1AD1 CS=1AD3 IP=0000 NV UP DI PL NZ NA PO NC
1AD3:0000 B8D21A MOV AX,1AD2
=t
AX=1AD2 BX=0000 CX=0242 DX=0000 SP=0002 BP=0000 SI=0000 DI=0000
DS=1AC1 ES=1AC1 SS=1AD1 CS=1AD3 IP=0003 NV UP DI PL NZ NA PO NC
1AD3:0003 8ED0 MOV SS,AX
1AD3:0005 BC1000 MOV SP,0010
=t
AX=1AD2 BX=0000 CX=0242 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=1AC1 ES=1AC1 SS=1AD2 CS=1AD3 IP=0008 NV UP DI PL NZ NA PO NC
1AD3:0008 B8D11A MOV AX,1AD1
困惑啊~~~~
push ax可以push al不可以
2013年1月16日
应该是编译器版本的问题
mov al,[bx+si+3]是正确的
mov al,[bx+3+si]是错误的
[...]只可以用bx、si、di、bp、idata的组合
其中在使用bp的时候,没有显性的给出段地址,段地址默认在ss中
mov ax,[bp] 表示 (ax)=((ss)*16+(bp))
inc word ptr [bx]bx作为一个字指针,所指向内容+1
inc byte ptr [bx] bx作为一个字节指针,所指向内容+1
push 和 pop指令只进行字操作
[di+si]是错误操作,[bx+si/di],[bp+si/di],可以
ex7.asm(57): error A2023: instruction operand must have size内存单元和内存单元之间不能赋值运算
2013年1月17日
offset的功能是取得标号的偏移地址,从哪开始的偏移地址
位移 = 标号处地址 - jmp指令后的第一个字节地址
所有有条件转移的指令都是短指令 ,所有循环指令都是短指令
短指令中都是位移
jcxz 标号 当cx=0时,跳转到标号处
loop一定要注意先cx=cx-1,再判断cx是否为0
2013年1月18日
在子程序中所有子程序用到的寄存器内容都保存起来,返回前再恢复
(1)今天在写实验10第3小问的时候,把前面的两个子程序都用上
开始现实66621,应该现实12666
我就在程序里用堆栈令现实内容逆序,结果程序不停的崩溃
后来发现在变成12666字符时,最后一位没有设置0,在填上0后,程序不崩溃了,但是变成了死循环
后来发现有个中间变量bx在子程序调用过程中为没有出时候为0,所以程序进入了死循环
(2)程序在正常运行的时候,也就是不用debug调试的时候,代码初始部分不会被0302H代替,能够正常运行
2013年1月19日
今天做了一下书上的课程设计1,把前面的程序拼一下,还算顺利,心情激动
2013年1月21日
pushf 将将标志寄存器的值压栈 popf将栈中数据弹出到标志寄存器中
zf:判断运算结果是否为0,是为1,不是为0
sf:符号标志位 指令执行结果为负则该位为1,指令执行结果为正则该位为0,无论运算的操作数是有符号还是无符号的,运算的过程都会影响sf,只是看编程者是否需要这位
cf:进位/借位标志位 对无符号数有意义
of:溢出标志位 记录有符号数运算结果是否发生了溢出
df:方向标志位,di、si递增则为0,递减则为1 cld 将df设置为0 std将df设置为1
movsb 相当于 mov es:[di] , byte ptr ds:[si](这个指令不存在)if(DF == 0) inc si inc di else if (DF==1)dec si dec di
movsw 功能和movsb类似,传送一个字
rep movsb 相当于
s:movsb
loop s
inc 和 loop指令不影响cf,必要的时候可以用inc代替add
cmp指令中,两个无符号数的判断可以由cf、zf来决定
在有符号数中的判断,先要看of是否溢出,若没溢出sf结果是正确的,若溢出sf结果取反,zf表示相等
本来以为 cmp byte ptr [si],'a'这样的指令不可以,试了一下是可以的
中断子程序返回应该用指令iret,但在在实验12中,中断子程序结束后必须用 mov ax,4c00h int 21h这两条指令,为什么呢?如果用iret就是死循环
2013年1月22日
iret执行的过程如下
pop ip
pop cs
popf
2013年1月23日
对0~255
in al,20h;从20h端口读入一个字节
out 20h,al;往20h端口写一个字节
对255~65535的端口进行读写时,端口号放在dx中(这是必须的吗)
mov dx,3f80h ;将端口号3f80h送入dx
in al,dx
out dx,al
shl、shr 如果移动位数大于1位,必须将移动位数放在cl中
时间在CMOS RAM中的存放单元
秒:0 分:2 时:4 日:7 月:8 年:9
2013年1月24日
在后面加有“:”的地址标号,只能在代码段中使用,不能在其他段中使用
如果想在代码段中,用数据段中的数据标号访问数据,则需要中伪指令assume将标号所在的段和一个段寄存器联系起来,否则无法确定标号的段地址在哪一个段寄存器
seg 操作符,功能为取得某一标号的段地址
程序里什么时候应该用偏移,什么时候用段里地址不懂
2013年2月1日
其实上网就为了打一下卡,怕自己坚持不下去
还有最后两个实验和一个课程设计
每次都虎头蛇尾
2013年2月19日
今天终于明白org 7c00h是什么意思,以前在写单片机程序的时候开始总会写一句org 0000h,删掉也没什么,虽然片面的理解是告诉编译器程序是在哪段内存运行的,但是不明白这样运行的意义何在,因为在arm中把某段程序下载到flash中后,由bootloader将其拷贝到特定内存中,cpu由这个特定内存开始执行程序,但是编译器是知道这段内存放在什么位置,所以这段程序的地址是由编译器来计算的,但是在汇编中如果没有org xxxxh这样的语句,编译器是不知道程序员准备将程序放到什么位置,dos下运行程序的时候会将程序的起始位置作为一个段的开头,即ip=0,所以开头的org显的不那么重要,在弄本书课程设计的最后,有一个问题让我很苦恼,
mov si,str0[bx]
这一句单看寄存器,和内存数据都没有问题,但是执行完后si的数据却是非常夸张的,这句的代码反复产看了N遍,终于发现原来程序中的偏移量(如:str0)都是针对程序开始的,而程序的段cs是0,所取得的数据当然很夸张!因为开始有下面的语句
20 mov ax,cs
21 mov ds,ax
接下来我把20那句改成
20 mov ax,codesg
这是程序段的名称,想要做引导的那段程序紧接着段开始,在引导调试的时候这个值是0x15dd,想不通是哪来的值,用debug调试的时候这个值变成了程序运行的cs地址,由于不知道在masm编译器下org 7c00h怎么写,所以我强制将其变为
20 mov ax,7c00h
重新查看发现没用,而且这几次修改值都一样,这怎么会呢?后来发现反汇编的语句是这样的
mov si, word ptr cs:[bx+116]
运行的段地址是cs,跟我刚才改来改去的ds完全没意义,这是什么原因?
我原句是这么写的
5 codesg segment
6 code:
7 jmp code_start
8 stack: db 64 dup(0)
9 str1 db "1) reset pc",0
10 str2 db "2) start system",0
11 str3 db "3) clock",0
12 str4 db "4) set clock",0
13 str0 dw str1,str2,str3,str4
25 mov si,str0[bx]
因为st0是在code这个段内,我现在有两种方法,一种是ds:这种方法,另一种是将上面的数据分离到另一个segment中(放在code的原因是因为开始的时候在copy程序的下面)
第一种方法后反汇编结果是这样的
mov si, word ptr ds:[bx+116]
但是查看数据还是不对
回想起来,上面ds段地址是这个值,不对是一定的
20 mov ax,7c00h
应该把上面的语句改成
20 mov ax,7c0h
引导成功!
由于这只是引导程序,有固定的大小用多个segment貌似不是好方法,而且我也没能实现
虽然觉得这样写并不是什么好方法,但是现在就先这样吧,这只是刚刚开始,这才是引导,功能还没有实现,貌似需要精确计算