Desktop
BMP 文件格式学习
Aug 21st
由于改进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] 冒泡排序
Aug 18th
冒泡排序法:)记得在一本三级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;
}
系统开发资源
Oct 29th
中断大全
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论坛文档资源中心
实模式进保护模式
Oct 11th
在写自己的操作系统时,不能总在实模式下,进入保护模式才是”正道”,不过怎么进入保护模式又是一个问题,我把
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中段也比较少,况且我的理解也有可能有些偏差或者表述
有些不清楚,这里只提供一个思路,更多更强的功能还是要靠自己才行.
写你自己的操作系统
Oct 3rd
这是转载的,原文可以在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 ;引导扇区标志
