Desktop

BMP 文件格式学习

由于改进IMAGELIS的需要,我对BMP文件的格式进行了一些研究,写在这里做为学习笔记,也给需要的朋友做为参考。如果没有特别说明,我所用的位图文件都是由WinowXP自带的画图产生。

BMP文件格式在网上已经很多教程之类的,不过我这里讲的是一些它们没提到的。

1.数据区起始位置

我在网上找到许多有关BMP文件格式的说明都说数据区起始位置在相对文件开头51个字节处,但我却发现数据区起始在相关文件开头55个字节处,不知是网上的错了还是我错了,不过我这样用了好像也没出错,有空再研究研究。

2.像素保存顺序

位图保存时扫描顺序是从下至上,从左至右,即在位图数据区,位图第一行保存在位图数据区的最后,而位图最后一行保存在位图数据区的开始,例有一个4×4的位图,那么它有4行,4列,像素保存在数据区的顺序是这样的:

line3 pixel 0 1 2 3

line2 pixel 0 1 2 3

line1 pixel 0 1 2 3

line0 pixel 0 1 2 3

因此,如果我们要自己产生一个位图文件,那么保存时就应该从最后一行,第一个像素开始保存,保存好一行后就往上一行直到第一行保存完毕,当然,我们必须在位图数据之前加上位图文件头,文件头的格式可以在网上找资料。

3.行字节对齐

可能是为了数据区的读取更快速,BMP格式规定一行的字节数必须是4的倍数,如果不足,就用0补足。例如有一幅5×5的24位色位图,那么本来每一行的字节数是24/8*5=15个字节,但15不是4的倍数,还需要1个字节来补足,于是在数据区这一行的数据就是:

pixel 0 1 2 3 4 0×00

同理如果宽度是7那么就需要补3个字节等等。如果是12×12单色位图,那么一行有12/2=6个字节,就需要在每一行的数据的最后补2个字节。

这个补0的问题很多朋友在处理位图的时候没有注意,以致于程序在处理诸如32×32之类的位图时能很好的运行,而处理如31×32这样的就不行了。

4.颜色数少于256时的数据位

在颜色数少于256时一个字节里就可能保存1个或者更多个像素,这时就要注意像素在字节里的保存顺序了。像素在字节内保存的顺序原则是前面的像素在高位,后面的像素在低位。例:

在16色位图里,每一个像素需要4个bit来保存,因此1个字节里就保存了两个像素,假如有一个2×1的16色位图,第一个像素是黑色,数据是0×0,第二个像素白色,数据是0xF,那么根据上面的原则,这两个像素在字节里是这样保存的:

pixel 0      1

bits  0000 1111

同理,如果是单色位图,也是前面的像素在高位,后面的像素在低位。

以上呢就是我在研究BMP格式时得到的一些经验,当然,如果有错或者有什么问题可以给我写信:)

[C] 冒泡排序

冒泡排序法:)记得在一本三级A类教程上看过怎么优化的,现在又给忘了,只写了这么个东东。

#include <stdio.h>

#define N 100

int main(int argc, char *argv[])

{

int a[N];

int i,j,temp;

for(i=0;i<N;i++) a[i]=rand();

printf(“start: %d\n”,st=(unsigned)time(NULL));

for(i=0;i<N;i++)

{

for(j=i;j<N-1;j++)

{

if(a[j]>a[j+1])

{

temp=a[j];

a[j]=a[j+1];

a[j+1]=temp;

}

}

}

for(i=0;i<N;i++) printf(“%d ”,a[i]);

printf(“\n”);

return 0;

}

[MenuetOS] HotCats 0.1 代码分析

用来在MEOS里抓取屏幕,不过这个版本并不完善。

在这个版本里,程序所用的方法是扫描屏幕上的点然后再写到缓存最后写到文件,这样就会导致速度很慢。

BMP格式文档可以去我的网络硬盘下载http://osdev.ys168.com

