c语言e怎么表示_自制操作系统系列——(四)尝试C语言

11123915f717964babd5ca610ebb9d94.png
操作系统是管理计算机硬件与软件资源的计算机程序,同时也是计算机系统的内核与基石。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入设备与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。本系列希望能够长期更新,对操作系统的原理逐一的进行实现。

一、内容介绍

本小结的内容相对较少,主要是直观的感受一下C语言是怎么引入的(目前的代码还存在许多问题,后面会慢慢完善)。另外简单介绍一下如何用gdb调试我们的二进制代码。

二、引入C语言

先看一下代码:

主要文件有5个,mbr.s(和前面的一样,没有修改,不再粘贴了),boot.s(修改了读写扇区的大小,将C语言部分的代码加载到内存中,另外添加了跳转,跳转到C语言代码段执行),main.c(这个是今天写的两个C语言函数,主要功能就是通过指针来修改显存对应内存的值,从而能够打印字符到显示器上),Makefile 和 linker.ld 完成文件的编译连接和执行。

boot.s

/*
 * Title: bootloader
 */   
	.set KERNEL_SIZE, 2			# number of 512 byte blocks
	.set KERNEL_ADDRESS, 0x8200		# load kernel to this address
	.section .text
	.code16
	
_restart:
	movw	%cs,	%ax
	xorw	%ax,	%ax			# setup stack and segments
	movw	%ax,	%ds
	movw	%ax,	%es
	movw	%ax,	%ss


	movb    $0x00,  %bh
	movb	$0x00,	%dh
	movb	$0x00,	%dl
	call	set_cursor2
	call	clear_screen2 

	movw    $0x07d0,  %cx  #size of char 80*25  0x50*0x19 
	movb    $0x00,  %bh      
	movb    $0x20,  %al  
	movb    $0x3f,  %bl
        movb    $0x09,  %ah

        int     $0x10

	
	movw	$msg2,	%ax
	movw	%ax,	%bp
	movw	len2,	%cx
	movb	$0x0d,	%dh
	movb	$0x1c,	%dl
	movb	$0x01,	%al
	movb	$0x13,	%ah
	movb	$0x3f,	%bl
	movb	$0x00,	%bh
	int	$0x10


	movw	$msg1,	%ax	#
	movw	%ax,	%bp
	movw	len1,	%cx	#

	movb	$0x0c,	%dh
	movb	$0x21,	%dl
	movb	$0x01,	%al
	movb	$0x13,	%ah
	movb	$0x3f,	%bl
	movb	$0x00,	%bh
	int	$0x10

load:
	movw	$0x00,	%dx	#dh->head,dl->driver
	movw	$0x0820,	%ax
	movw	%ax,	%es
	xorw	%bx,	%bx	#es:bx->the data   0x0820:0000
	movb	$0,	%ch	#ch->cidaohao
	movb	$4,	%cl	#cl->sector 
	movb	$2,	%ah	#param
	movb	$8,	%al	#how many blocks to read
	int	$0x13
	jnc 	gotonext
	jmp	load
gotonext:
	movb	$0x86,	%ah
	movw	$0x001e,	%cx
	movw	$0x8480,	%dx
	int	$0x15
	call	a20_enable


_end1:
	jmp	preswitch

set_cursor2:
	movb    $0x02,  %ah
        int     $0x10
	ret

clear_screen2:               
    movb    $0x06,  %ah     #  
    movb    $0,     %al     # 
    movb    $0,     %ch     # 
    movb    $0,     %cl     #    
    movb    $24,    %dh     # 
    movb    $79,    %dl     # 
    movb    $0x07,  %bh     #  
    int     $0x10  
    ret 

a20_enable:
	call	a20_wait
	movb	$0xd1,	%al			# write command
	outb	%al,	$0x64
	call	a20_wait
	movb	$0xdf,	%al			# enable a20  movb $0xdd, %al # disable a20		
	outb	%al,	$0x60
	call	a20_wait
	ret

a20_wait:
	inb	$0x64,	%al
	testb	$2,	%al
	jnz	a20_wait
	ret


preswitch:
	cli
	lgdt	gdt_pointer
	movl	%cr0,	%eax
	orl	$1,	%eax
	movl	%eax,	%cr0
	movl	$init_prot, %eax
	ljmp	$0b1000,	$init_prot
	

.code32
.extern kernal
init_prot:
	movw	$0b10000,	%ax
	movw	%ax,	%ds
	movw	%ax,	%es
	movw	%ax,	%fs
	movw	%ax,	%gs
	movw	%ax,	%ss

	#jmp	kernal
	ljmp	$0b1000, $0x8200	# jump to C file
	

	#movl	$hw,	%edi  # print string in code32
	#call	puts
myend:
	jmp	myend



puts:
	call	clear
	xorl	%ecx,	%ecx
putschar:
	movb	(%edi,%ecx),	%dl
	test	%dl,	%dl
	jz	putsend
	movb	$0x9f,	%dh
	movw	%dx,	(%ebx,%ecx,2)
	incl	%ecx
	jmp	putschar
putsend:
	ret

clear:
	xorl	%ecx,	%ecx
	movl	$0xb8000,	%ebx
