第二天:汇编语言学习与Makefile入门

第二天:汇编语言学习与Makefile入门
helloos.nas文件

; hello-os
; TAB=4

        ORG        0x7c00            ; 指明程序装载地址
; 标准FAT12格式
        JMP        entry
        DB        0x90
        DB        "HELLOIPL"        ;        
......

; 程序核心

entry:
        MOV        AX,0            ; 寄存器初期化
        MOV        SS,AX
        MOV        SP,0x7c00
        MOV        DS,AX
        MOV        ES,AX

        MOV        SI,msg
putloop:
        MOV        AL,[SI]
        ADD        SI,1            ; SI加1
        CMP        AL,0
        JE        fin
        MOV        AH,0x0e            ; 显示一个文字
        MOV        BX,15            ; 指定字符颜色
        INT        0x10            ; 显卡BIOS调用
        JMP        putloop
fin:
        HLT                        ; CPUを停止
        JMP        fin                ; 無限循环

msg:
        DB        0x0a, 0x0a        ; 改行
        DB        "hello, world"
        DB        0x0a            ; 改行
        DB        0

        RESB    0x7dfe-$        ; 0x7dfeまでを0x00で埋める命令

        DB        0x55, 0xaa
; 以下是启动区以外の部分の記述

        DB        0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
        RESB    4600
        DB        0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
        RESB    1469432
------
首先是ORG指令。这个指令会告诉nask,在开始执行的时候,把这些机器语言指令装载到内存中的哪个地址。如果没有它,有几个指令就不能被正确地翻译和执行。另外,有了这条指令的话,美元符($)的含义也随之变化,它不再是指输出文件的第几个字节,而是代表将要读入的内存地址。

ORG指令来源于英文“origin”,意思是“源头、起点”。它会告诉nask,程序要从指定的这个地址开始,也就是要把程序装载到内存中的指定地址。这里指定的地址是0x7c00,至于指定它的原因我们会在后文(本节末尾)详述。

下一个是JMP指令,它相当于C语言的goto语句,来源于英文的jump,意思是“跳转”。简单吧!

再下面是“entry:”,这是标签的声明,用于指定JMP指令的跳转目的地等。这与C语言很像。entry这个词是“入口”的意思。

然后我们来看看MOV指令。MOV指令应该是最常用的指令了,即便在这段程序里,MOV指令的使用次数也仅次于DB指令。这个指令的功能非常简单,即赋值。虽然简单,但笔者认为,只要完全掌握了MOV指令,也就理解了汇编语言的一大半。所以,我们在这里详细地讲解一下这个指令。

“MOV AX,0”,相当于“AX=0; ”这样一个赋值语句。同样,“MOV SS, AX”就相当于“SS=AX; ”。或许有人会问:“这个AX和SS是什么东西?”这个问题我们待会儿再回答。

MOV命令源自英文“move”,意思是“移动”。“赋值”与“移动”虽然有些相似,但毕竟还是不同的。一般说来,如果我们把一个东西移走了,它原来所占用的位置就会空出来。但是,在执行了“MOV SS, AX”语句之后,AX并没有变“空”,还保留着原来的值不变。所以这实际上是“赋值”,而不是“移动”。如果用“COPY”指令来打比方,理解起来就简单多了。至于为什么成了MOV指令,笔者也搞不明白。

现在来说说AX和SS。CPU里有一种名为寄存器的存储电路,在机器语言中就相当于变量的功能。具有代表性的寄存器有以下8个。各个寄存器本来都是有名字的,但现在知道这些名字的机会已经不多了,所以在这里顺便介绍一下。
AX——accumulator,累加寄存器
CX——counter,计数寄存器
DX——data,数据寄存器
BX——base,基址寄存器
SP——stack pointer,栈指针寄存器
BP——base pointer,基址指针寄存器
SI——source index,源变址寄存器
DI——destination index,目的变址寄存器

这些寄存器全都是16位寄存器,因此可以存储16位的二进制数。

其实寄存器的全名还是很能说明它本来的意义的。比如在这8个寄存器中,不管使用哪一个,差不多都能进行同样的计算,但如果都用AX来进行各种运算的话,程序就可以写得很简洁。
“ADD CX,0x1234”编译成81 C1 34 12,是一个4字节的命令。
而 “ADD AX,0x1234”编译成05 34 12,是一个3字节的命令。