;=======================================

;    HotCats

;    Ver : 0.1

;    BLOG: http://hotheart.go.3322.org

;=======================================

bits 32

org    0×0

db ‘MENUET01′ ; 8 byte id

dd 0×01                        ; header version

dd START                        ; start of code

dd I_END                        ; size of image

dd 0×300000                    ; memory for app

dd 0xffff                        ; esp

dd 0×0 , 0×0                    ; I_Param , I_Icon

START:                                ; start of execution

mov eax,14                    ; 获取屏幕分辨率

int 0×40

xor ebx,ebx

mov bx,ax

shr eax,16

inc eax ; X size

inc ebx ; Y size

mov [scr_x],eax

mov [scr_y],ebx

mov [img_w],eax

mov [img_h],ebx

imul ebx

imul eax,3                    ; image data size

mov [img_s],eax

add eax,0×36                ; BMP header size

mov [imgf_s],eax

mov [f_s],eax

mov esi,bminfo                ; fill the header

mov edi,filestart

mov ecx,0×36/4

cld

repz movsd

call draw_window                ; draw the window

still:

mov eax,10                    ; wait here for event

int 0×40

cmp eax,1                    ; redraw request ?

je red

cmp eax,2                    ; key in buffer ?

je key

cmp eax,3                    ; button in buffer ?

je button

jmp still

red:                                ; redraw

call draw_window

jmp still

key:                                ; key

mov eax,2                    ; just read it and ignore

int 0×40

jmp still

button:                            ; button

mov eax,17                    ; get id

int 0×40

cmp ah,1                    ; button id=1 ?

jne .cmdcap

mov eax,-1                    ; close this program

int 0×40

.cmdcap:

cmp ah,cmdcap                ; is the Capture button ?

jne .cmdsave

call capall                    ; capture the whole screen

jmp still

.cmdsave:

cmp ah,cmdsave                ; is the save button

jne .end

call savefile                ; save the file

jmp still

.end:

jmp still

;   *********************************************

;   *******  WINDOW DEFINITIONS AND DRAW ********

;   *********************************************

draw_window:

mov eax,12                    ; function 12:tell os about windowdraw

mov ebx,1                    ; 1, start of draw

int 0×40

; DRAW WINDOW

mov eax,0

mov ebx,[scr_x]

sub ebx,win_w+5

imul ebx,65536

add ebx,win_w

mov ecx,[scr_y]

sub ecx,win_h+25

imul ecx,65536

add ecx,win_h

mov edx,0x03ffffff

mov esi,0x40ffffff

mov edi,0x00ffffff

int 0×40

; WINDOW LABEL

mov eax,4                    ; function 4 : write text to window

mov ebx,8*65536+8            ; [x start] *65536 + [y start]

mov ecx,0x10ddeeff            ; font 1 & color ( 0xF0RRGGBB )

mov edx,labelt                ; pointer to text beginning

mov esi,labellen-labelt        ; text length

int 0×40

; Button Capture

mov eax,8

mov ebx,10*65536+40

mov ecx,(win_h-10-14)*65536+14

mov edx,cmdcap

mov esi,0xaabbcc

int 0×40

; Button Save

mov eax,8

mov ebx,(10+40+10)*65536+40

mov ecx,(win_h-10-14)*65536+14

mov edx,cmdsave

mov esi,0xaabbcc

int 0×40

; Button Text

mov eax,4                        ; function 4 : write text to window

mov ebx,18*65536+(win_h-10-14+4); [x start] *65536 + [y start]

mov ecx,0×00444444                ; font 1 & color ( 0xF0RRGGBB )

mov edx,btext                    ; pointer to text beginning

mov esi,btexte-btext            ; text length

int 0×40

mov eax,12                    ; function 12:tell os about windowdraw

mov ebx,2                    ; 2, end of draw

int 0×40

ret

