第3天 进入32位模式并导入C语言

这篇博客讲述了如何从实模式进入32位模式,包括读盘操作,从启动区执行操作系统,32位模式的前期准备,以及如何在32位模式下导入和调用C语言函数。通过读盘实现程序装载,并详细解释了磁盘结构和内存映射。最后,讨论了C语言与汇编的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、读盘

昨天写的IPL并没有装载程序,今天我们来写一个可以真正装载程序的IPL。

我们先来看软盘结构。

如图所示。一张软件有正反两面,对应读取用的磁头(0,1),而从外到内又分为80个环(0~79),称为柱面。每个柱面又分为18个扇区(1~18)。因为软盘的第一个扇区(正面的第0个柱面的第1个扇区)为启动区,所以我们读软盘的时候应该从第2个扇区开始读。

org 0x7c00

jmp init

DB	0x90
DB	"HARIBOTE"		
DW	512				
DB	1				
DW	1				
DB	2				
DW	224				
DW	2880			
DB	0xf0			
DW	9				
DW	18				
DW	2				
DD	0				
DD	2880			
DB	0,0,0x29		
DD	0xffffffff		
DB	"HARIBOTEOS "	
DB	"FAT12   "		
RESB	18				

init:
	mov bx,0
	mov ax,0x0820
	mov es,ax	;es:bx为缓冲地址,即读取的数据将存取在这里
	mov dl,0	;驱动器号0(即光驱号,现在一般只有一个光驱)
	mov dh,0	;磁头号0~1
	mov ch,0	;柱面号0~79
	mov cl,2	;扇区号1~18
	mov al,1	;要读的扇区数
	mov si,0	;读盘错误次数
	
read:
	
	mov ah,0x02	;读盘
	int 0x13	;中断,BIOS的0x13号函数
	jnc finish	;jnc指令(jump if not carry),如果上一步没错误,则cf标志位为0,当cf = 0时,跳转到fin
	inc si		;错误次数加1
	cmp si,5
	jae error	;jae指令(jump if above or equal)
	
	;重置驱动器
	mov ah,0
	mov dl,0
	int 0x13	;重置驱动器
	jmp read
	
finish:
	hlt
	jmp finish
	
error:
	mov ax,0
	mov ds,ax
	mov si,msg
	mov di,msg
	mov ah,0x0e
	mov bx,15
	
show:
	mov al,[di]
	inc di
	cmp al,0
	je finish
	int 0x10
	jmp show
	
msg:
	db 0x0a,0x0a
	db "5 times,failed!!"
	db 0x0a
	db 0
	
resb 0x7dfe - $

db 0x55,0xaa
读软盘时所需要设置的寄存器如下:

ah=0x02(读盘)

al=n(表示要读的扇区的个数)

ch=柱面号

cl=扇区号

dh=磁头号

dl=驱动器号(现在一般只有一个驱动器,故只要为0就OK了~)

es:bx=缓冲地址(即将讲到的数据存至内存的这个位置)

int 0x13的返回值:cf = 0 或cf = 1,有错误则cf标志位为1;无错误则为0。


至于我们设定的将数据装载到es=0x0820,bx=0,即内存0x8200处,是我们随意设定的,因为0x7c00~0x7dff为启动区,而在0x7e00后到0x9fbff都是没有特别用途的,我们可以随便使用。

因为软件这东西太不靠谱了,所以很容易出错,我们设定最多读5次,否则就提示错误。

好了,现在我们已经读了一个扇区了,接下来,我们将程序改写成能读入10个柱面。

cyls equ 10 ;#define cyls 10

org 0x7c00

jmp init

DB	0x90
DB	"HARIBOTE"		
DW	512				
DB	1				
DW	1				
DB	2				
DW	224				
DW	2880			
DB	0xf0			
DW	9				
DW	18				
DW	2				
DD	0				
DD	2880			
DB	0,0,0x29		
DD	0xffffffff		
DB	"HARIBOTEOS "	
DB	"FAT12   "		
RESB	18				

init:
	mov ax,0
	mov ss,ax
	mov sp,0x7c00
	mov ds,ax
	
	mov bx,0
	mov ax,0x0820
	mov es,ax	;es:bx为缓冲地址,即读取的数据将存取在这里
	mov dl,0	;驱动器号0(即光驱号,现在一般只有一个光驱)
	mov dh,0	;磁头号0~1
	mov ch,0	;柱面号0~79
	mov cl,2	;扇区号1~18
	
	
