汇编语言编写一个可以自行启动计算机不需要在现有操作系统环境中运行的程序.docx
《汇编语言编写一个可以自行启动计算机不需要在现有操作系统环境中运行的程序.docx》由会员分享,可在线阅读,更多相关《汇编语言编写一个可以自行启动计算机不需要在现有操作系统环境中运行的程序.docx(34页珍藏版)》请在冰豆网上搜索。
![汇编语言编写一个可以自行启动计算机不需要在现有操作系统环境中运行的程序.docx](https://file1.bdocx.com/fileroot1/2023-4/16/0b137c62-0d5c-446d-bdd7-5b9dc402e7ed/0b137c62-0d5c-446d-bdd7-5b9dc402e7ed1.gif)
汇编语言编写一个可以自行启动计算机不需要在现有操作系统环境中运行的程序
1、课程设计的目的、任务
1.1目的
编写一个可以自行启动计算机,不需要在现有操作系统环境中运行的程序。
1.2任务
该程序完成的功能如下:
①列出功能选项,让用户通过键盘进行选择,界面如下。
resetpc;重新启动计算机
startsystem;引导现有操作系统
clock;进入时钟程序
setclock;设置时间
②用户输入“1”后重新启动计算机。
③用户输入“2”后引导现有的操作系统。
④用户输入“3”后,执行动态显示当前日期、时间的程序。
显示格式:
年/月/日时:
分:
秒,当按下F1键后,改变显示颜色;按下Esc键后,回到主选单。
⑤用户输入“4”后可更改当前日期、时间,更改后返回到主选单。
2、软件需求分析和设计
2.1分析
①学习汇编语言的主要目的:
其一,为了“深入理解机器工作的基本原理”,其二,“培养底层编程意识和思想”。
为了提高自己对计算机工作基本原理的认识和培养底层编程思想,所选题目在脱离当前操作系统的环境下运行,摆脱了操作系统的束缚,实现了真正的对硬件编程。
②在DOS下编写安装程序,在安装程序中包含任务程序;
③运行安装程序,将任务程序写到软盘上;
④为了让任务程序可以在开机后自动执行,需要将任务程序写到软盘的0道0面1扇区上(程序最后两个字节以55AA作为引导程序结束标志)。
如果程序长度大于512字节,则需要用多个扇区来存放,这种情况下,处于软盘0道0面1扇区中的程序就必须负责将其他扇区中的内容读入内存。
2.2设计
(1)程序主体包含三个程序段,安装程序段(setupsg)、引导程序段(initsg)、任务程序段(syssg)。
(2)安装程序流程图:
安装程序完成功能:
①将引导程序写入软盘第一扇区。
②将任务程序写入软盘第二扇区开始以后的3个扇区。
其中引导程序从0x7C00到0x7E00共512字节(写在软盘的第一扇区);任务程序从0x0000到0x0485共1157字节(写在软盘的第二、三、四扇区),所以引导程序、任务程序共占用软盘的4个扇区即可。
(3)引导程序流程图:
引导程序完成功能:
①位于软盘第一扇区的引导程序,开机后被BIOSINT19H中断服务程序加载至内存0x07C00位置。
②接着,引导程序负责将软盘中从第二扇区开始的3个扇区的任务程序加载到内存0:
8000H处,并设置CS:
IP指向0:
8000H处。
③引导程序必须是512字节,且以55AA作为结束标志,这样BIOSINT19H中断才会认为该扇区的数据是引导程序,并将其加载至内存0x7C00处。
(4)任务程序流程图:
任务程序完成功能:
①显示程序主菜单。
②根据用户输入,调用对应子程序。
其中调用对应子程序采用子程序入口地址直接定址表,定义如下:
systabledwsys_reset,sys_start,sys_clock,sys_setclock
CALLWORDPTRsystable[bx]
当用户输入序列号:
1(31H)→减30H→1→赋值给BL,BH清零→ADDBX,BX→(BX)=2,所以由此可以得到序列号1对应子程序入口地址在表中的偏移地址,然后直接调用对应子程序。
(5)子程序1:
重新启动计算机
①设置CS:
IP指向FFFF:
0单元,此处有一条跳转指令。
②CPU执行该指令后,转去执行BIOS硬件系统检测和初始化程序。
③初始化程序完成,建立BIOS所支持的中断向量,即将BIOS提供的中断服务程序的入口地址登记在中断向量表中,以及安装BIOS中断处理程序。
④硬件系统检测和初始化完成后,CPU接收到一个BIOSINT19H中断,CPU根据中断类型号在中断向量表中找到该中断处理程序的入口地址,跳转执行INT19H的中断进程,即将硬盘(或软盘,根据BIOS开机启动项的设置)的0道0面1扇区的512字节加载至内存0:
7C00,进行操作系统的引导。
(6)子程序2:
引导现有操作系统(从本地硬盘,默认为C盘)
①读取硬盘C的0道0面1扇区512字节数据到内存0:
7C00。
②将CS:
IP指向0:
7C00,使CPU转去执行引导程序(即①中加载至内存0:
7C00处的程序)。
(7)子程序3:
读取时钟
①读取系统当前日期、时间。
系统日期以BCD码形式存放在CMOSRAM存储时间单元,单元地址和日期对应关系如下:
0号单元:
秒、2号单元:
分、4号单元:
时、
7号单元:
日、8号单元:
月、9号单元:
年。
②将系统日期的BCD码形式转换为ASCII码,并动态显示时间。
③BIOSINT08H时钟中断属于硬中断,每1/18.5秒发生一次,其执行的任务程序由BIOS程序控制,但是为了实现动态显示时间,将子程序3入口地址设置在INT08H中断向量表中,子程序返回时还原INT08H中断向量表中断处理程序的入口地址。
(8)子程序4:
设置时钟
①DS:
SI指向用户输入的时间字符串。
②将时间(两个字节表示的ASCII码)转换为一个字节表示的BCD码形式,写入到CMOS对应的时间存储单元。
(9)子程序5:
字符栈的入栈、出栈和显示。
①(ah)=功能号,0表示入栈、1表示出栈、2表示显示。
②DS:
SI指向字符栈空间:
对于0号功能:
(al)=入栈字符;
对于1号功能:
(al)=返回的字符;
对于2号功能:
(dh)、(dl)=字符串在屏幕上显示的行、列位置,(cl)=颜色。
(10)子程序6:
字符的输入、输出、显示。
调用子程序4实现字符的输入、输出、显示。
(11)子程序7:
显示以0结尾的字符串。
①入口参数:
(dh)=行号,(dl)=列号,(cl)=颜色。
②DS:
SI指向字符串首地址。
(12)子程序8:
清除屏幕。
①将80*25屏幕偶地址字节单元清0。
②将80*25屏幕奇地址字节单元赋值07h
3、程序实现说明
3.1程序实现过程
(1)各个程序段:
以下为各个程序段的大小及长度,对齐方式para(Paragraph节)各个程序段起始地址对齐到para,1para=16Bytes。
引导程序(initsg)512字节,任务程序(syssg)1157字节。
(2)安装程序:
负责将引导程序写入软盘第一扇区、任务程序写入软盘第二扇区开始以后的3个扇区。
以下为安装程序的关键代码:
(3)引导程序:
负责将软盘第二扇区开始的3个扇区数据加载至内存0:
8000H处,加载结束后并设置CS:
IP指向0:
8000H,使CPU转去执行任务程序。
以下为设置CS:
IP指向0:
8000H,接着重复定义数据保证引导程序为512字节,且最后两个字节以55AA结束。
(4)任务程序:
①共包含8个子程序,主要完成4个功能。
重新启动计算机、引导当前操作系统、动态显示系统时间、设置系统时间。
②首先显示主菜单,使用BIOSINT16H中断的0号功能,等待用户输入功能号;接着,处理用户输入,将用户输入的功能号(1-4)转换为(0-3)在子程序入口地址表中,依据偏移量调用对应子程序;最后,子程序调用结束返回主菜单继续等待用户输入。
③设置系统时间调用了:
子程序6:
字符的输入、输出、显示(调用子程序5完成,子程序5:
字符栈的入栈、出栈和显示);
子程序7:
显示以0结尾的字符串;
子程序8:
清除屏幕。
3.2关键代码分析
(1)子程序5:
字符栈的入栈、出栈和显示
①字符入栈:
0号功能,(AL)=入栈字符。
②字符出栈:
1号功能,(AL)=出栈字符。
③显示字符:
2号功能,(dh)、(dl)=字符串在屏幕上显示的行、列位置,(cl)=颜色。
从栈底0号单元依次显示栈中字符,直到栈中字符全部输出。
(2)子程序6:
字符的输入、输出、显示
①调用BIOSINT16H中断的0号功能等待用户键盘的输入;
②将键盘缓冲区中的ASCII码赋值AL,判断AL,如果小于20H,则说明不是字符,跳转非字符处理,大于20H,则按字符处理;
③非字符处理,如果是退格键,则调用子程序5的1号功能字符出栈,然后显示栈中所有字符;如果是回车键,则调用子程序5的0号功能字符入栈,将0入栈,显示栈中所有字符;
④字符处理,调用0号功能将字符入栈、2号功能显示栈中所有字符。
4、程序总结
4.1软件完成情况
(1)由于任务程序需要写入软盘,在开机时设置开始启动项为软盘引导,进而运行软盘中的任务程序。
首先,现在大多数计算机都没有软盘驱动器,而软盘也不常用,所以采用将程序通过虚拟软驱写入软盘镜像(.img格式,1.44M),也就是用虚拟软驱来代替软盘驱动器,软盘镜像文件代替软盘。
其次,将写好的软盘镜像文件加载到虚拟机的软驱中,并设置虚拟机开机启动顺序,软驱优先。
(虚拟机采用OracleVMVirtualBox)
最后,将软盘镜像文件作为引导盘,启动虚拟机读取软盘镜像文件,运行软盘镜像文件中的程序。
(2)程序并没有如期在虚拟机中得到正确运行。
首先,问题1:
不能确定引导程序和任务程序是否已经写入软盘镜像文件。
其次,问题2:
对虚拟软驱,软盘镜像文件的不了解。
最后,问题3:
虚拟机的配置。
4.2收获与不足
(1)收获
通过对程序的调试,提高了汇编语言程序设计能力,查阅资料,了解了操作系统启动的步骤,促使自己对操作系统、组成原理等核心等课程的学习。
(2)不足
程序还没有能在虚拟机中正常运行,希望查阅更多资料,完成配置,使其得到运行。
想法:
如果将程序写入USB闪存存储器,程序在真实主机中便可运行。
源代码:
;=======================================================================
;安装程序
;将任务程序写入到软盘
;=======================================================================
;=======================================================================
;BIOS中断INT10H:
功能03H
;功能描述:
写扇区
;入口参数:
AH=03H
;AL=扇区数CH=柱面CL=扇区DH=磁头DL=驱动器,00H~7FH:
软盘;80H~0FFH:
硬盘
;ES:
BX=缓冲区的地址
;出口参数:
CF=0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码
;=======================================================================
setupsgsegment
assumecs:
setupsg
setup:
movax,initsg;ES:
BX指向引导程序开始处
moves,ax
leabx,init
;将引导程序安装到第一扇区
;任务程序从第二扇区开始安装(需要3个扇区)
movah,03h;功能号03h,写扇区
moval,04h;写扇区数,4个扇区
movch,00h;磁道号,0道
movdh,00h;磁头号(面),0面
movcl,01h;扇区号,1号
movdl,00h;驱动器号,0号软驱
int13h
movax,4c00h;安装结束,返回DOS系统
int21h
setupsgends
;=======================================================================
;BIOS中断INT10H:
功能02H
;功能描述:
读扇区
;入口参数:
AH=02H,其他参数参照功能03H
;=======================================================================
;=======================================================================
;引导程序,安装在软盘0道0面第一扇区,引导程序必须是512字节(一个扇区)且以55AA作为结束标志
;开机启动后第一扇区的引导程序被BIOSint19中断加载至内存0:
7C00处
;该引导程序负责将软盘中其他扇区中的子程序加载至内存0:
8000处,并设置CS:
IP指向0:
8000
;=======================================================================
initsgsegment
assumecs:
initsg
org7c00h
init:
cli;避免设置堆栈时中断,关中段(IF=0)
movax,0;设置栈顶指向0:
7C00
movss,ax
movsp,7c00h
sti;开中断(IF=1)
movax,0
moves,ax;读取软盘第二扇区和以后扇区的子程序到内存0:
8000处
movbx,8000h
movah,02h;功能号,读扇区
moval,03h;读扇区数,3个扇区
movch,00h;磁道号,0道
movcl,02h;扇区号,2号
movdh,00h;磁头号(面),0面
movdl,00h;驱动器号,0号软驱
int13h
movax,00h;设置CS:
IP
movbx,8000h
pushax
pushbx
retf;用栈中数据同时改CS,IP,远转移
;相当于:
;popip
;popcs
numbersequoffsetinit-$
timesdb510-(numbers)dup(20h)
;凑足510字节
;$表示当前行被汇编后的偏移地址
dW55AAH;剩余两个字节由55AA填充
initsgends
;$$表示一个节的开始处被汇编后的地址。
在这里程序只有1个节,
;所以$$实际上就表示程序被编译后的开始地址,也就是0x7C00
;$-$$,它表示本行距离程序开始处的相对距离
;=======================================================================
;第一扇区以后的数据
;子程序
;包含所有菜单需要调用的子过程
;=======================================================================
syssgsegment
assumecs:
syssg
menu:
jmpnearptrmenushow
int8hdw0,0
charstacksdb128dup(0);字符栈
topdw0;字符栈栈顶
menudatadwoffsetmd0,offsetmd1,offsetmd2,offsetmd3,offsetmd4,offsetmd5,offsetmd6
md0db"-------------------------",0
md1db"Welcome",0
md2db"1)ResetPC",0
md3db"2)StartSystem",0
md4db"3)Clock",0
md5db"4)SetClock",0
md6db"-------------------------",0
systabledwsys_reset,sys_start,sys_clock,sys_setclock
menushow:
movdh,5
movdl,30
xorbx,bx
movax,cs
movds,ax
movcx,7
menushow_s:
pushcx
movsi,menudata[bx]
movcl,02h
callsys_showstr
addbx,2
adddh,2
popcx
loopmenushow_s
;处理用户输入
sys_input:
movah,0
int16h
xorbx,bx
subal,30h;ASCII码转为功能号
movbl,al
subbl,1;1-4转换为0-3
cmpbx,0
jbcycle
cmpbx,3
jacycle
addbx,bx
callWORDptrsystable[bx];调用菜单功能
cycle:
jmpshortsys_input
;=======================================================================
;子程序0:
重新启动计算机
;①设置CS:
IP指向FFFF:
0单元,此处有一条跳转指令。
;②CPU执行该指令后,转去执行BIOS硬件系统检测和初始化程序。
;③初始化程序完成,建立BIOS所支持的中断向量,
;即将BIOS提供的中断服务程序的入口地址登记在中断向量表中,以及安装中断处理程序
;④硬件系统检测和初始化完成后,CPU接收到一个int19中断,进行操作系统的引导。
;=======================================================================
sys_reset:
movax,0ffffh
pushax
movax,0h
pushax
retf
;=======================================================================
;子程序1:
引导现有操作系统(从本地硬盘,默认为C盘)
;①读取硬盘C的0道0面1扇区到0:
7C00。
;②将CS:
IP指向0:
7C00。
;=======================================================================
sys_start:
movax,0
moves,ax
movbx,7C00h
movah,02h;功能号,读扇区
moval,01h;读扇区数,1个扇区
movch,00h;磁道号,0道
movcl,01h;扇区号,1号
movdh,00h;磁头号(面),0面
movdl,80h;驱动器号,硬盘驱动器号80h,表示C盘
int13h
;设置CS:
IP指向0:
7C00
movax,00h
movbx,7C00h
pushax
pushbx
retf
;=======================================================================
;子程序2:
读取时钟
;①读取系统当前日期、时间(BCD码形式存放)
;CMOSRAM的存储时间单元的地址:
;0:
秒、2:
分、4:
时、7:
日、8:
月、9:
年
;②将时钟BCD码转换为ASCII码,并动态显示时间
;(将该程序入口地址设置在int8h中断向量表中,程序返回时还原int8h中断入口地址)
;=======================================================================
sys_clock:
cli
movax,0
moves,ax
pushes:
[8*4]
popint8h;保存原int8h中断入口地址
pushes:
[8*4+2]
popint8h+2
movWORDptres:
[8*4],offsetstart;设置新int8h中断入口偏移地址
moves:
[8*4+2],cs;设置新int8h中断入口段地址
sti
start:
callcls
jmpshortclockread
clockdatadwoffsetcl1,offsetcl2,offsetcl3
clockcolordb02h
cl1db'yy/mm/ddhh:
mm:
ss',0
cl2db'pressEscreturnmenu.'
cl3db'pressF1changecolor.'
clocktabledb9,8,7,4,2,0;各时间量在CMOSRAM中存放单元
clockread:
xorsi,si
xordi,di
movcx,06h;循环读取6次
loop0:
pushcx;保存外循环计数器
moval,clocktable[si];CMOS9(8\7\4\2\0)存储单元偏移地址赋值al
out70h,al;将要访问的CMOS存储单元写到70h地址端口
inal,71h;从71h数据端口读取数据到al寄存器中
movah,al;将读取到的时间BCD码形式赋值ah
movcl,4;BCD码→ASCII码
shrah,cl;ah中为时间的十位数码值
andal,00001111b;al中为时间的个位数码值
addah,30h
addal,30h
movcl1[di],ah
movcl1[di+1],al
adddi,3
incsi
popcx;还原外循环计数器
looploop0
clockprint:
movdh,6
movdl,30
movbx,0
movax,cs
movds,ax
movcx,3
clockprint_s:
pushcx
movsi,clockdata[bx]
movcl,clockcolor