mov eax,47                    ; display the screen size

mov ebx,4*65536

mov ecx,[scr_x]

mov edx,120*65536+30

mov esi,0×0

int 0×40

mov eax,47

mov ebx,4*65536

mov ecx,[scr_y]

mov edx,120*65536+40

mov esi,0×0

int 0×40

capall:

mov edi,imgarea

mov ecx,[scr_y]

cld

.nextline:

mov ebx,ecx

dec ebx

imul ebx,[scr_x]

push ecx

mov ecx,[scr_x]

.capline:

push edi

push ebx

mov eax,35                    ; get pixel

int 0×40

stosd

pop ebx

pop edi

inc ebx

add edi,3

loop .capline

pop ecx

loop .nextline

.end:

ret

savefile:                            ; save the file

mov eax,58

mov ebx,fileinfo

int 0×40

ret

; DATA AREA

win_w        equ 250

win_h        equ 50

cmdcap        equ 2

cmdsave        equ 3

filestart    equ 0×20000

imgarea        equ filestart+0×36

scr_x    dd 0

scr_y    dd 0

zoom    dd 2

fileinfo:

dd 1                    ; 1=WRITE

dd 0×0                    ; not used

f_s  dd 800*600*3+0×36        ; bytes to write

dd filestart            ; source data pointer

dd 0×10000                ; work area for os - 16384 bytes

db ‘/HD/1/TEST.BMP’,0    ; ASCIIZ dir & filename

btext:  db ‘ Cat     Save  ’

btexte:

labelt:

db ‘HotCats 0.1 - HotHeart HotWorks 2005′

labellen:

bminfo:

db ‘BM’ ; 0000-0001 位图标志

imgf_s    dd 800*600*3+0×36    ; 0002-0005 文件大小

dd 0×0                ; 0006-0009 保留

db 0×36,0×0,0×0,0×0; 000A-000D 文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)

dd 0×28            ; 000E-0011 图像描述信息块的大小,常为28H。

img_w    dd 800                ; 0012-0015    图像宽度。

img_h    dd 600                ; 0016-0019    图像高度。

dw 0×1                ; 001A-001B    图像的plane总数(恒为1)。

dw 24                ; 001C-001D    数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。

dd 0                ; 001E-0021    记录像素的位数,很重要的数值,图像的颜色数由该值决定。

img_s    dd 800*600*3        ; 0022-0025    图像区数据的大小。

dd 0×0                ; 0026-0029    水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

dd 0×0                ; 002A-002D    垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

dd 0×0                ; 002E-0031    此图像所用的颜色数,如值为0,表示所有颜色一样重要。

dd 0×0                ; 0032-0035 未用

I_END:

[MenuetOS] HotRun 0.4 代码分析

; Made by HotHeart

http://www.xujiwei.com

; vipxjw@tom.com

; MENUET RUN 0.4

; 1) 缺省目录 /RD/1/

; 2) 可以更改程序目录,在SETUP里设置硬盘后可以运行

;    硬盘上的程序

; 3) 只能访问根目录

; 4) 自动根据屏幕大小调整窗口位置(限任务栏为文字模式)

use32

org    0×0

db ‘MENUET01′ ; 8 byte id

dd 0×01                    ; header version

dd START                   ; start of code

dd I_END                   ; size of image

dd 0×100000                ; memory for app

dd 0x7fff0                 ; esp

dd 0×0 , 0×0               ; I_Param , I_Icon

START:                          ; start of execution

; 计算窗口位置

mov eax,14

int 0×40

mov [screenysize],ax

xor eax,eax

mov ax,[screenysize]

sub eax,116+20

imul eax,65536

add eax,116

mov [winy],eax

; 初始化变量

mov [filestr],dword runprogram

mov [dir],dword rundir

mov [ya],dword 53

mov [yb],dword 69

call draw_window

still:

mov eax,23                 ; wait here for event

