x86汇编从实模式到保护模式-程序加载器

调试一天半,复刻成功!!!

;文件名:Program_Loader.asm
;文件说明:硬盘主引导扇区代码(加载程序)
;创建日期:2021-11-1

;用户程序起始逻辑扇区号
;;Main:
;设置堆栈
;计算用户程序加载的段地址
;设置程序读取的上文(先读取一个扇区,获取基本信息)
;利用基本信息,计算程序的总扇区数
;(若剩余扇区数>0)读取剩余扇区
;重置段表
;跳转至用户程序入口
;
;
application equ 10                             ;用户程序所在扇区号
section app_load align=16 vstart=0x7c00

;设置堆栈指针
mov ax,0
mov ss,ax
mov sp,0x0

;设置用户程序加载位置
mov ax,[cs:phy_base]                          ;低位
mov dx,[cs:phy_base+0x2]                      ;高位
mov bx,0x10
div bx
mov ds,ax                                     ;ds与es指向用户程序加载位置
;mov es,ax

;读取一个扇区大小的用户程序
mov di,0                                      ;扇区读取目标地址ds:di
mov si,application                            ;需读取扇区号
call read_HardDisk_0                          ;开始读取

;计算用户程序大小
mov dx,[0x2]                                  ;用户程序大小高地址
mov ax,[0x0]                                  ;用户程序大小低地址
mov bx,512                                    ;一个扇区大小
div bx                                        ;ax为商,dx为余
cmp dx,0x0                                    ;ax:所占用完整的扇区数
jnz @1                                        ;dx为0,则共需读取ax个扇区(别忘了已经读取过一个扇区)
dec ax                                        ;dx不为0,则共需读取ax+1个扇区(别忘了已经读取过一个扇区)

                                              ;判断用户程序所占扇区大小
@1:                                           ;若ax为0,则无需再读取
cmp ax,0
jz re_entry

mov cx,ax                                     ;读取剩余扇区的程序
@2:                                           ;此时di=512,ds=0x10000
inc si                                        ;si为需读取扇区号
call read_HardDisk_0                          ;读取第si个扇区
loop @2

                                              ;用户程序读取完毕
											  ;用户程序起始地址:0x10000
re_entry:                                     ;重置用户程序代码入口
mov dx,[0x8]                                  ;代码段入口高地址
mov ax,[0x6]                                  ;代码段入口低地址
call translaion_address                       ;返回段地址ax
mov [0x6],ax                                  ;重写程序入口段基址


mov di,  0xc                                  ;段基址重定位表首地址
mov cx,[0xa]                                  ;需重置段基址数量
re_section:
mov dx,[di+0x2]                               ;代码段入口高地址
mov ax,[di]                                   ;代码段入口低地址
call translaion_address                       ;返回段地址ax
mov [di],ax                                   ;重写程序入口段基址
add di,4                                      ;下一需重定位段基址
loop re_section

jmp far [0x4]                                ;跳转至用户程序入口
;----------------------------------------------------------------------------------
;硬盘读取
read_HardDisk_0:                              ;si为所需扇区号
                                              ;ds:di为目标地址
push cx

mov al,0x1                                    ;读取扇区数
mov dx,0x1f2                                  ;0x01f2端口
out dx,al
inc dx

;0x01f3端口,0x01f4端口,0x01f5端口,0x01f6端口
;LBA地址0~7,LBA地址8~15,LBA地址16~23,LBA地址24~27
;0~27:LBA28逻辑扇区
;28~31:
;28:[0:主硬盘/1:从硬盘]
;29~31:[101:CHS/111:LBA]

mov ax,si
out dx,al                                     ;0x01f3端口,LBA地址0~7
inc dx

mov al,0x0
out dx,al                                     ;0x01f4端口,LBA地址8~15
inc dx

out dx,al                                     ;0x01f5端口,LBA地址16~23
inc dx

mov al,0xe0
out dx,al                                     ;0x01f6端口,LBA地址24~27
inc dx                                        ;28~31,模式设置

mov al,0x20
out dx,al                                     ;0x01f7端口,向端口写入0x20(写命令)

;等待硬盘数据准备
wait_:
in al,dx                                      ;0x01f7端口为状态寄存器
and al,0x88                                   ;留下第7位,第4位
cmp al,0x8                                    ;第7位为0表示不忙,第4位为1表示数据以准备好
jnz wait_

mov dx,0x1f0                                  ;数据传送端口
mov cx,256

;开始读取硬盘                                 ;读取字数
read_:
in ax,dx
mov [di],ax
inc di
inc di
loop read_

pop cx

ret
;--------------------------------------------------------------------------------------
translaion_address:                           ;输入
                                              ;dx为16位高地址(2B)
											  ;ax为16位低地址(2B)
											  ;phy_base=0x10000(4B)
add ax,[cs:phy_base]						  ;低位相加,CF进位
adc dx,[cs:phy_base+0x2]					  ;高位相加,加上CF进位,由此完成32位加法
                                              ;此时dx:ax为内存中入口点代码段起始地址
                                              ;8086中仅有20位地址线(前20位有效)
											  ;所以dx:ax仅有20位有效
											  ;因为ax有16位有效
											  ;于是dx虽有16位,dx仅有最后4位有效
											  ;只取20位中高16位作为段地址
shl dx,12                                     ;dx低4位放于高4位返回
shr ax,4                                      ;保留ax高12位放于低12位返回
add ax,dx                                     ;返回段地址ax

ret
;----------------------------------------------------------------------------------
phy_base dd 0x10000                           ;用户程序加载位置,16字节对齐
db 510-($-$$) dup(0)                          ;补充满一个扇区
db 0x55,0xaa
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值