使用makefile文件

那么用批命令来编译程序是不是就够用了?还不够,这里我们要使用批命令+makefile文件方法。

首先介绍如何写makefile

打开notepad++写入以下内容:

######################

#声明要编译的所有组成,这里的ya是本工程名称,可以取任何名字,这里就用ya

######################

ya:out/Helloworld.exe out/first.com

#开始对各部分编译

out/Helloworld.exe:code/Helloworld.c

    gcc code/Helloworld.c -o out/Helloworld.exe  

out/first.com:code/first.asm

    nasm code/first.asm -o out/first.com   

###################### 

注意倒数一、三行前面的空格是按Tab键,否则出错:makefile:7: *** missing separator. stop

将此文件保存在D:\GX\ya目录下,文件名就是Makefile

djgpp.bat的内容改为:djredir -o 30.txt -eo make

双击djgpp.bat编译。

现在的目录结构是这样:

 

clip_image001

 

boot程序

 

看看有名的boot程序

电脑开始启动后,首先在磁盘0号磁道0号扇区处寻找boot程序,这个程序只有512字节大小,如果是以0x550xAA两个字节结尾,电脑将认为这是一个boot程序,然后电脑将这个程序读入内存地址0xfc00:0000处开始执行。

一个最简单的boot.asm

[BITS16]                                                   ;;编译成16位的指令

[ORG0x7C00]

po:

jmp po

times510-($-$$)db0

db0x55

db0xAA

 

 

将它保存到code目录下

我们分析一下这个程序

这个程序什么都没有做,但是有完整的boot格式,以0x550xAA两个字节结尾,那么程序还有510个字节。

$表示本指令地址 $$表示程序开始地址

times 510-($-$$) db 0 就是填入510-(本指令地址-程序开始地址)这么多个0

我们给它中间加点语句,让它在屏幕上显示Hello World

[BITS16]  

[ORG0x7c00]

 

jmp main

ns db0x48,0x65,0x6C,0x6C,0x6F,0x20,0x77,0x6F,0x72,0x6C,0x64,0x21,   ;这几个是hello world!ASCII

main:

movax,cs

movds,ax

moves,ax

movcx,12           ;循环12

movbx,0            ;从数组[0]开始

movah,0eh

next:

moval,[ns+bx]

int10h

incbx

deccx

jnz next              ;cx不为0则继续

po:

jmp po

times510-($-$$)db0

db0x55

db0xAA

 

 

 

这里我们生成可执行文件格式

nasm code/boot.asm -o out/boot.bin

相应的修改makefile文件,这个大家会了吧,使用批命令+makefile生成boot.bin

下面我们将在虚拟机上模拟boot文件从软盘上启动的效果。

 

安装bochs虚拟机

Bochs可以实现虚拟软盘、虚拟硬盘、显示器、网卡等硬件模拟。模拟网卡就是我们首先要实现的目标。bochs的一个最大好处就是可以调试程序。

Bochs官网http://sourceforge.net/projects/bochs/files/bochs/2.6.8/下载Bochs2.6.8.exe,这个就是为win32准备的。

clip_image002

 

安装到D:\GX目录下。生成目录D:\GX\Bochs-2.6.8

Bochs-2.6.8目录下新建myos目录,这个目录将是我们模拟软盘文件所在地方。

 

运行bochs需要自己编辑两个程序:bat文件及bochsrc.bxrc,前者是批处理命令,后者是bochs要求的配置文件

1.bat文件内容:

cd "D:\GX\Bochs-2.6.8\myos"

..\bochs -q -f bochsrc.bxrc

放在D:\GX\Bochs-2.6.8\myos目录下。

bochsrc.bxrc
内容:

###############################################################

# bochsrc.txt file for flopy image.

###############################################################

 

# how much memory the emulated machine will have

megs: 32

 

# filename of ROM images

romimage: file=../BIOS-bochs-latest

vgaromimage: file=../VGABIOS-lgpl-latest

 

# what disk images will be used

floppya: 1_44=a.img, status=inserted

 

# choose the boot disk.

boot: a

 

# where do we send log messages?

log: bochsout.txt

 

# the mouse

mouse: enabled=0

 