int 0×40

cmp eax,1                  ; redraw request ?

je red

cmp eax,2                  ; key in buffer ?

je key

cmp eax,3                  ; button in buffer ?

je button

jmp still

red:                          ; redraw

call draw_window

jmp still

key:                          ; key

mov eax,2                  ; just read it and ignore

int 0×40

jmp still

button:                       ; button

mov eax,17                 ; get id

int 0×40

cmp ah,4                  ; id=4 结束

je close

cmp ah,1

je close

cmp ah,2                  ; id=2 输入文件名

je inputfile

cmp ah,5                  ; id=5 输入路径

je inputdir

cmp ah,3                  ; id=3 运行程序

je runp

jmp still

file_start: dd 16

dd 0,0,0,0×10000

rundir:     db ‘/RD/1/’ ; 缺省目录

runprogram: db ‘RUN’,0        ; 缺省程序

times 60 db 0

filestr  dd 0×0

dir      dd 0×0

addr dd 0×0

ya       dd 0×0

yb       dd 0×0

;==========================================================

close:

;结束程序

mov eax,-1

int 0×40

inputfile:

;输入文件名

call read_string_file

jmp still

inputdir:

;输入路径

call read_string_dir

jmp still

;==========================================================

runp:

;运行程序

mov eax,58        ;运行

mov ebx,file_start

int 0×40

jmp still

;==========================================================

read_string_file:

;输入文件名

mov edi,[filestr]

mov eax,0

mov ecx,40

cld

rep stosb

call print_file

mov edi,[filestr]

file_fun:

mov eax,23        ; 等待事件

mov ebx,100    ; 延时100毫秒

int 0×40

cmp eax,0        ; eax=0 无事件

je file_fun    ; 继续等待事件过程

cmp eax,2        ; eax=2 按钮事件

jne file_read_done    ; 跳到读取键盘事件结束

mov eax,2        ; 获取击键 ascii 码

int 0×40

shr eax,8

cmp eax,13        ; 是否为回车键

je file_read_done    ; 是则跳到读取结束

cmp eax,8        ; 是否为退格键

jnz file_nobsl    ; 不是则跳到 nobsl

cmp edi,[filestr]

jz file_fun

sub edi,1        ; 字符数量减 1

mov [edi],byte 32    ; 退格

call print_file    ; 显示字符串

jmp file_fun    ; 继续读取

file_nobsl:

cmp al,95        ; 判断是否为小写

jbe file_cok    ; 是则跳到 cok

sub al,32        ; 不是则将 ascii 减去 32

file_cok:

mov [edi],al ; 添加到字符串

call print_file    ; 显示字符串

add edi,1        ; 已读取字符数量

mov esi,[filestr]    ; 读入字符串地址

add esi,30        ; 加上 30

cmp esi,edi ; 比较

jnz file_fun    ; 未到最大长度

file_read_done:    ; 读取结束

mov [edi],byte 0    ; 结束标志

call print_file    ; 显示字符串

jmp still        ; 事件循环

;==========================================================

read_string_dir:

mov edi,[dir]

mov eax,0

mov ecx,6

cld

rep stosb

call print_dir

mov edi,[dir]

dir_fun:

mov eax,23        ; 等待事件

mov ebx,100    ; 延时100毫秒

int 0×40

cmp eax,0        ; eax=0 无事件

je dir_fun    ; 继续等待事件过程

cmp eax,2        ; eax=2 按钮事件

jne dir_read_done    ; 跳到读取键盘事件结束

mov eax,2        ; 获取击键 ascii 码

int 0×40

shr eax,8

cmp eax,13        ; 是否为回车键

je dir_read_done    ; 是则跳到读取结束

cmp eax,8        ; 是否为退格键

jnz dir_nobsl    ; 不是则跳到 nobsl

cmp edi,[filestr]

jz dir_fun

sub edi,1

mov [edi],byte 32