再说说别的寄存器,CX是为方便计数而设计的,BX则适合作为计算内存地址的基点。其他的寄存器也各有优点。

关于AX、CX、 DX、 BX这几个寄存器名字的由来,虽然我们找不到缩写为X的单词,但这个X表示扩展(extend)的意思。之所以说扩展是因为在这之前CPU的寄存器都是8位的,而现在一下变成了16位,扩展了一倍,所以发明者在原来寄存器的名字后面加了个X,意思是说“扩张了一倍,了不起吧!”。大家可能注意到了这几个寄存器的排列顺序,它并不遵循名称的字母顺序。没错,其实这是按照机器语言中寄存器的编号顺序排列的,可不是笔者随手瞎写的哦。

CPU中还有8个8位寄存器。
AL——累加寄存器低位(accumulator low)
CL——计数寄存器低位(counter low)
DL——数据寄存器低位(data low)
BL——基址寄存器低位(base low)
AH——累加寄存器高位(accumulator high)
CH——计数寄存器高位(counter high
DH——数据寄存器高位(data high)
BH——基址寄存器高位(base high)

那BP、SP、SI、DI怎么没分为“L”和“H”呢?能这么想,就说明大家已经做到举一反三了,但可惜的是这几个寄存器不能分为“L”和“H”。如果无论如何都要分别取高位或低位数据的话,就必须先用“MOV, AX, SI”将SI的值赋到AX中去,然后再用AL、AH来取值。这貌似是英特尔(Intel)的设计人员的思维模式。

32位
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
在16位寄存器的名字前面加上一个E就是32位寄存器的名字了。这个字母E其实还是来源于“Extend”(扩展)这个词。
在当时主流为16位的时代里,能扩展到32位算是个飞跃了。虽说EAX是个32位寄存器,但其实跟前面一样,它有一部分是与AX共用的,32位中的低16位就是AX,而高16位既没有名字,也没有寄存器编号。也就是说,虽然我们可以把EAX作为2个16位寄存器来用,但只有低16位用起来方便;如果我们要用高16位的话,就需要使用移位命令,把高16位移到低16位后才能用。

突然想起来,还有一个段寄存器(segment register),所以在这里一并给大家介绍一下吧。这些段寄存器都是16位寄存器。

ES——附加段寄存器(extra segment)
CS——代码段寄存器(code segment)
SS——栈段寄存器(stack segment)
DS——数据段寄存器(data segment)
FS——没有名称(segment part 2)
GS——没有名称(segment part 3)

那么接下来我们继续看程序,下一个看不懂的语句应该是“MOV SI, msg”吧。MOV是赋值,意思是SI=msg,而msg是下面将会出现的标号。“把标号赋值给寄存器?这到底是怎么回事?”为了理解这个谜团,我们先回到JMP指令。
前面我们已经看到了“JMP entry”这个指令,其实把它写成“JMP 0x7c50”也完全没有问题。本来JMP指令的基本形式就是跳转到指定的内存地址,因此这个指令就是让CPU去执行内存地址0x7c50的程序。

之所以可以用“JMP entry”来代替“JMP 0x7c50”,是因为entry就是0x7c50。在汇编语言中,所有标号都仅仅是单纯的数字。每个标号对应的数字,是由汇编语言编译器根据ORG指令计算出来的。编译器计算出的“标号的地方对应的内存地址”就是那个标号的值。

所以,如果我们在这个程序中写了“MOV AX, entry”,那它就会把0x7c50代入到AX寄存器里,我们代入到AX寄存器中的就是这个简单的数字。大家可不要以为写在“entry”下面的程序也都被储存了,这是不可能的

那么“MOV SI, msg”会怎么样呢?由于在这里msg的地址是0x7c74,所以这个指令就是把0x7c74代入到SI寄存器中去。

下面我们来看“MOV AL, [SI]”。如果这个命令是“MOV AL, SI”的话,不用多说大家也都能明白它的意思,可这里用方括号把SI括了起来。如果在汇编语言中出现这个方括号,寄存器所代表的意思就完全不一样了。

这个记号代表“内存”。如果大家自己组装过电脑,就知道所谓“内存”,指的是256MB或512MB的那个零件

显示一个字符
 AH=0x0e;
 AL=character code;
 BH=0;
 BL=color code;
 返回值:无
HLT指令源自英文“halt”,意思是“停止”
用C语言改写后的helloos.nas程序节选
entry:
    AX = 0;
    SS = AX;
    SP = 0x7c00;
    DS = AX;
    ES = AX;
    SI = msg;
putloop:
    AL = BYTE [SI];
    SI = SI + 1;
    if(AL==0) goto fin;
    AH = 0x0e;
    BX = 15;
    INT 0x10;
    goto putloop;
fin:
    HLT;
    goto fin;

为什么是0x7C00?

显然0x7C00是计算机的第一个重要魔数,BIOS为什么会将MBR装载到这个内存地址?这就必须从计算机制造者IBM说起。

下面我们一起来读一篇英语文章《Why BIOS loads MBR into 0x7C00 in x86 ?》:
首先说0x7c00的前身其实是0x200,当时操作系统是在1980年出现的SCP的"86-DOS"。为什么是0x200?因为这个系统是从0x400开始放置的,而内存空间0x200-0x3FF处于中断向量的区0x00-0x3FF的末端保留空间并没有用到,所以就刚好利用了这个512B的“缝隙”地址。


重点来了:再说到0x7c00的出现是在1981年的IBM PC机5150上,一个叫“大卫XX”的开发团队成员定下的,而且还特别强调了这个事情和INTEL公司以及微软公司没有任何关系:

那怎么定下是0x7c00这个值的呢?那是为了尽量让最多的连续内存空间分配给操作系统,选择把MBR放在内存的最高端方向、另外还需要预留512B的堆栈/数据空间,因此从内存的最高处倒数过来,就计算出了MBR的存放位置:

0X7FFF(5150机器总内存空间32KB)-512B(堆栈/数据空间)-512B(MBR空间)=0X7C00。

同时还指出:一旦计算机启动起来之后,MBR区、堆栈/数据区就不再需要了,因此我们完全还可以再次回收利用:0X7C00---0X7FFF之间处于32KB最后的1024B空间,这样无疑为操作系统又扩充了可用空间。这就更加说明MBR需要放置在内存高端方向的原因:因为如果放在内存的低端方向,这部分回收的空间就很难再利用起来,原因在于程序一般都是往内存地址的增长方向向上发展的。难以想象在计算机硬件匮乏的年代,那些设计者大师真是绞尽了脑汁。
---------------------------
先制作启动区
考虑到以后的开发,我们不要一下子就用nask来做整个磁盘映像,而是先只用它来制作512字节的启动区,剩下的部分我们用磁盘映像管理工具来做,这样以后用起来就方便了。
首先我们把heloos.nas的后半部分截掉了,这是因为启动区只需要最初的512字节。现在这个程序就仅仅是用来制作启动区的,所以我们把文件名也改为ipl.nas

然后我们来改造asm.bat,将输出的文件名改成ipl.bin。另外,也顺便输出列表文件ipl.lst。这是一个文本文件,可以用来简单地确认每个指令是怎样翻译成机器语言的。

nask.exe ipl.nas ipl.bin ipl.lst

另外我们还增加了一个makeimg.bat。它是以ipl.bin为基础,制作磁盘映像文件helloos.img的批处理文件。它利用笔者自己开发的磁盘映像管理工具edimg.exe,先读入一个空白的磁盘映像文件,然后在开头写入ipl.bin的内容,最后将结果输出为名为helloos.img的磁盘映像文件。详情请参考makeimg.bat的内容。

edimg.exe   imgin:D:\myos\30days\tolset\z_tools\fdimg0at.tek   wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img
-----------------
Makefile入门
default:
    make.exe img
ipl.bin: ipl.nas
    nask.exe ipl.nas ipl.bin ipl.lst

helloos.img: ipl.bin
    edimg.exe \
    imgin:D:\myos\30days\tolset\z_tools\fdimg0at.tek \
    wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img    

asm:
    make.exe -r ipl.bin

img:
    make.exe -r helloos.img
    
clean:
    del ipl.bin
    del ipl.lst

src_only:
    make.exe clean
    -del helloos.img


        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值