8086汇编和c语言混合实现操作系统.docx
《8086汇编和c语言混合实现操作系统.docx》由会员分享,可在线阅读,更多相关《8086汇编和c语言混合实现操作系统.docx(46页珍藏版)》请在冰豆网上搜索。
![8086汇编和c语言混合实现操作系统.docx](https://file1.bdocx.com/fileroot1/2023-1/24/fc8a301c-1fb5-4125-973c-12abf2392405/fc8a301c-1fb5-4125-973c-12abf23924051.gif)
8086汇编和c语言混合实现操作系统
实验三
一、实验目的
1.掌握TASM汇编语言与TURBOC语言汇合编程的方法。
2.实现内核与引导程序分离,掌握软盘上引导操作系统方法。
3.设计并实现一种简单的作业控制语言,建立具有较友好的控制命令的批处理原型操作系统,掌握操作系统提供用户界面和内部功能的实现方法。
二、实验内容
在实验二的基础上,进化你的原型操作系统,增加下列操作系统功能:
(1)将原型操作系统分离为引导程序和MYOS内核,由引导程序加载内核;
(2)内核由汇编语言kernal.asm和c语言kernal.c二个模块生成;
(3)利用C语言实现作业控制语言mJCB,原型操作系统在当前行显示一个指示符(可以是简单的一个字符或你的学号之类),允许用户输入一行命令(回车结束,语法由你设计),操作系统解释命令并完成相应的功能(3个以上的内置功能,如time、date、asc等,而且能执行软盘上的某个用户程序),并在你的实验报告中详细介绍你的mJCB语言的语法格式和功能。
三、实验报告
1、涉及的基础知识和实验环境工具综述、你的解决方案包括程序算法原理或流程图、程序模块说明、变量定义与作用说明、数据结构组织等
A.实验环境工具综述:
本次试验环境为VMware9平台上搭建的8086虚拟机,编译器为tasm,tcc,链接器为tlink,通过Winhex剪辑2进制文件,并用DiskWriter工具将主程序写入虚拟软盘,通过虚拟机加载软盘实现裸机运行。
B.算法原理:
a)计算机启动后,通过引导扇区加载软盘上余下的有效扇区。
b)引导扇区安装必要的中断,如8号中断用以动态显示时间,21号中断用以用户程序的返回,设置栈指针,数据段指针,建立c语言运行环境,设置计数器,用以动态显示时间。
配置完成后,转入基于c语言的控制程序的运行。
c)基于c语言的控制程序无限重复向输入端请求命令,解析命令,跳转调用对应程序,但接收到重启命令时重启,接到关机命令时进行关机。
d)系统总的实现为引导程序(osa.asm),汇编实现的c语言函数库(clib.asm),汇编实现的中断程序库(oslib.asm),c语言实现的控制台程序(osc.c)。
e)由于系统已搭建c语言运行环境,并提供了相应的库,再提供一个供用户用于链接c语言程序的启动程序,用户程序即可完全由c语言实现。
启动程序指定用户程序的偏移(如c9000.asm指定了用户的c程序偏移为9000h,可更改启动程序中的org来更改目标程序的偏移),并能够返回控制台(通过调用自己安装的int21H返回)。
C.程序模块说明:
a)clib.asm中汇编实现的c函数库
externcharcls();
功能:
清屏,清空第0页的显存
输入:
无
返回:
无
externintputs(constchar*str);
功能:
向当前光标位置输出字符串
输入:
字符串首地址
返回:
输出字符个数
externchar*gets(char*str);
功能:
读取字符串
输入:
字符串首地址
返回:
字符串首地址
externintstrlen(constchar*str);
功能:
求以0结束的字符串的长度
输入:
字符串首地址
返回:
字符从长度
externintgetchar(void);
功能:
读取字符
输入:
无
返回:
字符的ascii码
externintputchar(intc);
功能:
输出字符
输入:
字符c
返回:
字符c
externunsignedintport_in8(unsignedintport);
功能:
从port端口读入8位数据
输入:
端口号
返回:
读取的数据
externunsignedintport_in16(unsignedintport);
功能:
从port端口读入16位数据
输入:
端口号
返回:
读取的数据
externvoidport_out8(unsignedintport,unsignedintvalue);
功能:
向port端口输出8位数据value
输入:
端口号和数据
返回:
无
externvoidport_out16(unsignedintport,unsignedintvalue);
功能:
从port端口输出16位数据value
输入:
端口号和数据
返回:
无
externvoidsetp_cursor(unsignedintbh,unsignedintdh,unsignedintdl);
功能:
将第bh页的指针置于dh行dl列
输入:
页号bh,行号dh,列号dl
返回:
无
externvoidrun_pro(intord);
功能:
运行程序地址表中第ord号程序
输入:
程序序号
返回:
无
b)主控制程序:
osc.c
externcharcls();externintputs(constchar*str);
externchar*gets(char*str);
externintstrlen(constchar*str);
externintgetchar(void);
externintputchar(intc);
externunsignedintport_in8(unsignedintport);
externunsignedintport_in16(unsignedintport);
externvoidport_out8(unsignedintport,unsignedintvalue);
externvoidport_out16(unsignedintport,unsignedintvalue);
externvoidsetp_cursor(unsignedintbh,unsignedintdh,unsignedintdl);
externvoidrun_pro(intord);
voidinttostr(intx,char*str);
intstrtoint(char*str);
unsignedintbcdtodex(unsignedintx);
voidget_date(char*buf);/*yy-mm-dd*/
voidset_date(char*buf);
voidget_time(char*buf);/*hh:
mm:
ss*/
voidset_time(char*buf);
voidpre_time();
voidinterface();
intstrcmp(charstr1[],charstr2[]);
voidatoA(char*buf);
intdeal(char*buf);
intinside(intmin,intmax,intx);
intchange_time(intparas);
intislegal(inty,intm,intd);
intchange_date(intparas);
intshutdown(intparas);
intreboot(intparas);
intjc(intparas,char*buf);
charbuffer[80];
intcontrol=1;
charExit_Message[30]="\nPressanykeytoexit...";
unsignedintadd_Table[30]={0x9000,0,0xa000,0,0xb000,0};
cmain(){
intparas;
control=0;
while
(1)
{
cls();
interface();
gets(buffer);
setp_cursor(0,6,0);
paras=deal(buffer);
if(paras!
=0)
{
jc(paras,buffer);
puts(Exit_Message);
getchar();
}
}
}
c)用户程序:
cx0.ccx1.c
2、实验步骤、操作的主要过程、运行结果、屏幕画面截图等
A.实验步骤
a)编写代码
b)在dosbox上执行批处理文件0.bat,得到,执行9000.bat得到用户程序1,执行a000.bat得到用户程序2
c)使用WinHex将中7e00h到结束的所有数据写到虚拟软盘文件Tinix.img偏移为0的位置处,将用户程序1和用户程序2对应的代码写到1400h和2400h处。
d)从软盘启动虚拟机
B.屏幕截图
a)开机界面
b)时间设置
c)日期设置
d)运行程序地址表中的用户程序
e)指令参数错误检查
f)执行重启命令,由于重启时无法截图,只提供命令输入界面
g)关机指令
C.指令总结
a)Time修改cmos时间,不需要参数
b)Date修改cmos日期,不需要参数
c)Runx执行第x号用户程序,1<=x<=2,安装新程序x需扩展。
d)Reboot重启,不需要参数
e)Shutdown关机,不需要参数
四、实验心得体会
A.程序c语言环境的配置和实现,应基于编译器tcc默认的调用规范cdecl。
a)cdecl在8086上对c语言参数的入栈顺序为从右向左入栈,对栈的恢复由程序调用者caller实现,汇编实现c语言函数库时,不需要在函数中对栈进行恢复。
在汇编程序中调用c实现的函数,对参数压栈和对函数返回后要注意恢复。
b)cdecl对函数的返回值,8位,16位和32位分别存放于al、ax、dx和ax中,汇编中调用c实现的函数时返回值的获取为以上寄存器。
c)cdecl只对ds,ss,bp,si,di等寄存器进行保护。
当在汇编中调用c实现的函数时,若通用寄存器ax,bx,cx,dx中存在有意义的值,要自行进行压栈保护。
B.实现tcc的过程中,发现tcc在栈的处理上存在bug。
tcc编译tcc_bug文件得到的二进制文件,通过debug反汇编解析,发现,tcc对函数中的临时字符串buf的取值从[bx+offset]中取(产生错误的位置在tcc_bug.c文件中有标注),而bx默认的段地址为ds而不是ss。
当ds不等于ss时,会取出错误的值,产生乱码。
解决方法:
通过在引导程序中配置c语言环境时ds和ss设置相同的值,成功解决产生乱码的问题。
C.由于8086程序在dos上运行是使用int21h的4ch号功能返回控制台,本次实验对安装了仅有单个iret指令的21h中断,来实现初步对dos程序的小的兼容,用于与用户的c语言链接的启动程序使用int21h调用安装的iret进行返回控制台。
Osc.c
externcharcls();externintputs(constchar*str);
externchar*gets(char*str);
externintstrlen(constchar*str);
externintgetchar(void);
externintputchar(intc);
externunsignedintport_in8(unsignedintport);
externunsignedintport_in16(unsignedintport);
externvoidport_out8(unsignedintport,unsignedintvalue);
externvoidport_out16(unsignedintport,unsignedintvalue);
externvoidsetp_cursor(unsignedintbh,unsignedintdh,unsignedintdl);
externvoidrun_pro(intord);
voidinttostr(intx,char*str);
intstrtoint(char*str);
unsignedintbcdtodex(unsignedintx);
voidget_date(char*buf);/*yy-mm-dd*/
voidset_date(char*buf);
voidget_time(char*buf);/*hh:
mm:
ss*/
voidset_time(char*buf);
voidpre_time();
voidinterface();
intstrcmp(charstr1[],charstr2[]);
voidatoA(char*buf);
intdeal(char*buf);
intinside(intmin,intmax,intx);
intchange_time(intparas);
intislegal(inty,intm,intd);
intchange_date(intparas);
intshutdown(intparas);
intreboot(intparas);
intjc(intparas,char*buf);
charbuffer[80];
intcontrol=1;
charExit_Message[30]="\nPressanykeytoexit...";
unsignedintadd_Table[30]={0x9000,0,0xa000,0,0xb000,0};
cmain(){
intparas;
control=0;
while
(1)
{
cls();
interface();
gets(buffer);
setp_cursor(0,6,0);
paras=deal(buffer);
if(paras!
=0)
{
jc(paras,buffer);
puts(Exit_Message);
getchar();
}
}
/*control=1;*/
}
voidinttostr(intx,char*str)
{
intl=0;
inti,offset;
charbuf[10];
if(x==0)
{
str[0]='0';
str[1]=0;
return;
}
if(x<0)
{
x=-x;
offset=1;
str[0]='-';
}
else
offset=0;
while(x>0)
{
buf[l]=x%10+'0';
x=x/10;
l++;
}
for(i=0;istr[l-i-1+offset]=buf[i];
str[l+offset]=0;
}
intstrtoint(char*str)
{
intlen=strlen(str);
inti;
intsou;
intp;
intans;
if(str[0]=='-')
{
sou=1;
p=-1;
}
else
{
sou=0;
p=1;
}
ans=0;
for(i=sou;ians=ans*10+(str[i]-'0');
returnans*p;
}
unsignedintbcdtodex(unsignedintx)
{
return(x>>4)*10+(x&0xf);
}
voidget_date(char*buf)/*yy-mm-dd*/
{
unsignedinty,m,d;
port_out8(0x70,9);/*获取年*/
y=port_in8(0x71)&0xff;
buf[0]=(y>>4)+'0';
buf[1]=(y&0xf)+'0';
buf[2]='-';
port_out8(0x70,8);/*获取月*/
m=port_in8(0x71)&0xff;
buf[3]=(m>>4)+'0';
buf[4]=(m&0xf)+'0';
buf[5]='-';
port_out8(0x70,7);/*获取日*/
d=port_in8(0x71)&0xff;
buf[6]=(d>>4)+'0';
buf[7]=(d&0xf)+'0';
buf[8]=0;
}
voidset_date(char*buf)
{
unsignedinty,m,d;
y=((buf[0]-'0')<<4)+buf[1]-'0';
port_out8(0x70,9);
port_out8(0x71,y);
m=((buf[3]-'0')<<4)+buf[4]-'0';
port_out8(0x70,8);
port_out8(0x71,m);
d=((buf[6]-'0')<<4)+buf[7]-'0';
port_out8(0x70,7);
port_out8(0x71,d);
}
voidget_time(char*buf)/*hh:
mm:
ss*/
{
unsignedinth,m,s;
port_out8(0x70,4);/*获取时*/
h=port_in8(0x71)&0xff;
buf[0]=(h>>4)+'0';
buf[1]=(h&0xf)+'0';
buf[2]=':
';
port_out8(0x70,2);/*获取分*/
m=port_in8(0x71)&0xff;
buf[3]=(m>>4)+'0';
buf[4]=(m&0xf)+'0';
buf[5]=':
';
port_out8(0x70,0);/*获取秒*/
s=port_in8(0x71)&0xff;
buf[6]=(s>>4)+'0';
buf[7]=(s&0xf)+'0';
buf[8]=0;
}
voidset_time(char*buf)
{
unsignedinth,m,s;
h=((buf[0]-'0')<<4)+buf[1]-'0';
port_out8(0x70,4);
port_out8(0x71,h);
m=((buf[3]-'0')<<4)+buf[4]-'0';
port_out8(0x70,2);
port_out8(0x71,m);
s=((buf[6]-'0')<<4)+buf[7]-'0';
port_out8(0x70,0);
port_out8(0x71,s);
}
voidpre_time()
{
charbuf[20];
{
/*setp_cursor(0,1,62);*/
/*puts("Time:
");*/
setp_cursor(0,1,70);
get_time(buf);
puts(buf);
/*setp_cursor(0,2,62);*/
/*puts("Date:
");*/
setp_cursor(0,2,70);
get_date(buf);
puts(buf);
}
}
voidinterface()
{
inti;
intpos[4];
pos[0]=0;
pos[1]=8;
pos[2]=60;
pos[3]=79;
cls();
setp_cursor(0,0,0);
for(i=0;i<80;++i)
putchar('=');
for(i=0;i<4;++i)
{
setp_cursor(0,1,pos[i]);
putchar('|');
}
for(i=0;i<4;++i)
{
setp_cursor(0,2,pos[i]);
putchar('|');
}
setp_cursor(0,3,0);
for(i=0;i<80;++i)
putchar('=');
setp_cursor(0,1,2);
puts("Lab03");
setp_cursor(0,1,62);
/*把以下部分放到pre_time,乱码,寻址问题?
*/
puts("Time:
");
setp_cursor(0,2,62);
puts("Date:
");
pre_time();
setp_cursor(0,2,30);
puts("MYOS3.0");
setp_cursor(0,4,0);
puts("Inputcommand:
");
setp_cursor(0,5,0);
for(i=0;i<80;++i)
putchar('-');
setp_cursor(0,4,14);
}
intstrcmp(charstr1[],charstr2[])
{
intlen1=strlen(str1);
intlen2=strlen(str2);
inti=0,j=0;
while(i{
i++;
j++;
}
return(str2[j]-str1[i]);
}
voidatoA(char*buf)
{
intlen=strlen(buf);
inti;
for(i=0;iif('a'<=buf[i]&&buf[i]<='z')buf[