call print_dir    ; 显示字符串

jmp dir_fun    ; 继续读取

dir_nobsl:

cmp al,95        ; 判断是否为小写

jbe dir_cok    ; 是则跳到 cok

sub al,32        ; 不是则将 ascii 减去 32

dir_cok:

mov [edi],al ; 添加到字符串

call print_dir    ; 显示字符串

add edi,1        ; 已读取字符数量

mov esi,[dir]    ; 读入字符串地址

add esi,6        ; 加上 6

cmp esi,edi ; 比较

jnz dir_fun    ; 未到最大长度

dir_read_done:    ; 读取结束

call print_dir    ; 显示字符串

jmp still        ; 事件循环

;==========================================================

print_dir:

;显示路径

pusha

mov eax,13        ; 画底纹

mov ebx,55*65536+31*6

mov ecx,[ya]

shl ecx,16

mov cx,12

sub ecx,2*65536

mov edx,0xeeeeee

int 0×40

mov eax,4        ; 显示路径

mov edx,[dir]

mov ebx,56*65536

add ebx,[ya]

mov ecx,0×444444

mov esi,6

int 0×40

popa

ret

;==========================================================

print_file:

;显示文件名

pusha

mov eax,13        ; 画底纹

mov ebx,55*65536+31*6

mov ecx,[yb]

shl ecx,16

mov cx,12

sub ecx,2*65536

mov edx,0xeeeeee

int 0×40

mov eax,4        ; 显示文件名

mov edx,[filestr]

mov ebx,56*65536

add ebx,[yb]

mov ecx,0×444444

mov esi,30

int 0×40

popa

ret

;==========================================================

;   *********************************************

;   *******  WINDOW DEFINITIONS AND DRAW ********

;   *********************************************

draw_window:

mov eax,12                    ; function 12:tell os about windowdraw

mov ebx,1                     ; 1, start of draw

int 0×40

; DRAW WINDOW

mov eax,0                ; function 0 : define and draw window

mov ebx,1*65536+250    ; [x start] *65536 + [x size]

mov ecx,[winy]            ; [y start] *65536 + [y size]

mov edx,0x03ffffff        ; color of work area RRGGBB,8->color gl

mov esi,0x805080d0        ; color of grab bar  RRGGBB,8->color gl

mov edi,0x005080d0        ; color of frames    RRGGBB

int 0×40

; WINDOW LABEL

mov eax,4                ; function 4 : write text to window

mov ebx,8*65536+8        ; [x start] *65536 + [y start]

mov ecx,0x10ddeeff        ; font 1 & color ( 0xF0RRGGBB )

mov edx,labelt            ; pointer to text beginning

mov esi,labellen-labelt; text length

int 0×40

mov eax,8                ; 路径输入 id=5

mov ebx,13*65536+40

mov ecx,50*65536+12

mov edx,5

mov esi,0xaabbcc

int 0×40

mov eax,8                ; 文件名输入 id=2

mov ebx,13*65536+40

mov ecx,66*65536+12

mov edx,2

mov esi,0xaabbcc

int 0×40

mov eax,8                ; 确定 id=3

mov ebx,128*65536+48

mov ecx,84*65536+18

mov edx,3

mov esi,0xaabbcc

int 0×40

mov eax,8                ; 取消 id=4

mov ebx,182*65536+48

mov ecx,84*65536+18

mov edx,4

mov esi,0xaabbcc

int 0×40

mov ebx,13*65536+34    ; 提示信息

mov ecx,0×666666

mov edx,txttxt

mov esi,30

mov eax,4

int 0×40

mov eax,4                ; Path按钮文本

mov ebx,21*65536+53

mov ecx,0×666666

mov edx,txtdir

mov esi,4

int 0×40

mov ebx,21*65536+69    ; File按钮文本

mov ecx,0×444444

mov edx,txtfile

mov esi,4

mov eax,4

