既然我们知道了如何显示一个字符,那么显示多个字符似乎也不在话下,请看下面代码:
MOV AL,'H'
MOV AH,0Xe
MOV BX,15
INT 0X10
MOV AL,'e'
MOV AH,0Xe
MOV BX,15
INT 0X10
MOV AL,'l'
MOV AH,0Xe
MOV BX,15
INT 0X10
MOV AL,'l'
MOV AH,0Xe
MOV BX,15
INT 0X10
MOV AL,'o'
MOV AH,0Xe
MOV BX,15
INT 0X10
fin:
HLT ;让CPU停止,等待指令
JMP fin ;无限循环
RESB 0x1fe-$
DB 0x55,0xaa
如上所示,似乎这样就可以显示一个hello的字符串了,是这样吗?以图为证:
对于fin的解释:
如果不加这一段,似乎程序也是可以正确运行的,但是最好要加上这段,好让CPU在没什么可做的情况下,不会继续运行下去,相当于待机状态。
但是请问,如果要写一大段的话,那这样写可行吗?我们编写操作系统岂不是困难多多吗?其实,如果稍微熟悉一些汇编的人,都知道不要把要显示的字符串保存在程序里,而是要保存到一个其他的地方循环获取。
下面我隆重介绍一个寄存器:SI寄存器,我们将用它来指示下一个要获取的字符在内存中的位置,而我们要获取的数据,将保存在一个代码段里面,在内存加载磁盘的时候,就会加载这个代码段:
message:
DB 'Hello,world!'
DB 0x00
哇塞,汇编语言会将这一段代码编译成为什么呢?不出所料,汇编的结果是:
看到了吧,汇编语言会将字符串自动进行转码,转换成ascii保存在程序里面
既然汇编语言将这段代码保存在程序里面,并且在计算机启动的时候,被加载到了内存里面,我们就可以在内存里面找到这段代码了,当然他们是以010101的形式保存的,只不过内存里面是8个位8个位为单位的,每8个字节可以保存一个字节,也就是可以保存像H这样的一个字符了。
那我们如何找到这些字符呢?这是个问题啊,我们不能只保存不读取吧。
下面就要发挥SI的地方了,请看下面代码:
MOV SI , message + 0x7c00 ;程序的加载位置是内存的0x7c00处,因此要加上加载点以前的长度
loop:
MOV AL,[SI] ;将内存地址SI的位置的内存,一个字节,加载到AL中去
CMP AL, 0 ;判断是否是结束字符(可以自己定)
JE fin ;如果相等,则跳转到结束
ADD SI,1 ;SI中的值加1
MOV AH,0x0e
MOV BX,15
INT 0X10
JMP loop ;跳转回去,继续执行
fin:
HLT ;让CPU停止,等待指令
JMP fin ;无限循环
message:
DB 'Hello,world!'
DB 0x00
RESB 0x1fe-$
DB 0x55,0xaa
上面代码有一点多,但是主要部分其实还是循环,其中 MOV AL,[SI] 就是将SI处的内存中的数据,加载到AL中。
那 MOV SI,message + 0x7c00 呢?如果写作 MOV SI,message 可能还好理解一下,对于标号,其实就是一个地址,在汇编编译器看到这个标号的时候,会自动翻译成为它对于程序一开始的偏移地址,所以写成 MOV SI,message 就是将 message的偏移地址写入SI ,那为什么还要加上 0x7c00呢 ?这是因为,在BIOS启动的时候,是要在内存中加载很多的驱动的,这些驱动要占内存一部分的地方,而前 0x7c00的位置,都被这些驱动占领了,因此,没办法,在操作系统引导的时候,就会把磁盘的0柱面0扇区的512字节加载到内存中,作为启动区,也就是说,我们其实加载到内存中的message的位置,已经偏移了0x7c00个位置的,所以这里要加回去,这时候,SI所指示的位置,就是message在内存中的位置了。
下面,需要做循环的地方,其实不难
那么如何跳出循环呢:
CMP AL, 0 ;判断是否是结束字符(可以自己定)
JE fin ;如果相等,则跳转到结束
这里做了很好的解释,CMP和JE指令可以配合使用,这里表示如果相同,则进行跳转,语法很简单,不再多说了。
另外还有其它跳转指令可以配合,简单的比如 JNE ,指令,表示如果不想等,则跳转。
你甚至可以这样写:
CMP AL, 0 ;判断是否是结束字符(可以自己定)
JE fin ;如果相等,则跳转到结束
JE fin ;如果不相等,则跳转到结束
这就和无条件跳转一样了。哈哈哈哈。