clearchar:
	movb	$0x20,	%dl
	movb	$0x9f,	%dh	
	movl	$0x0f200f20,	(%ebx,%ecx,4)
	incl	%ecx
	cmpl	$1000,	%ecx
	jl	clearchar
	ret

 

gdt:
	.long 0,0

# code segment
	.word 0xffff		# limit (lower 16 bits)
	.word 0x0000		# base (lower 16 bits)
	.byte 0x00		# base (next 8 bits)
	.byte 0b10011010	# access byte

	.byte 0b11001111	# granularity byte
	.byte 0x00		# base (last 8 bits)

# data segment
	.word 0xffff		# limit (lower 16 bits)
	.word 0x0000		# base (lower 16 bits)
	.byte 0x00		# base (next 8 bits)
	.byte 0b10010010	# access byte

	.byte 0b11001111	# granularity byte
	.byte 0x00		# base (last 8 bits)

gdt_end:

gdt_pointer:
	.word gdt_end - gdt - 1	# size of GDT-1
	.long gdt		# GDT offset

hw:
	.string "Hello, World!"

msg1:
	.asciz "I'm bootloader!"
len1: 
	.int . - msg1

msg2:
	.asciz "My job is loading our OS!"
len2: 
	.int . - msg2


	.fill 0x1fe - (. - _restart) ,1,0 
	.word 0xaa55

main.c (目前没有任何头文件,因为我们是从0开始写的,除了GCC编译器支持的语法外,我们没有任何库函数可以使用)

void cleanscreen();
void writechar();

int kernalmain()
{
  cleanscreen();
  writechar();
  while(1);
  return 0;
}

void cleanscreen()
{
	unsigned int base = 0xb8000;
	for(int i=0; i<1024; ++i)
	{
		unsigned int *num = (unsigned int *)(base+i*4+1);
		*num = 0x0f200f20;
	}
}

void writechar()
{
	char str[30]={'W', 'e', ' ', 'a', 'r', 'e', ' ', 'n', 'o', 'w',' ', 'i', 'n', ' ', 
			't', 'h', 'e', ' ', 'k', 'e', 'r', 'n', 'a', 'l', ' ', 'C', '!', '!', '!', '0'};
	unsigned char * putschar = (void *)(0xb8000);
	int i=0;
        while(str[i]!='0')
	{
		putschar[i*2+1]=str[i];
		i++;
	}
}

linker.ld

ENTRY(main)

SECTIONS
{
    . = 0x007c00;
    
    .text :
    {
        mbr.o(.text)
	. += 0x000200;
	boot.o(.text)
	main.o(.text)
    }
    .data :
    {
	*.o(.data)
    }
}

makefile

GCCPARAMS = -nostartfiles -e kernalmain
objects = mbr.o boot.o main.o
inputs = main.c


main.o:
	gcc $(GCCPARAMS) -o  $@ $(inputs)

%.o: %.s
	as -o $@ $<
	
mykernel.bin: linker.ld $(objects) 
	ld $(objects) --oformat=binary -o $@ -T $<

run: mykernel.bin
	qemu-system-i386 -fda $< -boot a -monitor stdio #-S -s # 

clean: 
	rm -f ./*.o ./*.bin 

make run运行,我们会看到C语言打印出来的字符。但是目前的程序还存在很大的问题,我们并没有关心数据段在内存是怎么运作的,所以我们目前的代码不能处理全局变量或者静态变量。后面会一点点加进来。

这节其实还是在理解保护模式下,代码间是怎么跳转的。ljmp指令会修改段寄存器和IP指针的值,修改的过程上一节提到的视频讲解中讲的很清楚。

三、调试

上一节我们提到使用monitor stdio来显示寄存器信息(info registers),此外我们也可以查看内存数据(比如 x /10x 0x8b000, 表示以16进制形式显示从0x8b000开始的10的32位内存数据)以及一些其他信息。 但是当我的程序存在不确定问题,能不能单步调试我们的代码呢?目前我还没找到完美的单步调试的方法,但是gdb调试qemu程序,可以实现断点调试,只不过我们的断点是内存位置,(而且存在一个缺陷,一旦我们的断点位置不合适,将连贯指令分离,后面的程序将无法运行),不是太好用,但还是记录一下。

首先qemu支持gdb远程调试,只需要添加-s命令,默认通过1234端口与gdb链接。qemu启动后,在运行代码前会通过1234端口给gdb发送信号,等待gdb的调试指令。因为启动qemu后,我们可以在新的命令行启动gdb,然后输入target remote localhost:1234,和qemu完成连接,然后我们就可以使用gdb的命令来调试内核程序了。在使用时,next下一行等一些指令不太能用,因为我们的二进制中没有下一行的边界限定,gdb无法确定到底要执行哪些命令,只能通过在内存中打断点的方式,确实不太方便。如果以后碰到更好的方法再跟新 。

下节会考虑数据段,代码段在内存中如何安排,此外通过I/O来控制光标位置和打印字符,实现一个简单的显示器驱动功能。

以后代码会慢慢变多,一点点粘出来不太现实,会考虑放到Git上。文章只简单记录一下这节需要重要注意的地方。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值