int 0×40

mov ebx,140*65536+89    ; 确定取消按钮文本

mov ecx,0×444444

mov edx,txtbutton

mov esi,13

mov eax,4

int 0×40

call print_dir

call print_file

mov eax,12                ; function 12:tell os about windowdraw

mov ebx,2                ; 2, end of draw

int 0×40

ret

; DATA AREA

tcolor      dd 0×000000

txttxt      db ‘请输入要运行程序的路径和文件名’,‘x’

txtdir      db ‘Path’,‘x’

txtfile     db ‘File’,‘x’

txtbutton   db ‘确定     取消’,‘x’

screenysize dw 0×0

winy        dd 0×0

labelt:

db ‘运行’

labellen:

I_END:

[本日志由 xujiwei 于 2005-07-22 06:24 PM 编辑]

Comments (0), Views (2955), Pings (0), Leave a response!

系统开发资源

中断大全

http://www.ctyme.com/intr/int.htm

操 作系统资源(英文)

http://www.nondot.org/sabre/os/articles

内核版之OS设 计

http://www.linuxforum.net/forum/printthread.php?Ca … mp;main=334068&type=thread

Linux的一些Kernel资源

http://diy-os.gro.clinux.org/file.html

纯 C论坛文档资源中心

http://purec.binghua.com/Article/Index.asp

实模式进保护模式

在写自己的操作系统时,不能总在实模式下,进入保护模式才是”正道”,不过怎么进入保护模式又是一个问题,我把

XuOS里进入保护模式的代码分析一下.

; 装入GDT

mov  eax,ds  ;设置GDT在物理内存中的正确位置

shl  eax,4

add  [gdt_addr+2],eax

cli  ; 关中断

lgdt [gdt_addr] ;载入GDT

; 下面打开A20地址线,这段代码可以在OSzone上找到相关解释.

call  Empty_8042

mov  al,0xd1

out  0×64,al

call  Empty_8042

mov  al,0xdf

out  0×60,al

call  Empty_8042

; 进入保护模式

mov  eax,cr0 ;置PE位

or   eax,1

mov  cr0,eax

jmp oscodesel:code_32  ; 跳到32位代码处执行

Empty_8042:

in   al,0×64

test al,0×2

jnz  Empty_8042

ret

; GDT的内容

gdt:

gdt_null:

dd  0×0000

dd  0×0000

gdt_system_code:

oscodesel equ $-gdt  ; 段选择子

dd  0x0000ffff,0x00cf9a00

gdt_system_data:

osdatasel equ $-gdt

dd  0x1000ffff,0x00cf9200

videosel equ $-gdt

dd  0x0000ffff,0x00cf920a

gdt_addr:

dw  gdt_addr-gdt-1

dd  gdt        ; ;GDT表的位置

当然,由于XuOS目前还比较简单,还没有设置IDT,GDT中段也比较少,况且我的理解也有可能有些偏差或者表述

有些不清楚,这里只提供一个思路,更多更强的功能还是要靠自己才行.

写你自己的操作系统

这是转载的,原文可以在http://www.xemean.net的文档中心里找到.

因为原文中的代码编译后运行有错误,这里我把改过后能正确运行的代码讲一下

org 0x07c00     ; 起始地址是0000:7c00

jmp begin_boot ; 跳过其它的数据,跳转到引导程序的开始处

OEM_ID                db ”OSeg    ”   ;软盘信息,具体请参考”FAT格式”

BytesPerSector        dw 0×0200

SectorsPerCluster     db 0×01

ReservedSectors       dw 0×0001

TotalFATs             db 0×02

MaxRootEntries        dw 0x00E0

TotalSectorsSmall     dw 0x0B40

MediaDescriptor       db 0xF0

SectorsPerFAT         dw 0×0009

SectorsPerTrack       dw 0×0012

NumHeads              dw 0×0002

HiddenSectors         dd 0×00000000