bochs运行第一个启动盘
准备
安装完成后在它的目录中找到一个名为bximage.exe的程序,这个程序可以为我们创建磁盘镜像文件。
运行bximage.exe,它为我们创建了一个名为a.img的文件,这个文件是一张容量为1.44m的软盘的镜像。

please choose one  输入1

第二个问题输入fd

第三个、第四个问题直接回车

生成一个a.img软盘镜像,将a.img放到D:\GX\Bochs-2.6.8\myos目录下。

用磁盘编辑工具查看a.img内容,为1474560字节,全是0

现在的目录结构是这样:

 

clip_image003


前面已经编译了一个名为boot.bin的文件,大小正好为512字节。
用磁盘编辑工具打开这个文件,最后两个字节为55aa
然后,用磁盘编辑工具编辑器打开a.img,把boot.bin中的内容全部复制到其中。按下面方法:
(在boot.bin里)编辑——全部复制——标准。然后光标放在a.img00处,最左上角,编辑——剪贴板数据——写入(W

剪贴板数据将写入在偏移量0——确定

这样就覆盖了前512字节——0x0000x1ff字节。总字节数仍然是1474560字节。

插入数据后的a.img

clip_image004

文件——保存

OK,我们的启动软盘就制作好了。双击D:\GX\Bochs-2.6.8\myos目录下的1.bat,启动盘运行并显示Hello world!

clip_image005

 

bochs调试方法
建立一个批命令文件2.bat,内容:

cd "D:\GX\Bochs-2.6.8\myos"

..\bochsdbg -q -f bochsrc.bxrc

 

放在D:\GX\Bochs-2.6.8\myos目录下。

(使用bochsdbg.exe bochsdbg文件复制到imgbochsrc.bxrc所在文件夹中,双击bochsdbg 这个办法在同时安装2.62.6.8时会有冲突,选择使用批命令方法)选择start->c


b 
设置断点

比如:b  0x7c00  0x7c00处设置断点 
c    continue
命令运行到断点
s    step   
单步执行

p    跳过这一步

比如运行到call .55 而你不想查看这个子程序的运行,直接用p + 回车就可以跳过。
h    help
s n  
执行n
u /20 
在当前语句下反汇编20
u /20 0x7c00 :
反汇编内存0x7c00处,反汇编的长度是20
x /20 0x7c00:
16进制的形式从内存的0x7c00开始显示20个字的数据
r   
显示寄存器值

新建目录:D:\GX\ya\final  这里用来存放我们软盘的映像文件a.img

 

辅助工具
为了得到a.img我们要运行bximage.exe,然后用磁盘编辑工具打开复制数据,在开发过程中会多次重复这些过程,每次都要手工操作很麻烦,下面编写几个辅助工具代替手工操作

辅助工具一creat_img.c   用来生成映像文件a.img

 

#include <iostream>

#include <stdio.h>

#include <string>

#include<stdlib.h>

//argv[1]=目标文件创立镜像文件, 内容全为0,大小1474560

using namespace std;

 

int main (int argc,char* argv[])

{

  cout<<"reference argc is "<< argc << endl;

  int i;

  for(i =1; i <= argc; i++)

  {

    printf("argv[%d]=%s\n",i,argv[i]);

  }

FILE *tof;

   tof = fopen( argv[1],"rb");        //只读方式打开一个binary目标文件

 if( tof ==NULL)            // 第一次需创建目标文件

   {

     printf("\nerror on open file,will creat new file\n");

     tof = fopen (argv[1],"wb");

     fseek(tof,1474559, SEEK_SET);//相当于写入那么多0

     putc(0, tof);         // 写第1474560字节,1.44M软盘大小

   }

printf("done\n");

fclose(tof);     //关文件禁止读写才能对文件删除及改名

system("pause");   //为避免运行窗口关闭以便看清内容

return0;

}

 

 

creat_img.c文件放在code目录里。

相应的须修改makefile文件:

######################

#声明要编译的所有组成,这里的ya是本工程名称,可以取任何名字,这里就用ya

######################

ya:out/boot.bin out/creat_img.exe A

#开始对各部分编译,注意不是空格是Tab

 

out/boot.bin:code/boot.asm

    nasm code/boot.asm -o out/boot.bin

 

# 制作内核映象文件

out/creat_img.exe:code/creat_img.c

    gpp code/creat_img.c -o out/creat_img.exe

# 执行dos命令,在final目录下生成a.img文件

A:

    out/creat_img.exe final/a.img

######################

 

运行djgpp.bat,可以看到在final目录下生成了一个叫a.img、大小为1.44M的空白文件,目前a.img可以认为是一个空白盘,还不能起引导作用。

 

辅助工具二write_in_img.c  用来向a.img中写入数据。

#include <iostream>

#include <stdio.h>

#include <string>

#include <stdlib.h>

//argv[1]=目标文件 argv[2]=源文件  argv[3]=写入偏移量   DOS下用法: write.exe a.img kernelloader.bin 512

 

using namespace std; 

 

long get_file_size( FILE *fp )

{

    if( fp ==NULL)return-1;

    fseek(fp,0L, SEEK_END );

    return ftell( fp );

}

 

int main (int argc,char* argv[])

{

 

  int i;

  int ch;

  int k = atoi(argv[3]);  // 转换得到写入偏移量

 

FILE *fromf,*tof,*temp;

    fromf  = fopen( argv[2],"rb");  //只读方式打开一个binary源文件

    tof = fopen( argv[1],"rb");        //只读方式打开一个binary目标文件

 if( tof ==NULL)            // 第一次需创建目标文件

   {

     printf("\nerror on open file,use creat_img.exe to creat img file\n");

     exit(0);

   }

    temp = fopen("final\\t.img","ab");        //追加方式打开中间文件,如不存在就新建

    fseek(tof,0L, SEEK_SET );         //从新定位到文件开头

    for(int i =0; i < k; i++)          // 保留目标文件第一部分

    {

      ch = getc( tof );

      putc(ch, temp);

    }

 

    long ja = get_file_size( fromf );

    fseek(fromf,0L, SEEK_SET );         //定位到源文件开头

    for(int i =0; i < ja; i++)          // 复制文件第二部分

    {

      ch = getc( fromf );

      putc(ch, temp);

    }

 

    fseek ( tof, ja+k, SEEK_SET );        //指针指向从文件头开始ja+K个字节

    ch = getc( tof );

    i =0;

    while( ch != EOF )                    // 复制最后的第三部分

    {

    putc(ch, temp);     // 写入文件

    ch = getc( tof );

    i++;

    }

 

fclose(tof);     //关文件禁止读写才能对文件删除及改名

fclose(fromf);

fclose(temp);

remove(argv[1]);

rename("final\\t.img",argv[1]);

return0;

}

 

 

 

write_in_img.c文件放在code目录里。

相应的须修改makefile文件:

 

######################

#声明要编译的所有组成,这里的ya是本工程名称,可以取任何名字,这里就用ya

######################

ya:out/boot.bin out/creat_img.exe out/write_in_img.exe A B

#开始对各部分编译,注意不是空格是Tab

 

out/boot.bin:code/boot.asm

    nasm code/boot.asm -o out/boot.bin

 

# 制作内核映象文件

out/creat_img.exe:code/creat_img.c

    gpp code/creat_img.c -o out/creat_img.exe

# 执行dos命令,在final目录下生成a.img文件

A:

    out/creat_img.exe final/a.img

 

# 写入文件,argv[1]=目标文件 argv[2]=源文件  argv[3]=写入偏移量  

#DOS下用法: write.exe a.img boot.bin 0

out/write_in_img.exe:code/write_in_img.c

    gpp code/write_in_img.c -o out/write_in_img.exe

# 执行dos命令,向a.img写入代码,内容是boot.bin

#写入磁盘位置从0偏移量起始,1个扇区512字节

B:

    out/write_in_img.exe final/a.img out/boot.bin 0

######################

 

 

运行djgpp.bat,现在final目录下生成了a.img,不再是一个空白盘,而是一个启动盘。将a.img复制到D:\gx\Bochs-2.6.8\myos目录下,双击1.bat,是不是显示Hello world?说明这两个辅助工具起作用了,免去了我们的手工操作。

运行djgpp.bat实际上是运行了makefile文件,出现DOS窗口后需要按回车键才能关闭DOS窗口。

 

 

posted on 2015-11-06 15:16  ya20151015  阅读(185)  评论(0编辑  收藏  举报