resetsi:
	mov si,0	;读盘错误次数
	
read:
	mov al,1	;要读的扇区数
	mov bx,0
	mov dl,0
	mov ah,0x02	;读盘
	int 0x13	;中断,BIOS的0x13号函数
	jnc setnext	;没有出错则设置下一个要读的地方
	add si,1		;错误次数加1
	cmp si,5
	jae error	;jae指令(jump if above or equal)
	
	;重置驱动器
	mov ah,0
	mov dl,0
	int 0x13	;重置驱动器
	jmp read
	
setnext:
	mov ax,es
	add ax,0x0020
	mov es,ax	;es加512字节
	
	add cl,1
	cmp cl,18
	jbe resetsi	;jbe:jump if below or equal
	mov cl,1
	add dh,1
	cmp dh,2
	jb resetsi	;jb:jump if below
	mov dh,0
	add ch,1
	cmp ch,cyls
	jb resetsi
	
finish:
	hlt
	jmp finish
	
error:
	mov ax,0
	mov ds,ax
	mov si,msg
	mov di,msg
	mov ah,0x0e
	mov bx,15
	
show:
	mov al,[di]
	inc di
	cmp al,0
	je finish
	int 0x10
	jmp show
	
msg:
	db 0x0a,0x0a
	db "5 times,failed!!"
	db 0x0a
	db 0
	
resb 0x7dfe - $

db 0x55,0xaa
现在,由程序中我们读软盘的顺序是:柱面->正反面->扇区。


2、从启动区执行操作系统

启动区现在我们已经写好了,那么操作系统呢?我们可以写个最简单的.nas文件

finish:
		hlt
		jmp finish
将其保存成os.nas文件,然后将它编译后成os.sys文件,最后将其保存到映像里。一般向一个空软盘保存文件时,

(1)文件名会写在0x002600以后的地方;

(2)文件的内容会写在0x004200以后的地方。

知道了这个,我们就可以在启动区执行这个操作系统了。假设我们把磁盘上的内容装载到内存0x8000,那么磁盘0x4200处的内容就应该位于内存0x8000 + 0x4200 = 0xc200处,所以我们在os.nas里加上org 0xc200,在ipl.nas最后加上jmp 0xc200,那么,这个最简单的操作系统就会执行了。

3、32位模式前期准备

所谓32位模式,指的是CPU的模式。CPU有16位和32位两种模式,在32位模式下不能启用BIOS,这是因为BIOS是用16位机器语言写的,如果我们有什么事情想要用BIOS来做,那就将其全部放在开头先做。我们将会用到int 0x13,表示使用VGA图形模式的320 * 200 * 8位彩色模式。即会有200行,320列的像素,每位像素可以在256种颜色中选 一种。另外,如果要在画面上显示东西,那么就要往显卡内存里写数据。在VGA模式下,显卡内存为0xa0000 ~ 0xaffff的64kb,每个地址都对应着画面上的像素。

4、导入C语言

导入C语言其实就是将.c文件编译成.obj文件并链接后,将其于我们写好的汇编文件合并。具体的我们就不关注了。

5、C语言调用汇编函数

; naskfunc
; TAB=4

[FORMAT "WCOFF"]	;将输出格式设定为WCOFF模式
[INSTRSET "i486p"]	;告诉编译器此程序给486用
[BITS 32]		;制作32位模式用的机器语言

[FILE "naskfunc.nas"]	;源文件名信息,必须在定义函数名前写上
		GLOBAL	_io_hlt,_write_mem8	;需要链接的函数名,都要用global指令声明,函数名必须以下划线开关	

[SECTION .text]		;在写函数前必须写上这句后再写函数
_io_hlt:	; void io_hlt(void);
		HLT
		RET
		
_write_mem8:	; void write_mem8(int addr,int data)
		mov ecx,[esp + 4]
		mov al,[esp + 8]
		mov [ecx],al
		ret
其中,ret与C语言中的return类似。如果要传参数的话,那么,第一个参数存放的地址是esp + 4,第二个参数存放的地址是esp + 8,第三个是esp + 12,以此类推。

那么,在.c文件中如何调用呢?

void io_hlt(void);
void write_mem8(int addr,int data);

void HariMain(void)
{
    int i;
    for(i = 0xa0000;i <= 0xaffff;i++)
        write_mem8(i,i & 0x0f);
    while(1)
        io_hlt();
}
先声明函数,然后再调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值