TotalSectorsLarge     dd 0×00000000

DriveNumber           db 0×00

Flags                 db 0×00

Signature             db 0×29

VolumeID              dd 0xFFFFFFFF

VolumeLabel           db ”OSexample  ”

SystemID              db ”FAT12   ”

print_mesg:  ;打印信息调用

mov ah,0×13 ; 使用中断10h的功能13,在屏幕上写一个字符串

mov al,0×00 ; 决定调用函数后光标所处的位置

mov bx,0×0007 ; 设置显示属性

mov cx,0×20 ; 在此字符串长度为32

mov dx,0×0000 ; 光标的起始行和列

int 0×10 ; 调用BIOS的中断10h

ret ; 返回调用程序

get_key:  ;等待按键

mov ah,0×00

int 0×16 ; Get_key使用中断16h的功能0,读取下一个字符

ret

clrscr:   ;清屏

mov ax,0×0600 ; 使用中断10h的功能6,实现卷屏,如果al=0则清屏

mov cx,0×0000 ; 清屏

mov dx,0x174f ; 卷屏至23,79

mov bh,0 ; 使用颜色0来填充

int 0×10 ; 调用10h中断

ret  ;返回

begin_boot:  ;;引导程序开始

mov  ax,cs    ;设置段寄存器

mov  ds,ax

mov  es,ax

call clrscr ; 先清屏

mov bp,bootmesg ; 提供串地址

call print_mesg ; 输出信息

call get_key ; 等待用户按下任一键

bits 16   ;以16位方式编译

call clrscr ; 清屏

mov ax,0xb800 ; 使gs指向显示内存

mov gs,ax ; 在实模式下显示一个棕色的A

mov word [gs:0],0×641 ; 显示

call get_key ; 调用Get_key等待用户按下任一键

mov bp,pm_mesg ; 设置串指针

call print_mesg ; 调用print_mesg子程序

call get_key ; 等待按键

call clrscr ; 清屏

cli ; 关中断

mov  eax,ds   ;设置GDT物理地址

shl  eax,4

add  [gdtr+2],eax

lgdt [gdtr] ; 加载GDT

mov eax,cr0 ;进入保护模式

or  al,0×01 ; 设置保护模式位

mov cr0,eax ; 将更改后的字送至控制寄存器中

jmp codesel:go_pm  ;跳至32位代码

bits 32  ;以32位方式编译

go_pm:

mov ax,datasel  ;设置段寄存器

mov ds,ax ; 初始化ds和es,使其指向数据段

mov es,ax

mov ax,videosel  ;使gs指向显存

mov gs,ax

mov word [gs:0],0×0400+’B' ; 在保护模式下显示一个红色的字符B

jmp $ ; 无限循环

bits 16

gdtr:

dw gdt_end-gdt-1 ; gdt的长度

dd gdt ; gdt的物理地址

gdt:   ;具体GDT信息请参考GDT格式

nullsel equ $-gdt ; $指向当前位置,所以nullsel = 0h

dd 0

dd 0 ; 所有的段描述符都是64位的

codesel equ $-gdt ; 这是8h也就是gdt的第二个描述符

code_gdt:

dw 0x0ffff ; 段描述符的界限是4Gb

dw 0×0000

db 0×00

db 0x09a

db 0x0cf

db 0×00

datasel equ $-gdt

data_gdt:

dw 0x0ffff

dw 0×0000

db 0×00

db 0×092

db 0x0cf

db 0×00

videosel equ $-gdt

dw 3999

dw 0×8000 ; 基址是0xb8000

db 0x0b

db 0×92

db 0xcf

db 0×00

gdt_end:

bootmesg db ”Our OS boot sector loading…   ”   ;信息数据

pm_mesg  db ”Switching to protected mode…  ”

times 510-($-$$) db 0  ;填满512个字节

dw 0x0AA55  ;引导扇区标志