[转贴]内核版之OS设计

hyl
(enthusiast)
10/06/02 10:28
[精华] 内核版之OS设计  

本贴是索引页, 其回贴是早期系统的一些介绍.


了解最新进展 新闻和进展 Join us

参与问题的解决 Bug List

最新发布请看----项目主页 -------2003.4.8

导航

内核版OS设计讨论&下载

内核版OS设计--Kernel结构简析

内核设计--进程相关问题

内核版OS设计---LFB简介

内核设计--文件系统


论坛资源
写个小的OS项目,如何?----------杂谈而已
为什么懂了内核以后都要去搞自己的操作系统呢?

编辑者: hyl (10/21/04 10:08)

文章选项:

hyl
(enthusiast)
09/17/02 12:19
[精华] bootloader [re: hyl]  



需要从一个简单的bootsect 开始写我的操作系统吗?

下面的网站给出了一些建议:
千万不要写bootloader. 至少不要把他当作写os的第一件事.
bootloader 不是 OS.
bootloader 仅仅是os的一个非常小的部分.
写一个好些的bootloader 的工作量和写一个小 OS的工作量相当.
写一个 bootloader 需要一些神秘的知识,像什么a20等等.
如果在 DOS 或者 Windows 9x下开发,可以使用 DOS 作为你的boottloader. , 你看 GRUB 怎么样.


但是,为什么不写一个自己的?

既然如此神秘, 既然有巨多的开源bootloader......, 拷贝一些代码修剪一下也挺好


你选择写还是不写? 不惜半年的深夜...写!? bootloader的一些研究


bootloader中0.0.5版有了如下特性:

1.Boot 装载 setup+os 到 0X10000

2.在实模式下跳到 setup, setup 把 os 移动到 0x100000 (1M)

3.然后切换到保护模式,用一句长跳转 jmp dword 8:0x100000
(nasm can mix 16bit and 32bit code)
4.使用了 c++

1).setup.s 预留了2048(0x800)个字节供以后使用

2).setup.s 预留空间, setup 搬移 os 的位置(紧邻setup之后),应该一致.
总结:
系统从 1M 的地方跑起来,总算像个操作系统的 Loader 了!!!!!

第一个版本0.0.1可以说是第一步了, 可能费时也最久.







http://osdev.neopages.net/

编辑者: hyl (10/01/02 09:05)

文章选项:

hyl
(enthusiast)
09/18/02 10:19
资源网站 [re: hyl]  

操作系统资源中心
这个网站之好, 无法用言语表达............

OSD之家
好好好!

OS dev
也不错

execpc
快来看看吧......

openBLT
reactos
brainix team
v2os
Nachos
Thix
hello
aros
kerne link
os file
文件格式大全



编辑者: hyl (09/18/02 18:26)

文章选项:

hyl
(enthusiast)
09/18/02 12:30
建议 [re: hyl]  



1.djgpp下的make 可能不认识长文件名. 总说no rule to make ....
也可能是版本低. 总之使用短一点的文件名称好.

2.把bootsect osimg 使用copy /b 连接成一个文件. 可以直接作为vmWare的虚拟软驱使用. 真是方便.^_^
注意:最好加上pad 使之大于1.44M.

3. NASM version 0.98 的bug
如果你向elf添加了自定义的section ,nasm就会把文件弄坏! (只可有 .text, .data, or .bss, 切记)

4. CygWin 和 MinGW32 把 BSS size 用错误的方式存入了 BSS section header.
这是bug, 就是因此, 这些工具不能和NASM 或者微软的编译器交互!.

5. CygWin and MinGW32 linkers crash when asked to make a binary kernel.
嗐! 我是不用这两个东西! 因此下面的东西我也不翻译了.
You can get around this to some extent by linking to PE format with identical memory alignment and file alignment:

ld --oformat pei-i386 --file-alignment 0x1000 --section-alignment 0x1000 ...
Now you have a 'binary' kernel with a 4096-byte PE header at the beginning, which the bootloader can skip over (thanks to Tim Robinson for this idea). Note that this may still fail if you have user-defined sections that come after the BSS.

6. 注意连接脚本的bug!:
这样写有错误:
.bss: {
*(.bss)
*(.common)
end = . ; _end = . ;
}
一些有bug的 ld 会把 'end' 放在common 和 bss中间. 所以应该这样写:
...
*(.common)
}
end = .; _end = . ;
(thanks to Jarek Pelczar for finding this bug)

7. DJGPP 的elf 工具好像有bug?!
如果你的内核仅仅有 .text, .data, and .bss sections, 还可以使用 objcopy 把 DJGPP COFF 转换到 ELF.

8. Bootloader 'foo' 不能装载我的 'bar' 核
根据 Multiboot 标准(GRUB就如此) , 一个保护模式的 kernel 不能依赖 GDT 的布局, 段描述符, 或bootloader建立的选择子. 你的核要做好防御性的措施啊! 不要对bootloader有所假设! 做到这一点大概需要使用汇编核相对地址.

9.只使用可用 RAM
段地址:偏移地址.......线性地址............... 使用情况
0000:0000-0000:03FF 000000-0003FF 中断向量表interrupt vector table
0040:0000-0040:00FF 000400-0004FF BIOS 数据区
0050:0000-0050:76FF 000500-007BFF 空闲常规内存(CONVENTIONAL MEMORY )
0000:7C00-0000:7DFF 007C00-007DFF 引导扇区
0000:7E00-9000:FBFF 007E00-09FBFF 空闲常规内存
9000:FC00-9000:FFFF 09FC00-09FFFF 扩展BIOS 数据区(EBDA)
A000:0000-F000:FFFF 0A0000-0FFFFF video memory 和BIOS ROMs
FFFF:000F-FFFF:FFFF 100000-10FFEF 高端内存区 (HMA)
10FFF0- 空闲扩展内存

引导扇区程序可能超出 007C00-007DFF, 决定于如何写. 可以在内核或第二阶段的装载完成后覆盖这段内存.

EBDA 这里说是1K. 有些计算机没有 EBDA, 有些大于 1K. 用BIOS 中断 INT 12h or INT 15h AX=E820h 可以找到常规内存的顶端, 不要使用超过这个限制的常规内存.

DOS 7+ 自动加载HIMEM.SYS , 然后把自己放到HMA. 最好不把你的内核装到HMA .最好使用 XMS 分配一段扩展内存. 这样可以防止把其他 XMS 的 'clients' , 像 SMARTDRV, 写掉.

Watch out for the case where HIMEM.SYS is loaded and SMARTDRV is also loaded in such a way that the free XMS memory block straddles a 4 meg line. If your kernel is loaded into this memory, and it uses paging, the kernel will need two pages tables to map the kernel memory. The kernel could be copied to 1 meg after it's been loaded. This will probably trash DOS, so do it just before entering pmode.

10.内核 code 和 data 没有连接到正确的地址
请使用连接脚本 (see 'More linker gotchas', below).

可以先连接到非binary 如COFF, ELF, PE, 然后 dump the symbols and disassemble the kernel, 最后转换到 binary:
ld -g -Tcoffkrnl.ld -o krnl.cof $(OBJS) lib/libc.a
objdump --line-numbers --source krnl.cof >krnl.lst
nm --line-numbers krnl.cof | sort >krnl.sym
objcopy -O binary krnl.cof krnl.bin
这样可以检查 krnl.lst and krnl.sym ,看看是不是正确的定位了内核的code 和data.

对于 x86, 内核的数据段对 连接和定位的错误很敏感,因为许多代码使用 EIP-相对寻址. 你的内核启动代码可以检测看看内核的数据段是不是被正确的连接,定位和加载:

DS_MAGIC equ 3544DA2Ah

[SECTION .text]
[BITS 32]
GLOBAL entry
entry:
call where_am_i ; where did the loader put us?
where_am_i:
pop esi ; ESI=physical adr (where we are)
sub esi,where_am_i ; subtract virtual adr (where we want to be)
; now ESI=virt-to-phys
cmp dword [esi + ds_magic],DS_MAGIC
je ds_ok
mov word [0B8000h],9F44h ; display blinking white-on-blue 'D'
jmp short $ ; freeze
ds_ok:
...

[SECTION .data]
ds_magic:
dd DS_MAGIC
...

More linker gotchas
ld -Ttext=NNN ... 会把代码段定位于地址 NNN, 但是 ld 仍然使用缺省的连接脚本定位 .data 和 .bss. 所以使用自己的连接脚本是一个好的选择.

下面列出的方法均不能在binary kernel中生成一个清零的BSS:
objcopy -O binary krnl.cof krnl.bin
ld --oformat binary -o krnl.bin $(OBJS)
如果你把一个文件附加到binary kernel,
copy /b krnl.bin + ramdisk.bin boot.bin
如果内核试图在地址 'end' 访问附加文件的话, 会发现得到不正确的结果. 因为附加文件覆盖了内核的BSS段,当内核的启动代码或者bootloader清除内核的BSS段时,附加的文件会被擦掉.

11. 从C程序编译的 binary kernel 之Entry point
C 编译的程序其入口不一定是c文件中的第一个函数,试试下面的方法:
copy con hello.c (cat >hello.c for Unix)
#include
int main(void) { printf("hello"); return 0; }
^Z (^D for Unix)
gcc -c -O2 -g hello.c
objdump --disassemble hello.o
00000000 <.text>:
hello 0: 68 65 6c 6c 6f push $0x6f6c6c65
/0 5: 00 89 f6 55 89 e5 add %cl,0xe58955f6(%ecx)
00000008 <_main>:
在.text 段的第一个东东不是main(), 而是字符串 'hello'. 可以把 main() 单独放在一个文件中:
int main(void) { return real_main_in_another_file(); }
或者在文件开头放一个伪 main() 并且文件开始处没有变量或文字:
int real_main(int arg_c, char *arg_v[]);
int main(int arg_c, char *arg_v[])
{ return real_main(arg_c, arg_v); }
/* ... */
int real_main(int arg_c, char *arg_v[])
{ /* ... */ }
再者, 使用编译选项 -fwritable-strings:
gcc -fwritable-strings ...
一个相关的问题: COFF 重定位 (.o) 文件没有 a.out 头, 因此 entry point 不能指定. 可以假定 entry point 始 .text section的开始, 注意这里提到的方法,可以把C 程序的entry放在.text的开始.


12.一些东东需要 线性地址 (LINEAR addresses)
通常的基于段的地址转换不应用到LGDT指令的 "伪描述符" 中, IDT and LIDT 也是如此. 必须自己做转换:
... ; now in real mode
xor ebx,ebx
mov bx,ds
shl ebx,4
add [gdt_ptr + 2],ebx
lgdt [gdt_ptr]
...
gdt:
... ; your GDT here
gdt_end:

gdt_ptr:
dw gdt_end - gdt - 1
dd gdt ; this address is converted to linear

13. 显示输出不工作
也许你这样访问video memory :
*(unsigned char *)0xB8000 = 'A';
只有kernel data从0 开始时这句程序才工作. 如果你的 OS 没有这个条件,就要定义一个单独的保护模式段描述符,基址为 0, 然后使用 far pointer 功能访问 video memory. 假定 LINEAR_SEL 是0基址的段:
#include /* DJGPP only */
...
_farpokeb(LINEAR_SEL, 0xB8000, 'A');
或者, 使用virt_to_phys的转换:
*(unsigned char *)(0xB8000 - virt_to_phys) = 'A';
为了使near pointers 起到作用, kernel data 段必须没有限制 (i.e. limit = 4 Gig - 1 = 0xFFFFFFFF).

14.使用BIOS调用得到正确的内存大小
CMOS 不会报告大于 63.999 meg (65535/1024) 的扩展内存 , 也不会报告 扩展内存的'holes' , 也就是在 15 meg and 16 meg 中间的1m的holes,这个洞存在于特定的 16-bit SVGA 板, 特定的 OSes, 特定的 BIOS 设置.
直接探测会有问题:

地址回绕: 大于64 meg 的地址回到 0.
当探测到使用内存映射的硬件的地址时会引起机器'冻结' .
PC 可能使用一种奇异的方法打开 A20 门; 这种机制不支持直接的内存探测.
未使用volatile的C代码, 或者有bug的compiler 即使使用 volatile 也会产生错误的代码.
总线挂空时,即使没有内存也会使探测成功.
如果你非得使用直接探测,看看这个 直接内存探测

15. 麻烦的A20
没有一种单独的方法可以在所有的pc机上控制 A20 (HIMEM.SYS 支持 17 种不同的方法). 因此:
不要打开 A20, 如果不是必要的话.
如果需要把一些东西拷贝到扩展内存, 使用INT 15h AH=87h. bios 会自动控制 A20 .
可以使用 INT 15h AH=89h 进入 pmode.
如果使用自己的代码打开 A20, 请在打开后检测一下.
你的代码应该多试几种方法.
如果 HIMEM.SYS 已经加载, 使用他的 XMS 服务去控制 A20.
我就要使用自己的代码, 有一个可用就行了.^_^


16.只能从键盘得到一个中断?!
如果你不在你的ISR中清除或复位中断, 你将不能收到后续的interrupt. 对于所有的设备,你必须清除 8259 中断控制芯片. 对于 IRQs 0-7:
outportb(0x20, 0x20);
对于 IRQs 8-15:
outportb(0xA0, 0x20);
outportb(0x20, 0x20);
必须清掉产生这个中断的设备的某个控制位. 通常是读一个 I/O 寄存器.
Timer: (只需要在 8259 芯片清除一把即可)
Keyboard: 从I/O 口 0x60 读扫描码即可
Realtime clock: outportb(0x70, 0x0C); (void)inportb(0x71);
IDE disk: 从I/O 口0x1F7读状态字节就行.

17. 中断处理函数的重入问题
不要在中断处理中使用 printf() ! printf() 和许多其他的函数不能重入. 也不要使用浮点运算.


18. 混合 16位和 32位的代码
使用 aout, .obj (OMF) 或其他支持混合代码的文件格式:
nasm -f elf x.asm
x.asm:30: ELF format does not support non-32-bit relocations
16-bit objects 必须小于 64K (0x10000). 否则:
ld -s -oformat binary -Ttext=0x10000 -ox.bin x.o y.o
x.o(.text+0x13): relocation truncated to fit: 16 text
最后, linker 必须支持你使用的 object 文件的格式:
ld-elf -o test test.o
test.o: file not recognized: File format not recognized
如果你不能满足这种条件,那你必须把 16- 和 32-bit 代码放到不同的文件中.
不要忘记把 BSS清零!
无初值的全局和静态的局部变量存储于 uninitialized data segment (BSS). bootloader 和 kernel startup code 中的一个必须 zero the BSS.

19. 16-bit DPMI 的问题( with Turbo or Borland C for DOS)
Borland C for DOS (version 3.1 or newer) 和 Turbo C for DOS (version 3.0 or newer, not the free version 2.0) 使用16-bit 的DPMI. 这个和DJGPP冲突, DJGPP使用 32-bit的 DPMI. 如果你混合使用 Borland and DJGPP 的工具, 你会发现一些奇怪的信息:
在纯dos 环境下, 使用 DJGPP MAKE (32-bit DPMI) 调用 Turbo C 3.0 (16-bit DPMI) :

c:/tc/bin/tcc.exe -v -mt -w -O2 -d -Z -1 -D__STARTUP_ASM__=1 -c -oboot.obj boot.c
16-bit DPMI unsupported.
make.exe: *** [tboot.exe] Error 1
Using DJGPP MAKE to invoke Turbo C 3.0 from Windows DOS box
(note the absence of error message text):
c:/tc/bin/tcc.exe -v -mt -w -O2 -d -Z -1 -D__STARTUP_ASM__=1 -c -oboot.obj boot.c
make.exe: *** [tboot.exe] Error 234

在纯dos 下用Turbo C 3.0 的MAKE 调用 DJGPP :
gcc -c boot.c
Load error: no DPMI - Get csdpmi*b.zip

** error 110 ** deleting all
Using Turbo C 3.0 MAKE to invoke DJGPP from Windows DOS box:
gcc -c boot.c
Load error: can't switch mode

** error 106 ** deleting all
修正你的方法吧. 在紧要关头, 也可以用 Borland MAKER.EXE 调用 DJGPP 的工具. MAKER.EXE 运行于 real mode, 而不是 16-bit pmode.

20. 连接的问题: C to asm, or C++ to C, or C++ to asm
External C 和 asm 的函数在C++ 代码中调用时必须使用如下的方式声明:
extern "C" void mul64(long *, long, long); /* 32*32 -> 64 multiply */
者就会禁止 'name-mangling' (名称窜改), C++ 使用这种方式实现多态.
参见 http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html

一些编译器 (mainly those for DOS and Windows) 在C程序中定义的名称前加了一个下划线. 汇编中如果调用c程序时必须考虑到这个习惯:

; extern unsigned long virt_to_phys;
GLOBAL _virt_to_phys
_virt_to_phys:
dd 0

; /* extern */ void blink(void);
GLOBAL _blink
_blink:
push ebx
mov ebx,0B8000h
sub ebx,[_virt_to_phys]
inc byte [ebx]
pop ebx
ret

21. 汇编的labels 使用和指令相同的名称
在 NASM中可以避免这个问题, 在label前加上 $ , 但是 label并没有这个$, 他只是告诉 NASM 这是一个标号, 不是保留字:
GLOBAL $cli
$cli:
cli
ret
(Thanks to Julian Hall for this tip.)

22.objcopy -O binary ... 产生的垃圾
确信把你不想要的 sections 从核中移走::
# -g 去掉debug sections (.stabs, .stabstr)
objcopy -g -O binary -R .note -R .comment krnl.elf krnl.bin
MinGW32 objcopy 据说有bug. 我真的不要他了!

23. 用RAWRITE安装bootsect 的问题
RAWRITE DOS 版本把3到一个track的东西一次写道磁盘 .如果用他来安装 bootsector 到一个 FAT12 的floppy, 他会覆盖第一个 FAT. (我不知道是不是 Windows 版本的 RawWrite 更好些, partcopy又 怎么样?)

24.Turbo C .EXE 文件太大了
编译 (tcc -v ...) 或 连接 (tlink /v ...) 时如果使用 debug 选项暗示着 TLINK /v /i ...
/i 选项放一个清零了的 BSS 到 .EXE 文件中. 通常情况下, 只有BSS 的大小在 .EXE 文件头 , BSS memory 在他装载的时候分配.

25.fixed or forbidden register ... was spilled'
新版本的 GNU 汇编程序对行内汇编的 clobber lists 的处理有所不同, 尽管下面的代码使用老版本的 GNU assembler 时工作的很好, 但是现在我们认为这是不正确的::
static inline void
memset(void *__dest, unsigned int __fill, unsigned int __size)
{
__asm__ __volatile__ ("cld
rep
stosb" :
/* no outputs */ :
"c" (__size),
"a" (__fill),
"D" (__dest) :
"ecx","eax","edi","memory");
}
因为 ECX, EAX, and EDI 同时存在于 clobber list 和input constraints. 所以要把他们从 clobber list移出:
...
"D" (__dest) :
"memory");
}
这样就可以无错的编译了.

26.不要把你的Linux program 命名为 'test'
test 是(bash)的一个内嵌命令. 如果你的程序叫test, 很不幸, 实际运行的程序是内嵌的那个. 咋一看, 什么都没有! 上帝,这不是你的错!.


编辑者: hyl (09/18/02 16:55)

文章选项:

hyl
(enthusiast)
09/18/02 12:53
开发杂谈 [re: hyl]  



1. 为什么写 OS ?
研究: 更高,更强,更好
高可考性, 更高的性能,实时,分布,面向对象,更易移植,挑战商业,挑战自我,新构架.

学习
多任务,内存保护,标注c库,设备驱动,文件系统,应用

兴趣
就是喜欢!


2.设计决策
移植性
受限于intel? 使用x86-only 的特性? 4 个特权级(大多机器有只有2个), 基于段的地址转换和保护 (一般都有paging), TSSes 任务切换(vs. 栈交换).

内核体系
Linux? monolithic kernel? microkernel? exokernel? SASOS? .

多任务
不支持?协作?抢占调度?

线程
无? 内核支持还是进程协作? 如何在同宗线程间协调地址空间?

多处理器
不支持?紧耦合的SMP? 松散的分布式?

多用户和安全

开发工作站
linux?没有SourceInsight!
win? ...........

开发语言
C C++ asm ......

可执行文件格式
还有是否支持动态链接? (就如 ELF 和 PE , 他们简单)

高级语言库
GNU glibc, Cygnus Newlib, homebrew, .........

兼容?

抄袭代码
拿来学习的代码.......

编辑者: hyl (09/18/02 13:13)

文章选项:

hyl
(enthusiast)
09/19/02 13:30
boot loader 0.0.1 和最小系统 [re: hyl]  

其代码见: bootloader的一些研究

0.0.1 可以说是最最小的东西了.
相当于一个hello wold!
当初是在win下写的, 但既不知vmWare是何方神圣又不知道partcopy. 只晓得linux下的dd好使, gcc好使. 也真是移到了linux下, 才看到屏幕上的几行简单的信息. 心里的感觉真是无比xx. 那是2002.1.28 深夜23:06.中国年的前夕.

为此, 已经不知多少无眠的夜晚捧着印出来的minix bootsect, linux0.0.1 bootsect 看来看去.... 至今这种印记还在代码中. 从floppy读os的代码我实在没有兴趣, 因此至今还是linux的老样子.

如果没有 make binary use C Compier , 没有nondot, 估计到如今写不可能有0.0.1的诞生.
0.0.1 中的Makefile中, 最最有用的一句:

# 将生成的操作系统文件从 elf 格式转换到 binary 格式
os.bin: os.elf
objcopy -R .comment -R .note -S -O binary os.elf os.bin


就是来自于nondot的那篇文章.

关于这个Makefile 可能还有一句值得一看:

....
OBJS = c.o
.......
os.elf: $(OBJS)
$(LD) $(OBJS) -o os.elf -e c -Ttext 0
# 因为我们的映象是binary格式.
# 所以入口点函数应该是第一个.o文件的第一个函数


有件事情我很抱歉, 0.0.1中没有使用head.s.
只有boot.s 和c.c.

boot.s 把c.c 生成的bin文件os.bin从磁盘上载入内存, 切换到保护模式, 跳到os.bin.
看看makefile中的

# 最终生成文件 bootimg
bootimg:os.bin Boot
cat Boot os.bin>bootimg
# Boot 是引导扇区内容,内核 os.bin 紧跟其后的扇区

应该能够想象整个的流程.

c.c 除了打印了一句信息外好像什么都没有做.
从c.c生成的os.bin是32位的代码, 需要保护模式才能运行. 其中的函数

/*
Text mode kprint
Show 'c' at
line : x (0...24 )
col : y (0...79 )
*/
void kputc(char c,char color,int x,int y)
{
/* p ponitor to Video Memory */
char *p = (char*)0xb8000;

/* calc line pos*/
p += 2*x*80+y*2;

/* show char whith color*/
*p = c;

*(p+1) = color;
}

要想正确工作, os的ds选择子选择的描述符所描述的段之基址必须从0 开始, 可读写, limit 大于video mem上限(0.0.1是8m, 见boot.s).


0.0.1是我的希望之光.

文章选项:

hyl
(enthusiast)
09/19/02 14:30
0.0.2---0.0.5 启动构架 [re: hyl]  

0.0.1 太简单, 不知道他能够做什么工作.
0.0.2 中, 好像只是把c.c 搞来搞去,没有实质性的工作.

0.0.3 中多了两个文件, setup.s, head.s . 这个可以从Makefile中看出来.
关键在于, boot.s中不再切换到保护模式了, 这样的话,我可以在setup.s 中多做一些事情. 因为boot.s 只有512 字节,太少了!
可以看到, 0.0.3 重的setup.s 所作的事情也很简单. 它把os(head.s, c.c)搬到物理地址0. 然后使用临时的gdt切换到保护模式.最后跳到os的head.s.
head.s 重新加载了os自己的gdt, 因为setup.s 不属于操作系统的一部分!
可以看到setup.s 和head.s 的基本功能. 更重要的是基于这样一个设想, 在setup.s中, 未切换到保护模式前, 可以写更多的代码完成更多的功能.甚至我使用16bit 的c编译器, 配合bios, 那就方便的多了. head.s主要是os的一个入口, 单独放到一个文件中使入口管理更方便. 其中的idt,gdt也使os的全局变量更加集中.

考虑到把os放到地址0:0 不合适, 所以0.0.3.5中, os 被移到0x20000, 位于物理内存 128k处.

但是还是觉得不爽, 干脆在0.0.4中, 切换到保护模式后直接把os放到1M的地方. 离开常规内存这个是非之地.

从Makefile 中ld -T 的选项可以清楚的看到这种变化.
中间遇到很多问题, 一些记录了下来,一些却没有.


0.0.5 中我想试一试c++ 好不好玩.

文章选项:

hyl
(enthusiast)
09/20/02 16:12
轮询式驱动 [re: hyl]  

新的简单内核中,比如说bootloader 0.0.5----, 还没有多任务支持, 没有中断机制的支持, 没有应该有的一切.

怎么在这种情况下做一些简单的i/o可能是开发到这个阶段比较关心的问题.
找一些资料, 比如kbd, hd, vga......

0.0.5对vga的支持仅仅是操作默认的framebuffer, 甚至连光标都移动不了.
国外有些好资料, 前面我也推荐了不少网站.

看看这些资料...... 拿来我们先用用!


感谢 execpc.osd 的兄弟们杰出无私的奉献.


edit by hyl

编辑者: xshell (09/26/02 09:55)

文章选项:

hyl
(enthusiast)
09/20/02 17:38
VGA [re: hyl]  

关于在实模式下使用bios 输出文本的方法这里不再讨论, 如果那位兄弟还不清楚到google搜一把.

推荐
Ralf Brown著名的中断列表
freshground (old hand) 推荐
bios中断表



保护模式中不依赖bios才是重点.


原理: 显示到屏幕上的字母和符号统统存在于一段叫做 framebuffer 的显存中. 至于其出现于内存的物理地址, 要看VGA板的工作模式. VGA

的两种模式是: monochrome (单色?) emulation , 或者color emulation.

emulation---|--framebuffer linear address--|--framebuffer real-mode address--|--I/O address of CRTC
color-------|--B8000h----------------------|--B800h:0000 --------------------|--3D4h
monochrome--|--B0000h----------------------|--B000h:0000 --------------------|--3B4h


CRTC 是VGA的一个功能单元, 待会再讨论有关CRTC的东东. 一般来说, 应该是 color emulation, 记得大一的时候我们的实验室倒是有几台386

上有 monochrome 的古董.

备注1 的c代码可以检测VGA是处于那种工作模式.
如果能够看懂, 拿来用用应该不成问题.

不会弄代码的格式, 大家拷贝后自己整理吧.
这里给一个简单的.


/* video card mono/colour detection by Dark Fiber
* returns 0=mono, 1=colour
*/
int detect_video_type(void)
{
int rc;
char c=(*(USHORT*)0x410&0x30

/* C can be 0x00 or 0x20 for colour, 0x30 for mono
if(c==0x30)
rc=0; // mono
else
rc=1; // colour

return rc;
}






字符及属性


在framebuffer中的每个字符都占用两个字节: ASCII 码值在地址 N 的话, N+1 就是他的属性字节.
属性字节的各个位的含义如下.

b7 ----- 闪烁
b6:b4 -- 背景色(0-7)
b3:b0 -- 前景色(0-15)


color value -- color -- color value -- color
0 ------------ 黑色------ 8 ---------- 暗灰
1 ------------ 蓝色------ 9 ---------- 亮蓝
2 ------------ green ---- 10 --------- bright green
3 ------------ cyan ----- 11 --------- bright cyan
4 ------------ red ------ 12 --------- pink
5 ------------ magenta -- 13 --------- bright magenta
6 ------------ brown ---- 14 --------- yellow
7 ------------ white ---- 15 --------- bright white


假定使用color模式:

Turbo C 代码的例子(cpu 工作于 16-bit real mode), 把白色的 'H' 以蓝色背景放到屏幕左上角.

#include /* pokeb() */
pokeb(0xB800, 0, 'H');
pokeb(0xB800, 1, 0x1F);


NASM 汇编中这么写(16-bit real mode):

mov bx,0B800h
mov es,bx
mov byte [es:0],'H'
mov byte [es:1],1Fh


DJGPP 代码(32-bit pmode), 有所不同. 把黄的 'H' 以红色背景放到屏幕右上角.因为处于保护模式, 我们使用far指针.

#include /* _farpokeb() */
#include /* _dos_ds */
_farpokeb(_dos_ds, 0xB8000 + 79 * 2 + 0, '*');
_farpokeb(_dos_ds, 0xB8000 + 79 * 2 + 1, 0x4E);


非得用 near 指针?

#include
#include
#include

unsigned char *fb;

if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))
{ if(!__djgpp_nearptr_enable())
{ printf("Could not enable nearptr access/n");
return -1; } } /* probably Windows NT DOS box */
fb = (unsigned char *)0xB8000 + __djgpp_conventional_base;
fb[79 * 2 + 0] = '*';
fb[79 * 2 + 1] = 0x4E;



Scrolling(滚屏)


BIOS 滚屏就算了吧?!

如果使用 movedata() , 也算简单. 与memcpy() 不同的地方在于movedata对于源和目的都使用 far指针.


Turbo C 代码: 在 80x25 的方式下上滚一行(color emulation):

#include /* movedata() */
movedata(0xB800, 80 * 2,
0xB800, 0,
80 * (25 - 1) * 2);


DJGPP 代码 scroll 80x25 display up one line (color emulation):

#include /* movedata() */
#include /* _dos_ds */
movedata(_dos_ds, 0xB8000L + 80 * 2,
_dos_ds, 0xB8000L,
80 * (25 - 1) * 2);


使用 movedata() 的的话,如果 src < dst, 比如下滚, 可能不正确. 使用near 指针最好了, memmove() 保证任何滚动都能正确的工

作.


hardware scrolling

硬件来做滚动就比较快了. 把VGA配置成使用不同地址的framebuffer 就可以实现快速滚屏. CRTC 寄存器 12 号13号 分别包含framebuffer

相对于B0000h, B8000h, or A0000h 之偏移(offset) 的MSB 与 LSB .


/* scroll up one line */
#include /* outportb() */
unsigned short crtc_adr = 0x3D4; /* 0x3B4 for monochrome */
unsigned short offset = 80;

/* the CRTC index is at crtc_adr + 0
select register 12 */
outportb(crtc_adr + 0, 12);
/* the selected CRTC register appears at crtc_adr + 1 */
outportb(crtc_adr + 1, offset >> 8);
outportb(crtc_adr + 0, 13);
outportb(crtc_adr + 1, offset & 0xFF);



硬件滚屏的缺陷在于不能够持续无限的滚动. 因为最终 framebuffer 会超过 video memory 的上(下)限.
可以用作 framebuffer 的那段内存可以分成几个虚拟控制台(virtual consoles (VCs)). 32K 的 video memory 可以被分成8 个80x25的VCs.

console 译作控制台我认为不妥, 这里的console无非就是虚拟的几个屏幕.上面的代码就可以选择把那个虚拟屏呈现给用户. (Linux 的 VCs

使用了不同的管理方法, 我不知道.)


Moving the cursor


CRTC 寄存器14号和 15 号, 包含光标位置的 MSB LSB . 光标的位置用相对B8000h 或 B0000h的偏移来表示.

#include /* outportb() */
unsigned short crtc_adr = 0x3D4; /* 0x3B4 for monochrome */
unsigned short offset;
unsigned short x = 20, y = 3;

offset = x + y * 80; /* 80 characters per line */
outportb(crtc_adr + 0, 14); /* MSB of offset to CRTC reg 14 */
outportb(crtc_adr + 1, offset >> 8);
outportb(crtc_adr + 0, 15); /* LSB of offset to CRTC reg 15 */
outportb(crtc_adr + 1, offset);



[i] 不要告诉我, 你不知道outportb 那里去找! [/i]


推荐网站
pc-hardware
VGADOC
一个vag包
execpc的控制台代码






备注1





/*****************************************************************************
Determines if VGA board is set for monochrome or color emulation.
Uses 3 different algorithms.

This code is public domain (no copyright).
You can do whatever you want with it.
*****************************************************************************/
#include /* atoi() */
#include /* printf() */
//#include "../port.c" /* inportb(), peekw() */

/********************************* TURBO C **********************************/
#if defined(__TURBOC__)
#include /* inportb(), peek() */

#define peekw(S,O) peek(S,O)

/********************************* DJGPP ************************************/
#elif defined(__DJGPP__)
#include /* _CRT0_FLAG_LOCK_MEMORY */
#include /* inportb() */

//#define NEARPTR 1

/* near pointers; not supported in Windows NT/2k/XP DOS box
Must call __djgpp_nearptr_enable() before using these functions */
#if defined(NEARPTR)
#include /* __djgpp_conventional_base, __djgpp_nearptr_enable() */
#include /* printf() */
#include /* _CRT0_FLAG_NEARPTR, _crt0_startup_flags */

#define peekw(S,O) *(unsigned short *)(16uL * (S) + (O) + /
__djgpp_conventional_base)
/* far pointers */
#else
#include /* _farpeekw() */
#include /* _dos_ds */

#define peekw(S,O) _farpeekw(_dos_ds, 16uL * (S) + (O))
#endif

/******************************** WATCOM C **********************************/
#elif defined(__WATCOMC__)
#include /* inp() */

#if defined(__386__)
/* CauseWay DOS extender only */
#define peekw(S,O) *(unsigned short *)(16uL * (S) + (O))
#else
#include /* MK_FP() */

#define peekw(S,O) *(unsigned short far *)MK_FP(S,O)
#endif

#define inportb(P) inp(P)

#else
#error Not Turbo C, not DJGPP, not Watcom C. Sorry.
#endif


static unsigned short g_crtc_base_adr;
/*****************************************************************************
Pentium 486 Bochs
method color color (color) mono
------ ------- ----- ------- -------
1 pass pass pass UNTESTED
2 pass pass pass UNTESTED
3 pass pass pass UNTESTED
*****************************************************************************/
int main(int arg_c, char *arg_v[])
{
int method;

#if defined(__DJGPP__)&&defined(NEARPTR)
if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))
{
if(!__djgpp_nearptr_enable())
{
printf("Could not enable nearptr access "
"(Windows NT/2k/XP?)/nUn-define NEARPTR "
"in source code and re-compile/n");
return 1;
}
}
#endif
if(arg_c < 2)
{
printf("attempt to detect monochrome/color VGA emulation "
"using one of three methods/n"
"specify 1, 2, or 3 on the command line/n");
return 1;
}
method = atoi(arg_v[1]);
switch(method)
{
case 1:
/* this method cobbled from info in Finn Thoegersen's VGADOC4 */
#define VGA_MISC_READ 0x3CC

if((inportb(VGA_MISC_READ) & 0x01) == 0)
g_crtc_base_adr = 0x3B4; /* mono */
else
g_crtc_base_adr = 0x3D4; /* color */
break;
case 2:
/* I forgot where this came from:
"The word at low memory address 0040:0063 (or 0000:0463) contains the
I/O address of the CRTC which can be used to determine whether the video
system is colour or monochrome. A value of 3B4 hex indicates monochrome."
(I presume 3D4 hex means color; my Pentium system has that value at 0463.) */
g_crtc_base_adr = peekw(0x40, 0x63);
break;
case 3:
/* Dark Fiber's method, from the OS FAQ
[url=www.mega-tokyo.com/os]http://www.mega-tokyo.com/os[/url]

from MEMORY.LST of Ralf Brown's Interrupt List
0040:0010 is Installed Hardware word, b5:b4 indicate video hardware:
00 EGA,VGA,PGA, or other with on-board video BIOS
01 40x25 CGA color
10 80x25 CGA color
11 80x25 mono text

whoa, this won't work with DJGPP -- OK, I will make a slight change here
if((*(unsigned short *)0x410 & 30) == 0x30) */
if((peekw(0x40, 0x10) & 30) == 0x30)
g_crtc_base_adr = 0x3B4; /* mono */
else
g_crtc_base_adr = 0x3D4; /* color */
break;
default:
printf("didn't find 1, 2, or 3 on the command line, sorry/n");
return 1;
}
/* what've we got? */
if(g_crtc_base_adr < 0x3C0)
printf("MONOCHROME emulation detected/n");
else
printf("color emulation detected/n");
return 0;
}

编辑者: hyl (10/11/02 16:45)

文章选项:

hyl
(enthusiast)
09/23/02 21:38
附加档案
梦中的开发环境(0.0.5.1告别linux) [re: hyl]  

0.0.5 以前的版本再linux下编译. 当时对boch vmWare不太了解.
调试及其痛苦. linux下的vim虽然语法加亮,虽然....我却不太会使用.

倒是非常喜欢SourceInsight.

知道windows下的nasm djgpp也是free,但是当时从没有再win下编译出一个bin核.

试图找出一个linux版本,有很好的中文支持, 有一个类似sourceinsight的工具, 我当然不希望用蹩足的英文注释我的代码....

但是始终没有一个中文环境, 使自己满意.

大四时玩turbo 4.1, 只会安装而已,
现在在linux下体验os, 感叹啊.......

还是不断的努力做到在win下开发.......
windows下我很容易找到16bit的C编译器,可以增强loader.

2002.9.16这天, 又找出nasm, vmWare, djgpp.....
没想到, 这么顺利的在vmWare下调出了熟悉的画面.

真是让人兴奋.

上穿的版本中如果make会找不到一个文件 pad .
这个pad什么都不是,仅仅为了在vmWare下调试时把bootimg扩展到1.4M以上, 可以随便找个什么东西代替!


新的特性
见0.0.5.1的readme.txt
5.移植到djgpp 使用windows 下的gcc nasm 编译成功,并且使用vmWare调试.
至此,使用下面的工具: sourceinsight, vmvare, djgpp, nasm. (这是我梦中的环境^_^)
下一步考 虑使用tc 或者其他的编译器制作setup中的16位程序. (使用C 加强setup.s 的功能)

编辑者: hyl (09/23/02 21:55)

文章选项:

hyl
(enthusiast)
09/24/02 13:19
keyboard [re: hyl]  

thanks Adam Chapweske
信号和协议, 扫描码, 命令集, 初始化,兼容性问题, pc的键盘控制器.
参考



历史

常用键盘包括:
USB 键盘 - 最近为所有的新机器所支持(Macintosh and IBM/compatible).
IBM/兼容 键盘 - 也称 "AT keyboards" 或者 "PS/2 keyboards", 现代pc都支持. 本文的主题.
ADB 键盘 - Apple Desktop Bus of older Macintosh systems.
原来的IBM 以及兼容机使用一种称作 "XT "的键盘. 现在不多见了,我们不介绍.后来IBM引入 AT 系统, AT之后是IBM PS/2. AT 键盘和 PS/2

键盘类似,是我们常用的键盘. PS/2 设备使用更小的连接器,支持更多一点的特性. 同时兼容AT, 新的特性几乎不使用.

IBM 的几种键盘:

IBM PC/XT 键盘 (1981):
83 个键
5-pin DIN 连接器
简单单向串口协议
使用扫描码 set 1
没有 host-to-keyboard 的命令

IBM AT 键盘 (1984) - 不兼容XT
84 到 101键
5-pin DIN 连接器
双向串口协议
使用扫描码 set 2
8个 host-to-keyboard 命令

IBM PS/2 键盘(1987) - 兼容AT
84 到 101 键
6-pin mini-DIN 连接器
双向串口协议
可选扫描码 set 3
17 个 host-to-keyboard 命令


今天买的键盘都兼容 PS/2 和 AT .所以今天的 "AT" 和 "PS/2" 有意义的只有他们的接口大小. 尽量不使用扩展特性.


现代兼容 AT-PS/2 键盘
任意键数目 (通常是101 或 104)
5-pin or 6-pin 连接器
双向串口协议
扫描码 set 2 肯定支持
应答所有的命令,但是可能并无此功能.



简单描述:

键盘包含一个由 keys组成的矩阵. 所有的键都为一个板上处理器监控,称作键盘编码器, (一般是i8048? 见下表).虽然这种芯片挺多,但是其职
能基本如下:
监控是哪个或那几个键被按下/释放,把相应的数据送到主板. 这个处理器处理所有的 debouncing(?what!) ,把数据缓存到他的16-byte 的缓冲区中. 在IBM兼容机上,主板也有一个板上芯片,称作键盘控制器.一般是8042. 他负责解码从键盘来的信息,通知系统软件各种事件.在host 和主板的通讯中 都使用IBM 协议.


现代键盘的encoders:
Holtek: HT82K28A, HT82K628A, HT82K68A, HT82K68E?EMC: EM83050, EM83050H, EM83052H, EM83053H,?Intel: 8048, 8049
Motorola: 6868, 68HC11, 6805
Zilog: Z8602, Z8614, Z8615, Z86C15, Z86E23


电器接口和协议:
AT 和 PS/2 键盘使用和 PS/2 鼠标一样的协议. 这里是协议的细节.
扫描码:
键盘处理器(encoder)大部分时间在"扫描", 监视着键矩阵. 一旦发现有键被按下,释放,或被按住不放,encoder就会向计算机发送一个数据包,称为扫描码. 有两种不同的扫描码, "make codes" 和 "break codes". make code 是键被按下,按住不放是产生的. break code 是键被释放时产生的. 每个键都有自己唯一的make code 和 break code. make code 和 break codes 的集合称为扫描码集. 共有三种标准的扫描码集.所有现代的键盘默认使用扫描码集 set 2.

详细情况见:
Scan Code Set 1 - 原来的 XT 扫描码集, 也有现代键盘支持此种东东.
Scan Code Set 2 - 现代键盘的缺省扫描码集.
Scan Code Set 3 - 可选的 PS/2 扫描码集,很少使用.

Make Codes, Break Codes, and Typematic Repeat:
a make code 和一个 ASCII 码之间并无固定的转换关系.尽管set 2的 make codes 大部分只有1个字节宽度, 但是有"extended keys" 的make codes 是2或4个字节宽,这些扩展键的make codes 的第一个字节总是 E0h.?每个键也有自己的唯一 break code(1). 但总是和make code有联系, 给程序设计带来不少方便. set 2 的大部分break codes 有2个字节宽,第一个字节是 F0h 第二个是那个键的make code. 扩展键的 Break codes 通常3个字节,前两个字节是E0h, F0h, 最后一个字节是那个键的 make code的最后一个字节.
按住一个键不放时,那个key 变成 typematic, 意味着键盘会不断的向主板发送那个键,直到释放他,或者另外一个键按下.这个过程有两个参数: typematic 延迟, typematic 速率. typematic 延迟从 0.25 秒到 1.00 秒, typematic 速率从 2.0 cps (characters per second) 到 30.0 cps. 可以使用"Set Typematic Rate/Delay" (0xF3) 命令改变这个值.如果多个键被按住不放,只有最后一个键成为typematic. 实际上,"Pause/Break" 键没有 break code(set 1,2).


i8042 :键盘控制器

以上的讨论都是针对硬件,其实,如果写一个底层的键盘相关的软件for PC,是不该直接和键盘通信的. 主板上一般都有键盘控制器,它在键盘和外设总线间是一个接口. 这个控制器处理 signal-level的东东和协议的细节 ,同时提供转换,解释,处理扫描码,执行命令的功能.
PC 的键盘一般使用Intel 8042/兼容 的微控制器.现代计算机上,这个功能一般集成到南桥 . 然而,这个设备逻辑上仍然叫做 "the 8042".基于主板的不同,键盘控制器可以工作于:"AT-兼容" 模式, 或者 "PS/2-兼容" 模式. 如果主板支持 PS/2 鼠标就会使用后者. 这时, 8042 既是键盘控制器又是鼠标控制器. 键盘控制器根据硬连线的方式自动决定工作于哪种模式.

8042 包含如下寄存器:

1字节的输入缓存 - 包含从键盘来的数据,只读
1字节的输出缓存 - 包含,要被写到键盘的数据;只写
1字节的状态寄存器 - 8 个状态位; 只读
1字节控制寄存器 - 7 个控制位; 读写?

前三个寄存器可以被cpu通过io端口0x60 and 0x64直接访问.最后一个必须使用"Read Command Byte" 命令读, 使用"Write Command Byte" 命令写.(见关于键盘的其他帖)

8042的端口在cpu的io空间地址如下:

port----Read/Write-----Function
0x60----Read----------Read Input Buffer
0x60----Write---------Write Output Buffer
0x64----Read----------Read Status Register
0x64----Write---------Send Command


写端口 0x64 不会写任何指定的寄存器, 只是给8042一个命令. 如果命令有参数,参数就送到端口0x60. 命令的返回值也从端口 0x60去读.
("input buffer" : input from the keyboard, "output buffer": output to be sent to the keyboard.)

键盘复位
加电时,(或者"Reset" 命令) , 键盘进行 BAT (Basic Assurance Test)并装载下面的缺省值:

Typematic delay 500 ms.
Typematic rate 10.9 cps.
*选择扫描码集 set 2.
*把所有键设置为 typematic/make/break 统统使能.

* 一些键盘可以改变,一些不能.

进入 BAT后, 三个 LED 点亮,BAT 完成之后熄灭.同时,BAT 代码返回给host: 0xAA (BAT successful) , 0xFC (Error). 许多键盘在BAT时忽略CLOCK 和 DATA 线, 直到BAT完成.因此, "禁止条件" (CLOCK line low) 不能键盘阻止向host发送BAT code.





参考资料
Adam's micro-Resources Home
Keyboard Scan Codes
PS/2 Mouse/Keyboard Protocol - Protocol used by AT and PS/2 keyboards.
Keyboard Code/Projects

编辑者: hyl (09/24/02 18:43)

文章选项:

hyl
(enthusiast)
09/24/02 13:46
i8042-----寄存器 [re: hyl]  


1字节的输入缓存 - 包含从键盘来的数据,只读
1字节的输出缓存 - 包含,要被写到键盘的数据;只写
1字节的状态寄存器 - 8 个状态位; 只读
1字节控制寄存器 - 7 个控制位; 读写?



状态寄存器(Read)

映射到 io 地址空间 0x64 (0x64 号io端口), 只读. 也就是读io地址0x64就是读状态寄存器. 一个字节长度, AT模式 和 PS/2模式 稍有不同.

PS/2 模式:

Bit 7 - 奇偶校验错
置1时代表和keyboard或者mouse的最后一次字节传输出现偶校验错误.(rx或tx)

Bit 6 - 超时
最后一次传输(RX或TX)超时时置1.

Bit 5 - Mouse 输出缓冲满
鼠标输出缓冲有数据时置1.

Bit 4 - 键盘锁定状态
未锁定时置1.

Bit 3 - 命令/数据指示
如果最后一次写的是command此位置1. 0 代表写入数据.

Bit 2 - 系统标志
上电时是0,自检成功后是1.

Bit 1 - 键盘输入缓冲满 (host --> keyboard)
如果输入缓冲有数据此位置1.

Bit 0 - 键盘输出缓冲满 (keyboard --> host)
如果键盘输出缓冲区有数据此位置1.

AT 模式:

Bit 6 - RX 超时t
上一次RX超时时置1.

Bit 5 - TX 超时
上一次TX超时时置1.



控制寄存器 (ReadWrite)
~~~~~~~~~~~~~~~~~~~~~

没有映射进io地址空间, 不能被cup直接访问. 键盘命令0x20 和 0x60 用于读写这个寄存器.
PS/2 和 AT 模式仍有细微差别.

PS/2 模式:

Bit 7 - 保留
大部分控制器总是0

Bit 6 - PC 兼容模式
置1时, 键盘扫描码转换被启动

Bit 5 - 禁止鼠标接口
置1时, 鼠标接口被禁止

Bit 4 - 禁止键盘接口
置1时, 键盘接口被禁止

Bit 3 - 忽略键盘锁
置1时, 键盘锁被忽略

Bit 2 - 系统标志
连接到状态寄存器bit 2

Bit 1 - 鼠标中断使能
置1后, 控制器会在接受到鼠标的数据后产生IRQ12 (P25)中断.

Bit 0 - 键盘中断使能
置1后, 控制器会在接受到键盘数据后产生IRQ1 (P24) 中断.

AT 模式:

Bit 5 - AT 键盘协议
置0时, 控制器使用11-bit 的AT 协议
置1时, 使用XT协议


输入输出缓冲 (ReadWrite)
输入和输出缓存都映射到同一个寄存器,共享io地址,0x60.

编辑者: hyl (09/24/02 13:50)

文章选项:

hyl
(enthusiast)
09/24/02 13:57
i8042-----端口 [re: hyl]  

为了讨论的方便, 有必要介绍i8042的端口(一组pin). 这些pin的操作在 i8042-----控制器命令 中有详细描述.


输入端口(ReadWrite)
在控制芯片自己上的8个pins 组成输入端口(input port).现在这个芯片已经被集成到南桥,pins已经找不到了, 但是功能还是没有改变. 输入端口可以被控制器命令 0xbx , 0xc0-0xc2所访问. Pin 的定义,除了 P10 , P11 在不同的主板上极为不同. P12也在所有的ps/2 型的机器上被定义为 Turbo.


Bit 7 - P17 - 键盘 禁止/安全 锁
置1时锁定

Bit 6 - P16 - 显示器类型
CGA时是0, MDA 或其他种类时是1

Bit 5 - P15 - Jumper / Phoenix 的Turbo pin
P15 jumper 安装时为1 / 1 fast, 0 slow

Bit 4 - P16 - 系统RAM / 系统speed
1 代表640K, 或者高速, 0 代表512K 或低速

Bit 3 - P13 - Turbo pin
1 代表 8 MHz, 0 代表4 MHz

Bit 2 - P12 - Turbo pin / NPU
1 high speed, 0 slow / 0 80287, 1 not installed

Bit 1 - P11 - 鼠标数据输入
用于获得的鼠标数据?

Bit 0 - P10 - 键盘数据输入
用于获得的键盘数据?


输出端口(ReadWrite)

和输入端口极为类似. 使用控制器命令0xbx and 0xd0-0xd1访问. 每个制造商的定义都不同.

PS/2 模式:

Bit 7 - P27 - 键盘数据输出
用于驱动键盘数据线

Bit 6 - P26 - 键盘时钟输出
用于键盘时钟

Bit 5 - P25 - 鼠标中断
连接到IRQ 12

Bit 4 - P24 - 键盘中断
连接到IRQ 1

Bit 3 - P23 - 鼠标时钟输出
用于鼠标时钟

Bit 2 - P22 - 鼠标数据输出
用于驱动鼠标数据线

Bit 1 - P21 - A20 Gate
置1 时容许A20
Bit 0 - P20 - SYSRST
置1 复位系统

AT 模式:

Bit 5 - 输入缓冲区空
1 代表输入缓冲空

Bit 4 - 输出缓冲空满
1 代表输出缓冲满

Bit 3 - 保留(Turbo, password, lock)

Bit 2 - 保留 (Turbo, password, lock)
Award BIOSes 上时Turbo pin.


测试口(Read)
很像输入端口和输出端口. 只读, 因为只有一个读命令 0xe0.

Bit 1 - T1 - 鼠标时钟输入
用于鼠标时钟

Bit 0 - T0 - 键盘时钟输入
用于键盘时钟

文章选项:

hyl
(enthusiast)
09/24/02 14:17
i8042-----控制命令 [re: hyl]  


和状态寄存器映射到相同的 io 地址空间 0x64 (0x64 号io端口). 写端口 0x64 不会写任何指定的寄存器, 只是给8042一个命令. 如果命令有参数,参数就送到端口0x60. 命令的返回值也从端口 0x60去读.




控制器命令

下面的某些命令不一定被所有的控制器所支持.使用下面的方法向控制器发送命令:
向 0x64 写一个命令, 然后把数据写入 输出缓冲(0x60) .

[nothing] - n/a - 标准
如果不向命令口(0x64)写任何东西就写输出缓存(0x60), 数据就直接送到keyboard.

0x20-0x3f - n/a - 标准
读控制器的 RAM. 地址由命令的低5 bit指出. 偏移0时是 控制寄存器. 一些控制器只容许读 控制寄存器 , 也就是命令0x20, 其他请求统统忽略.

0x60-0x7f - n/a - 标准
写控制器的RAM , 地址由命令的低 5 bits 给出.偏移 0 是 控制寄存器. 一些控制器只响应0x60.

0x90-0x9f - n/a - VIA
把低4 bits 写到 输入端口的低 4 bits (P10-P13).

0xa0 - n*R - AMI
从键盘控制器读出版权信息的ASCIIZ 字符串.

0xa1 - 1*R - VIA, AMI
读控制器版本号.

0xa2 - n/a - AMI
把 P22 和 P23 置为低电平

0xa3 - Compaq
使能系统速度控制

0xa3 - n/a - AMI
把 P22 和 P23 置高电位

0xa4 - 1*R - PS/2, VIA
检测密码. 如果没有安装返回 0xf1 , 安装了就返回 0xfa. (?)

0xa4 - n/a - AMI
把系统设置为低速

0xa4 - n/a - Compaq
切换系统速率

0xa5 - n*W - PS/2
加载密码. 以0 结尾的字符串之形式送给键盘控制器.

0xa5 - n/a - AMI
把系统设置为高速

0xa5 - 1*R - Compaq
特殊读. 读端口2 (output?). 重新生成Bits 4 ,5 . Bit 4 代表 kbd 中断使能, bit 5 代表 AT 键盘使用中 (对应 XT).

0xa6 - n/a - PS/2
密码匹配. 用从键盘输入的字符匹配加载的密码,直到匹配成功.

0xa6 - 1*R - AMI
得到系统速度. 系统速度标志是读出字节的第0 bit.

0xa7 - n/a - PS/2, VIA
禁止鼠标接口. 把控制端口bit 5 and P23 置1

0xa7 - n/a - AMI
设置内部的 'bad write cache' 标志?

0xa8 - n/a - PS/2, VIA
使能鼠标接口. 把控制端口bit 5 and P23 设置为0

0xa8 - n/a - AMI
清除内部的 'bad write cache' 标志?

0xa9 - 1*R - PS/2, VIA
鼠标接口探测. 返回0x00 代表 OK, 0x01 代表 clock stuck low, 0x02 代表clock
stuck high, 0x03 代表数据stuck low, 0x04 代表数据 stuck high, 0xff 一般错误

0xa9 - 1*R - AMI
得到内部的 'bad write cache' 标记. 是读出字节的 bit 0

0xaa - 1*R - 标准
控制器自检. 成功返回 0x55 , 失败返回0xFC . 也许会禁止键盘, 并把几乎所有的
东东复位到一些特定状态.

0xab - 1*R - 标准
键盘接口测试. 成功时返回0x00 , 0x01 clock stuck low, 0x02
clock stuck high, 0x03 data stuck low, 0x04 data stuck high, 0x05 Compaq
diagnostic feature, 0xff 普通错误

0xac - 20*R - IBM
诊断命令. Sends 16 bytes of 8042's RAM, current input port
state, current output port state and 8042 program status ord

0xad - n/a - 标准
禁止键盘接口. 设置控制寄存器的bit 4.

0xae - n/a - 标准
使能键盘接口. 清除控制寄存器的bit 4.

0xaf - 1*R - Award, VIA
读键盘控制器版本

0xb0-0xb7 - n/a - AMI, VIA
设置以下pin之一为low. P10, P11, P12, P13, P22, P23, P14, P15.

0xb8-0xbf - n/a - AMI, VIA
设置以下pin之一为high. P10, P11, P12, P13, P22, P23, P14, P15.

0xc0 - 1*R - 标准
读输入端口 (P10-P17)

0xc1 - n/a - PS/2, VIA
轮询输入端口的 (P10-P13). 把结果值放到状态寄存器的bits 5-7, 直到另一个命令到来.

0xc2 - n/a - PS/2, VIA
轮询输入端口的 (P14-P17). 把结果值放到状态寄存器的bits 5-7, 直到另一个命令到来.

0xc8 - n/a - AMI, VIA
使 0xd1 命令工作在 P22 和 P23上 (mouse clock & data).

0xc9 - n/a - AMI, VIA
使 0xd1 命令忽略P22 和 P23 - 这是默认值.

0xca - 1*R - AMI, VIA
读控制器模式. Bit 0 - 1 for PS/2, 0 for AT mode

0xcb - 1*W - AMI
设置控制器模式.

0xd0 - 1*R - 标准
读输出端口(P20-P27)

0xd1 - 1*W - 标准
写输出端口(P20-P27), 和命令 0xc8 and 0xc9 有关. 小心:bit 0 是复位,bit 1 是Gate A20.

0xd2 - 1*W - PS/2, VIA
写键盘输出缓存. 写进输入缓存的字节会出现于输出缓存. 如果容许,就有一个中断产生.

0xd3 - 1*W - PS/2, VIA
写鼠标输出缓存. T写进输入缓存的字节会出现于输出缓存. 如果容许,就有一个中断产生.

0xd4 - 1*W - PS/2, VIA, Award
写鼠标. 写入的字节发送给鼠标.

0xdd - n/a - HP Vectra
禁止 A20

0xdf - n/a - HP Vectra
使能 A20

0xe0 - n/a - Standard
读测试端口 (T0-T1)

0xe0-0xef - n/a - Award, VIA
写输出端口. P23-P21 映射 bits 3-1.

0xf0-0xff - n/a - 标准
P23-P21 设置成低电平, 持续 6 usec, 映射 bits 3-0. 这就是系统reset过程.

编辑者: hyl (09/24/02 17:54)

文章选项:

hyl
(enthusiast)
09/24/02 17:53
i8042----键盘命令 [re: hyl]  



键盘和鼠标命令

下面的命令有两种方式送到鼠标或键盘.
1. 直接写输出缓冲 (见控制器命令 [nothing], 不确信键盘是否只能这么写?)
2. 使用控制器命令 0xd4(送到mouse?)
每一个命令, 键盘/鼠标 都会应答一个ACK 字节.

0xe6 - n/a - Mouse
设置鼠标的缩放比为 1:1.

0xe7 - n/a - Mouse
设置鼠标缩放比为 2:1.

0xe8 - 1*W - Mouse
设置鼠标分辨率, 0 - 1/mm, 1 - 2/mm, 3 - 4/mm, 4 - 8/mm.

0xe9 - 3*R - Mouse
得到鼠标信息.
得到的三个字节意义如下:

Byte 0:

Bit 7 - 未用 (0)
Bit 6 - Remote (1) / Stream (0) 模式
Bit 5 - 鼠标使能
Bit 4 - 缩放比为2:1
Bit 3 - 未用
Bit 2 - 左键按下
Bit 1 - 中键按下
Bit 0 - 右键按下

Byte 1:

Bits 7-2 - 未用
Bits 1-0 - 解析度

Byte 2:
Bits 7-0 - 速度

0xea - 1*W - Mouse
设置鼠标的stream 模式

0xeb - 3*R - Mouse
轮询数据. 返回的含义如下:

Byte 0:

Bits 7-3 - 移动 [unknown]
Bit 2 - 中键按下
Bit 1 - 右键按下
Bit 0 - 左键按下

Byte 1:

Bits 7-0 - 移动 [unknown]

Byte 2:
Bits 7-0 - 移动 [unknown]

0xec - n/a - Mouse
取消 Echo . 停止环回echo 测试

0xed - 1*W - Keyboard
设置LEDs. Bit 0 是 ScrollLock, bit 1 是 NumLock, bit 是 is CapsLock. 其他位应该是0.

0xee - 1*R - Keyboard
Echo. 返回0xee 没有 ACK.

0xee - n/a - Mouse
进入echo 环回测试. 所有字符被环回,直到接到命令 0xec .

0xf0 - 1*W [+1*R] - PS/2 Keyboard
得到/设置 扫描码集. 0 - 得到扫描码集, 1-3 设置扫描码集1-3.
[1=PC, 2=AT, 3=PS/2]

0xf0 - n/a - Mouse
设置 remote 模式.

0xf2 - 2*R - PS/2 Keyboard / Mouse
得到ID. 读到 0x83 and 0xab 代表键盘. 从mouse读到0x00 ,可能意味着ID以外的其他东西.

0xf3 - 1*W - Keyboard
设置 typematic 速率/延迟. Bits 0-4 是速率, bits 5-6 是延迟, bit 7 应该是0.

0xf3 - 1*W - Mouse
设置鼠标采样速率. 容许值有 10,20, 40, 60, 80, 100 and 200.

0xf4 - n/a - Keyboard / Mouse
使能设备. 设备开始产生事件.

0xf5 - n/a - Keyboard
复位并禁止键盘. 键盘复位到上电状态,但是不影响 LEDs, 停止产生事件.

0xf5 - n/a - Mouse
禁止鼠标. 鼠标停止产生事件.

0xf6 - n/a - Keyboard
复位键盘. 复位并禁止键盘. 键盘复位到上电状态,但是不影响 LEDs, 继续产生事件.

0xf6 - n/a - Mouse
复位并禁止鼠标.鼠标复位到上电状态, 停止产生事件.


0xf7 - n/a - PS/2 keyboard
所有键只上报press 和repeat 事件. 扫描码集 3 有效.

0xf8 - n/a - PS/2 keyboard
所有键只上报press 和release 事件. 扫描码集 3 有效.

0xf9 - n/a - PS/2 keyboard
所有键只上报press 事件. 扫描码集 3 有效.

0xfa - n/a - PS/2 keyboard
所有键上报press ,release ,repeat 事件. 扫描码集 3 有效. 默认行为.

0xfb - 1*W - PS/2 keyboard
设置一个单个的键为 press & repeat. 这个键的扫描码在命令执行之后送出.

0xfc - 1*W - PS/2 keyboard
设置一个单个的键为 press & release. 这个键的扫描码在命令执行之后送出.

0xfd - 1*W - PS/2 keyboard
设置一个单个的键为 press. 这个键的扫描码在命令执行之后送出.

0xfe - n*R - Keyboard / Mouse
重发最后字节, 如果奇偶校验出错,这就很有用. 无ACK. 鼠标可能送出多余一个字节的数据.键盘也有可能.比如设备最后一次发送的是多字节序列. 最好不要使用这个.

0xff - 1*R - Keyboard / Mouse
复位, 进行 Basic Assurance Test (BAT). 设备返回 BAT 完成代码0xaa. 如果是键盘,切换到扫描码集2.上电时也进行BAT,所以,设备插上后马上送出 0xaa. 注意: 键盘回应 ACK ,所以必须把数据和时钟pin设置成high状态, 并且至少500 秒! (设置方法参考i8042----端口). 之后, BAT 开始. 成功时返回 aah,失败返回fch . 有资料说0xfe ff, f4,f5,f6不存在!?



键盘和鼠标的响应


0x00
键侦测错误,或者缓冲区过载(overrun) (for set 1).

0xaa
BAT successful completion code. See comand 0xff.

0xee
Echo 响应. 见命令 0xee.

0xf0
Release prefix for scancode sets 2 and 3.

0xfa
ACK - 应答. 键盘在接受到一个命令或数据字节后应答这个值,echo 和 resend 命令除外.

0xfc
BAT 失败. 见命令0xff.

0xf3
重发. 键盘接受的最后数据失败, 要求重发.

0xff
键侦测失败或缓冲区过载( overrun)( sets 2 and 3).

感谢 Vojtech Pavlik

编辑者: hyl (09/25/02 10:31)

文章选项:

hyl
(enthusiast)
09/24/02 18:28
演示代码 [re: hyl]  


读键盘输入


一般, 8042 接到一个合法的扫描码后, 会把它转换成等价的set 1扫描码.转换后的扫描码放到输入缓冲, 并且IBF(Input Buffer Full,read 0x64) 标志位置1, IRQ 1 请求送出. 此外, 一旦接收到从键盘来的字节, 8042就会禁止以后的数据传输(把Clock置低电位?), 直到输入缓冲为空.如果容许的话,IRQ 1 一般会激活键盘驱动.驱动从io地址0x60 读入扫描码, 这是8042就会撤销IRQ 1 请求, 复位IBF标志. 没有中断的情况下就要轮询键盘进行输入处理了. 这种情况下应该禁止8042 IBF 中断,并轮询IBF flag.输入缓冲区有数据时IBF位变1,数据从输入缓存中读出后自动清0. 读0x60就是读输入缓冲,IBF位在io地址空间00x64, bit 1.


kbRead:
WaitLoop:
in al, 64h ; Read Status byte
and al, 10b ; Test IBF flag (Status<1>)
jz WaitLoop ; Wait for IBF = 1
in al, 60h ; Read input buffer




写键盘

当向 8042 的输出缓存写数据的时候(io 地址 0x60), 控制器把OBF ("Output Buffer Full") 置1,并开始处理. 8042 会把数据发送到键盘,并等待一个应答. 如果键盘在一段时间内没有响应, 状态寄存器的超时位置位. 如果奇偶校验错误,8042 向键盘发送 "Resend" (0xFE) 命令. 此时若键盘仍然不能正确应答, "Parity Error" 位置位. 如果没有错误发生, 应答字节会被放到输入缓存, IBF ("Input Buffer Full") 置位.开始IRQ 1 请求.
(记住, 写输出缓冲后,要用中断或者轮询io地址64h,得到键盘的响应.)


kbWrite:
WaitLoop: in al, 64h ; Read Status byte
and al, 01b ; Test OBF flag (Status<0>)
jnz WaitLoop ; Wait for OBF = 0
out 60h, cl ; Write data to output buffer


点亮三个键盘 LEDs:
每个发送到键盘的字节都得到一个回应:0xFA ("acknowledge") . "Resend" and "Echo" 命令例外.host应该在发送下一个字节到键盘之前等待一个ack(0xFA).
call kbd
mov al,0EDh ; 8048 command byte to set LEDs
out 60h,al
call kbd
; b0 is ScrollLock, b1 is NumLock, b2 is CapsLock ( 见 i8042----键盘命令, 同时也是一个补充)
mov al,07h
out 60h,al

...

kbd0: jmp short $+2
in al,60h
kbd: jmp short $+2
in al,64h
test al,1
jnz kbd0
test al,2
jnz kbd
ret

复位 PC:
; set the POST reset word at 0040h:0072h
; 0000h for cold boot, 1234h to bypass memory test (warm boot)

mov ax,40h
mov es,ax
mov ax,0
mov [es:72h],ax

送给键盘控制器的命令写到io地址0x64. 命令参数在命令发送之后写到io地址0x60 . 命令的结果返回到io地址0x60. 在向8042写命令和参数之前,总应该检测OBF("Output Buffer Full").

; bit b0 of the 8042 'Output Port' drives the CPU reset line
; pulse it low to reset the system

call kbd ; kbd routine above
mov al,0FEh ; 8042 command byte to pulse Output Port pin ( 见 i8042-----控制命令)
out 64h,al


有关总结

1. 无论向0x60,还是0x64写东西前都要等待 状态寄存器 OBF变0.
2. 注意区分 i8042-----控制命令(write 0x64), i8042----键盘命令(write 0x60), 和其中的相似命令.
3.每个发送到键盘的字节都得到一个回应:0xFA ("acknowledge") . "Resend" and "Echo" 命令例外.host应该在发送下一个字节到键盘之前等待一个ack(0xFA).
4. 个人认为, 读0x60前要等待 状态寄存器 IBF变1.

编辑者: hyl (09/24/02 18:59)

文章选项:

hyl
(enthusiast)
09/26/02 12:02
奇异的键盘io---bug备忘录之一 [re: hyl]  

这个bug 我有帖子: 键盘 io 的疑惑 为证.

把0.0.5.1.test 也放到了 版本0.0.5.1.test 上.


Bug 3:(2002.9.25 22:46)

前几天写完了vga的光标支持. 开始搞一下keyboared.

写键盘leds控制,......... 到了键盘reset( 键盘命令ff, 或者控制器命令aa). vmWare 开始报triple fault 错误!
开始漫长无助的探索!
考虑是不是我得键盘不支持复位? 但是为什么! 复位都不支持, 还两种都不支持. 考虑如果键盘的缓冲区没有数据我去读会不会产生这个毛病? 考虑我忽略键盘的应答去发命令会不会产生这个毛病? 考虑键盘正在处理上一个命令, 就发命令会不会死机?
..............
无功而返!!~!!!!!!!!!
今天 换了一台计算机 , 换了gcc(2.8.1 ---> 3.1), 换了vmWare. 使用以前的0.0.5.1...... 竟然可以了!!!!!!!!!
是谁的错?
......................
回家一试.........果然成!
..............
想不通. 就反复试几个不同的方式, 发现只要main.c 中调用kb_reset 就玩完......
.............
发现键盘的灯亮了一个就玩完.......... (忘了怎么该了).
反汇编........ 分析..............
无功............
后来主意到同是打开键盘灯的程序其实都试验成功过. 但是往里边加上几句话 就不行了. 换成试验通过的那个kbdledon 也不行.
为什么灯亮了一个就完蛋?因为他发给键盘命令后向下执行......?!.... 为什么加上几句话就不行?
triple fault!?............
我这么试了试: 保留失败的kbdledon , 注释掉kb_reset..........因为一调用kb_reset 就死.
............成功了!................
想了想....... 是不是太大了?我得核心是不是太大?
............哈哈哈哈哈哈哈哈哈哈哈哈哈!
有可能. fsector 中有加载os的代码, 是有长度限制的. 一看才12k!
setup.s 中更小. 0x1500 * 4... 6k.
继续像这个方面测试. 果然如此.
代码少的时候, kb_reset 也可以工作, 两种方式都可以!
改掉fsector 和setup.s 中的代码. 打开所有的代码, 增大os. .......... 一切都没有问题?
.....................
triple fault ........有何感想???????



声明

希望不要打扰了大家, 解决了这个问题, 大家可能觉得没有什么,但是调试的过程充满了失望.乃至绝望. 成功的兴奋也就大些.

当初在另一台机器上成功了,仅仅因为0.0.5.1 编译以后小于6k! 让人难忘!

编辑者: hyl (09/26/02 13:07)

文章选项:

hyl
(enthusiast)
09/26/02 21:23
附加档案
0.0.5.2.fixbug3(temp ver) [re: hyl]  

修复bug 3后的版本 0.0.5.2.fixbug3

What's new

8. 加入vga的光标支持, kbd 的简单演示. 遇到了终生难忘的bug3.
7. 改进makefile , 建立两个目录 长文件名会引起no rule make ... 的错误.

编辑者: hyl (09/27/02 13:59)

文章选项:

hyl
(enthusiast)
09/26/02 22:25
键盘开发体会 [re: hyl]  



1. 如果数据没有准备好就读0x60口, 读到的结果是上一次的输出值.

2. 主意区分键盘命令和控制器命令.

3. 不知道键盘没有空闲时就发下一个命令会怎么样. 但是应该避免, 恐怕得不到正确结果.

4. 主意下面代码中的 if(da&2) //输出缓冲区满 (host to kbd) 和 if(!(da&1)) .

void kb_wait()
{
unsigned char da;
wait:
da =inb(0x64); // read , 8042 status
if(da&2) //输出缓冲区满 (host to kbd)
goto wait;
return;
// 当然是if(da&2)正确,但是重启,改成1, 一个led亮后重启
}


unsigned char read_kb()
{
//dead loop (because origin is if(da&1) )
unsigned char data,da;
wait:
da =inb(0x64); // read , 8042 status
if(!(da&1))
goto wait; //没有数据,等待 (kbd to host)


data = inb_p(0x60);

return data;

}




文章选项:

hyl
(enthusiast)
09/27/02 22:45
内存布局&0.0.5.2预览 [re: hyl]  


在head.s 中定义了x86机制下的内核分段情况.
但是,像linux 一样. 不准备使用段机制.
所以应该是这个样子:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;全局描述符表
;像linux 一样,我们不准备使用x86的分段机制
;我们用4G的描述符绕过这个机制, 绝大多数cpu
;只支持分页就是我们最好的理由
; LIMITL DW 0 ;段界限低16位
; BASEL DW 0 ;基地址低16位
; BASEM DB 0 ;基地址中间8位
; ATTRIB DB 0 ;段属性 P DPL(2) S TYPE(4)
; LIMITH DB 0 ;G D/B 0 AVL ,段界限的高4位16-19bits
; BASEH DB 0 ;基地址的高8位

_gdt: dw 0,0,0,0 ; CPU 不使用第一个段描述符
;见IA-32 Volume 3, 3.5.1. Segment Descriptor Tables

;kernel code
dw 0xFFFF ; 4G - limit=2047 (2048*4096=8Mb)
dw 0x0000 ; base address=0
db 0x00 ; base address=0
db 0x9A ; code read//exec || 1 00(dpl)//1(code/data) 1010
db 0xCF ; 1100,粒度4k,32bit默认操作数,1111(4G),
db 0x00 ; base

;kernel data
dw 0xFFFF ; 4G - limit=2047 (2048*4096=8Mb)
dw 0x0000 ; base address=0
db 0x00 ; base address=0
db 0x92 ; data read/write || 1 00(dpl)/1(code/data) 1010
db 0xCF ; 1100,粒度4k,32bit默认操作数,1111(4G),
db 0x00 ; base


但是, 0.0.5.2.fixbug3(temp ver) 以前的代码没有定义成这个样子, 唯一的区别是: 段界限只有8M.
在上面的定义中访问位的状态是0, 未访问.


等移植一个轮询方式工作的键盘驱动demo, 然后发布0.0.5.2. 这个版本将:
1. 定义上面所述的段表
2. 加入一个轮询模式的键盘驱动程序,虽然有些.

文章选项:

hyl
(enthusiast)
09/29/02 12:37
截图 [re: hyl]  

0.0.5.2截图



键盘演示





轮询键盘驱动




系统idle

编辑者: hyl (09/29/02 12:39)

文章选项:

hyl
(enthusiast)
09/29/02 13:03
IA-32保护模式 [re: hyl]  

ia32, 感谢!


The IA-32 Intel® Architecture Software Developer’s Manual



对杨季文书中关于指令预取进/出保护模式的一点修正(感谢stone)

drivers

a20的精彩讨论

menuetos OS

资源

编辑者: hyl (09/29/02 13:29)

文章选项:

hyl
(enthusiast)
09/29/02 22:21
附加档案
ver 0.0.5.2 [re: hyl]  



10. 像linux 一样绕过x86的分段机制. 见head.s.

9. 加入一个轮询模式的键盘驱动程序,虽然有些bug.

文章选项:

hyl
(enthusiast)
10/01/02 09:28
论坛中有关Ld脚本的总结 [re: hyl]  

一个关于链接脚本的问题
内核镜像是如果映射到3G去的?
[ jkl ]对.prvious的解释
[ Feiyun ]Linux makefiles (Linux 2.4.9)
如何解读vmlinux.lds.S文件?


Q.freshground (old hand)

我看到一个链接脚本的sections段的描述如下:
SECTIONS
{
. = ALIGN(4); //为什么这儿没有指定具体的地址,他的默认值是什么?
.text : { *(.text) }

. = ALIGN(4);
.rodata : { *(.rodata) }

. = ALIGN(4); //数据段也没有指定地址,如果我的程序是在ROM里运行的话,会有什么问题吗?
.data : { *(.data) }

. = ALIGN(4);
.got : { *(.got) }
}

而在内核的链接脚本里都指定了地址。

A: xiongjs (member)


.默认从0开始,随着输出段的大小递增.



内核镜像是如果映射到3G去的?

arch/i386/vmlinux.lds

在i386/kernel/head.S中
很快就使用__PAGE_OFFSET了,可是文件头的stext是在什么地方变成3G+1M的?


在page_offset.h中定义了PAGE_OFFSET_RAW,在vmlinux.lds.S中.=PAGE_OFFSET_RAW+0x100000,这个位置就是stext的位置。然后自然在head.S中对此有映射了。

你看的不是x86的吧?
再说vmlinux.lds 中是_text标志,还是和stext没关系呀?



你从2.3.2 Provisional Kernel Page Tables开始看。它讲了如何从实地址模式转换到虚地址模式,其中就讲了如何吧内核映射到3G以上的空间的。


好好看看ld -e的说明你可能知道为什么stext就是_text。呵呵。




还有啊,为什么vmlinux.lds中很多个标致都使用"."?这不就是3G+1M吗,那他们不是都在一样的映射地方了?
. = 0xC0000000 + 0x100000;
_text = .; /* Text and read-only data */
.text : {
*(.text)
*(.fixup)
*(.gnu.warning)
} = 0x9090
.text.lock : { *(.text.lock) } /* out-of-line lock text */

_etext = .; /* End of text section */
text段的开头和结尾都一样?



it's all about binutils and elf;

1. >ld -e symbol
set value of symbol to elf header->entry

"entry" is said to be start of the program; and here, it is the start point of kernel. after bootloader loads kernel image, it jumps to "entry", which is "startup" code refered below;

2. >how kernel maps to 3G?
binutils says "the image should be mapped to 3G" as vmlinux.ld.S indicated , and records this information in ELF header; and "startup" code in kernel sets up the page-table and enter virtual(protect) mode to make sure kernel is really mapped to virutal address, which is from ELF header either.



真是高人!有些明白了
不过还有一个问题想问一下:
ld会把head.S的标志地址都映射到3G+1M空间去,那么
ld -e stext 告诉了机器的是真实物理地址呢?还是虚拟地址?
也就是说这个stext是经过映射的了,还是在映射之前的呢?



其实你根本没必要去管虚拟地址或者真实地址。IP(x86中)是一个一个递增的,但为什么我们要制定地址呢? 那就是因为程序中存在ljmp等跳转指令必须为他们制定确切的地址。
这里当然是虚拟地址,因为在运行这些的时候,已经切换到虚模式啦


既然碰见了专家,就要问明白 :-)
1 在vmlinux.lds中为什么有.text=. 和_etext=.?那不是都等于3G+1M了吗?
2 System.map所显示的内容是就是vmlinux的解压缩内核内容吧?




symbol "." stands for "current position"; except setting it explicitly, it increases automaticly according to storage allocated; so _etext = _stext + ALIGN(code size of .text + .text.lock).




[ jkl ]对.prvious的解释 回复

张贴者: lucian_yao (journeyman)
张贴日期 03/24/01 21:14


.previous伪指令恢复当前段的前一个段作为当前段,由于ELF中允许用.section自定义段,这里的.previous作用就是恢复.text作为当前段。
或许应该说恢复到当前.section定义之前的段作为当前段。




#define RESTORE_ALL /
popl 離; /
popl 靫; /
popl 韝; /
popl % esi; /
popl 韎; /
popl 雙; /
popl 陎; /
1: popl %ds; /
2: popl % es; /
addl $4,% esp; /
3: iret; /
.section .fixup,"ax"; /
4: movl $0,(% esp); /
jmp 1b; /
5: movl $0,(% esp); /
jmp 2b; /
6: pushl %ss; /
popl %ds; /
pushl %ss; /
popl %es; /
pushl $11; /
call do_exit; /
.previous; /
.section __ex_table,"a";/
.align 4; /
.long 1b,4b; /
.long 2b,5b; /
.long 3b,6b; /
.previous
这是asm/entry.S中的一段代码,用到了两个.previous指示符,它的意思是不是把
.section .fixup,"ax";.section __ex_table,"a";汇编到各自定义的段中,然后
跳回去,将这之后的的代码汇编到.text段中,也就是自定义段之前的段?是不是这个
意思,请指教。




对,.section与.previous还必须配对使用。



The .previous directive forces the assembler to insert the code that follows into section that was active when the last .section directive was encountered. .section directive allows programmers to specify which section of the executable file contains the code that follows.


<< Understanding Linux Kernel >>P245


.section .fixup,"ax";
.section __ex_table,"a";

請問這2句中的"ax","a"表示身麼?




'a' section是可分配的
'x' section是可执行的

更多内容参看info as



///
//
// makefile.txt
// Copyright(C) 2001, Feiyun Wang
//
// analysis on Linux makefiles (Linux 2.4.9)
//

//
// linux/Makefile
include arch/i386/Makefile
depend: generate linux/.depend
config: generate linux/.config
vmlinux: generate linux/vmlinux
// entry point ".text" in linux/arch/i386/kernel/head.S
$(LD) -T $(TOPDIR)/arch/i386/vmlinux.lds -e .text
+ arch/i386 (from linux/arch/i386/Makefile)
kernel/head.o
kernel/init_task.o
init/main.o
init/version.o
+ arch/i386 (from linux/arch/i386/Makefile)
kernel/kernel.o
mm/mm.o
kernel/kernel.o
mm/mm.o
fs/fs.o
ipc/ipc.o
drivers/...
char/char.o
block/block.o
misc/misc.o
net/net.o
media/media.o
cdrom/driver.o
+ arch/i386 (from linux/arch/i386/Makefile)
math-emu/math.o (ifdef CONFIG_MATH_EMULATION)
net/network.o
lib/lib.a
+ arch/i386 (from linux/arch/i386/Makefile)
lib/lib.a
System.map: $(NM) vmlinux | ... > System.map

//
// linux/arch/i386/vmlinux.lds
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start) /* overridden by command line option -e .text */
.text = 0xC0000000 + 0x100000;
.text /* unallocated holes filled with 0x9090, NOP */
.text.lock
_.text ;
.rodata
.kstrtab

ALIGN(16)
__start___ex_table ;
__ex_table
__stop___ex_table = __start___e_table + SIZEOF(__ex_table);

__start___ksymtab ;
__ksymtab
__stop___ksymtab = __start___ksymtab + SIZEOF(__ksymtab);

.data
_edata ;
ALIGN(8192)
.data.init_task
ALIGN(4096)
__init_begin ;
.text.init
.data.init

ALIGN(16)
__setup_start ;
.setup.init
__setup_end = __setup_start + SIZEOF(.setup.init);

__initcall_start ;
.initcall.init
__initcall_end = __initcall_start + SIZEOF(.initcall.init);
ALIGN(4096)
__init_end;

ALIGN(4096)
.data.page_aligned : { *(.data.idt) }
ALIGN(32)
.data.cacheline_aligned

__bss_start ;
.bss
_end = __bss_start + SIZEOF(.bss);

/DISCARD/ { *(.text.exit) *(.data.exit) *(.exitcall.exit) }
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }

//
// linux/arch/i386/Makefile
The right side are targets in linux/arch/i386/boot/Makefile
zImage compressed: zImage
zlilo: zlilo with BOOTIMAGE=zImage
zdisk: zdisk with BOOTIMAGE=zImage
bzImage: bzImage
bzlilo: zlilo with BOOTIMAGE=bzImage
bzdisk: zdisk with BOOTIMAGE=bzImage
install: install with BOOTIMAGE=bzImage

//
// linux/arch/i386/boot/Makefile
compressed/vmlinux & compressed/bvmlinux are targets in
linux/arch/i386/boot/compressed/Makefile
zImage:
$(OBJCOPY) compressed/vmlinux compressed/vmlinux.out
tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage
bzImage:
$(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out
tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) >bzImage
zlilo: cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz; lilo
zdisk: dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0
install: sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) /
$(TOPDIR)/System.map "$(INSTALL_PATH)"

bootsect: $(LD) -.text 0x0 -s --oformat binary bootsect.o
setup: $(LD) -.text 0x0 -s --oformat binary -e be.text setup.o
bbootsect: $(LD) -.text 0x0 -s --oformat binary bbootsect.o
bsetup: $(LD) -.text 0x0 -s --oformat binary -e be.text bsetup.o

tool/build.c builds a disk-image from three different files:
- bootsect: exactly 512 bytes, load the rest, i386/boot/bootsect.S
- setup: set up system parameters, i386/boot/setup.S
- system: 80386 code for actual system, i386/boot/compressed/(b)vmlinux

//
// linux/arch/i386/boot/compressed/Makefile
piggy.o: contains int input_len, pointer input_data & zipped linux/vmlinux
vmlinux: $(LD) -.text 0x1000 -e startup_32 -o vmlinux head.o misc.opiggy.o
bvmlinux: $(LD) -.text 0x100000 -e startup_32 -o bvmlinux head.o misc.opiggy.o

//
// Linux bzImage
The last step of "make bzImage":
linux/arch/i386/tools/build -b bbootsect bsetup bvmlinux $(ROOT_DEV) >bzImage
-b option means is_big_kernel

It will output the following to stdout (redirected to bzImage) one by one:
bbootsect
from linux/arch/i386/boot/bootsect.S with -D__BIG_KERNEL__
512 bytes
bsetup
from linux/arch/i386/boot/setup.S with -D__BIG_KERNEL__
4 sectors (padded) or more, sector aligned
bvmlinux
- head.o (from linux/arch/i386/boot/compressed/head.S)
- misc.o (from linux/arch/i386/boot/compressed/misc.c)
- zipped linux/vmlinux

It will change some of the contents from input:



Offset Size Content

1F1(497) Byte setup_sectors (>=4)

1F4(500) Word bvmlinux in paragraph (16
bytes)

1FC(508) Byte root dev minor

1FD(509) Byte root dev major



We can use the following command to create a bootable floppy:
dd bs=8192 if=bzImage of=/dev/fd0



如何解读vmlinux.lds.S文件


vmlinux.lds.S用于对ld的输出进行组版,这个文件的格式在ld.info手册中有详细的说明。
vmlinux.lds.S的主要目的是对输出文件中段进行排序,并定义相关的符号名,
以下是简要注释。



/* ld script to make i386 Linux kernel
* Written by Martin Mares ;
*/

OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386) /* 输出格式 */
ENTRY(_start) /* 定义_start作为入口点 */


SECTIONS{

. = PAGE_OFFSET_RAW + 0x100000; /* 定义当前段的偏移量(.代表当前计数器) */

_text = .; /* 定义符号_text为当前位置 */

.text : { /* 定义段.text (": {"是段定义符)*/
*(.text) /* 将所有输入文件中.text段合并到这里 */
*(.fixup) /* 将所有输入文件中的.fixup段合并到这里 */
*(.gnu.warning) /* 将所有输入文件中的.gnu.warning段合并到这里 */
} = 0x9090 /* 合并中的空隙用0x9090填充 */

/* 以下的语法含义可以类推 */
.text.lock : { *(.text.lock) } /* out-of-line lock text */
.rodata : { *(.rodata) }
.kstrtab : { *(.kstrtab) }

. = ALIGN(16); /* Exception table */

__start___ex_table = .; /* 定义__start_ex_table符号为当前位置 */
__ex_table : { *(__ex_table) }

__stop___ex_table = .;
__start___ksymtab = .; /* Kernel symbol table */
__ksymtab : { *(__ksymtab) }
__stop___ksymtab = .;

_etext = .; /* End of text section */

.data : { /* Data */
*(.data) CONSTRUCTORS /* 将C++的构造函数指针段合并到这里 */
}

_edata = .; /* End of data section */

. = ALIGN(8192); /* init_task */
.data.init_task : { *(.data.init_task) }
. = ALIGN(4096); /* Init code and data */
__init_begin = .; .text.init : { *(.text.init) }
.data.init : { *(.data.init) }
. = ALIGN(4096); /* 输出计数器在页边界上对齐 */
__init_end = .;

. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }

__bss_start = .; /* BSS */

.bss : {*(.bss) }
_end = . ;

/* Stabs debugging sections. */
.stab 0 : { *(.stab) } /* 0 是段属性,代表段的起始地址 */
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }


} /*end SECTIONS */



建议用"objdump --headers vmlinux"得到的组版结果.

























文章选项:

hyl
(enthusiast)
10/06/02 10:28
附加档案
0.0.5.3 1:1分页 [re: hyl]  

修改记录:
12. 考虑到保留开启分页的这段经验. 中止0.0.5.3 版. 保留堆栈在8M, 开启分页.
分页的平滑过渡基于0-8M的恒同映射.

11. 终于开启了分页.(2002.10.2 22:08)
一开始总是死机. 不知为何.
一开始在.text 中定义_kpg_dir, 这样以来增大了内核的尺寸. 又没有控制好
_kpg_dir 的起始地址, setup_paging 用_pgt0+7 来设置属性位当然不正确.
我看过了os.o 的符号表, _kpg_dir 起始地址最后三位不是000.(objdump -t os.o)
setup_paging:
mov eax,_pgt0+7 ;set present bit/user r/w
mov [_kpg_dir],eax
然后查nasm的资料, 使用absolute 把页目录和页表定位于.bss 0x200000(2m).
还调整了fsector.s setup.s, 以避免bug3 的重现.
但是还是没有成功. 以但启动分页(cr0分页使能), 就重启.
据经验, 这可能是页表设置不正确. 果然在setup_paging 中查出几个错误.
但是还是不行, 直到查出最后一个bug. 下面的cmp eax,0x800007, 原来是
cmp eax,0x7ff007, 移植自linux. 但是据这段程序, 第二个页表的最后一项没有设置.
正好还用到了, 因为原来把系统堆栈设置在8M.
(见head.s: mov esp,0x800000)(如果本版看不出, 见0.0.5.2)
;设置两个页表, 恒同映射
mov edi,_pgt0 ;+4092
mov eax,0x007 ;/* 8Mb - 4096 + 7 (r/w user,p) */
setup_paging1:
mov [edi],eax
add eax,0x1000 ;页面号加1(物理页面号)
add edi,4
cmp eax,0x800007
jl setup_paging1
后来把内核栈该到.bss中, 证实了这个错误.

文章选项:

hyl
(enthusiast)
10/09/02 22:58
附加档案
0.0.5.4 ----3G+1M 处的内核 [re: hyl]  

我的系统也3g


17. 0.5.4 到此为止吧. 虽然改动不多, 费力却不少.

16. 为了调试3g处的内核, 使用die:jmp die 这种原始武器, 发现分页以后如果
重新加载内核gdt 就会死机, 并且是把选择子 装入ds 拿那一刻.

我用section .data 把idt, gdt 放到数据段, 发现字符串输出都不正常了!
是一堆乱码!

努力总会感动上帝!
2002.10.5 8:53.
突然意识到, 代码在物理内存1M的地方, 1:1 映射!
不是-T 0xc0000000 , 而是-T 0xc0100000.
这样, 什么问题都没有了.
顺便把flag 清除到了全零状态.
现在, 把Makefile 写成-T 0xc0000000 或-T 0x100000 都可以正确工作.

section .data 也没有问题了.
mov esp,0xc0800000; 堆栈建在3g+8M 处.
vga.c : 驱动改成3g ok. 显存在0xc00b8000.
生成kerlsyb.txt kerllst.txt .
主意: 由于页表都使用了绝对定位, 所以引用时无须把地址减去3G.


15. 2002.10.4 试图仿照linux 把内核移到3g去运行, 但是, 始终不成功.
把setup_paging 函数去掉了省去了调用这一步.(因为-T
3g, 调用时必须用类似call setup_paging-3g 这样的语句.
我没有
试不知行不行! )
注:
调试时为了看3g处的页表有没有设置好使用:
mov edx, 0x310a330a
mov eax, 0xc00b8000
mov [eax],edx
测试0xc00b8000 地址是不是显存,以此确信页表正常.

文章选项:

guoyichao
(addict)
10/16/02 17:49
在cygwin下演示 [re: hyl]  

把Makefile改一下就可以在cygwin下编译通过(本来想弄个patch的,没备份原先的Makefile,就?br>懔耍?


CCFLAGS = -c -Wall -fno-builtin -nostdinc -fwritable-strings -mno-cygwin
ASFLAGS = -f win32

bootimg : fsector.bin setup.bin os.bin
cat -B $^ > $@
./genimg.sh $@


os.o: $(KERNEL_OBJS)
strip -N ___main -N __alloca main.o
$(LD) $(KERNEL_OBJS) -e Start_OS -Ttext 0xc0100000 -o $@


genimg.sh

$ cat genimg.sh
#!/bin/bash

OBS=`wc -c $1|awk '{print /$1}'`
IBS=`expr 1474560 - $OBS`
dd of=$@ if=/dev/zero seek=1 count=1 obs=$OBS ibs=$IBS

------------


编译后在vmware下运行,还算正常,不过和截图不一样
也没仔细研究,也许cygwin下编译还需要做更多工作。

编辑者: guoyichao (10/16/02 18:20)

文章选项:

dengyl
(journeyman)
11/01/02 15:29
附加档案
在linux下的编译 0.0.5.4 vmware [re: guoyichao]  

我在linux下编译时改的Makefile,还有运行结果。
原来Makefile 里的pad我认为没有太大用处,rm 掉了。
文件没附上来,就粘贴一下吧!:)
其实Makefile就改了跟这些有关的地方:
del , pad, copy /b, partcopy

还有kernel/head.s里的_main要改成kbd_main,不知道作者为什么要这样写?
不, 就是_main --hyl

##########################################
# #
# 这个 Makefile 负责编译安装到软盘的 OS #
# #
##########################################
CC = gcc
AS = nasm
LD = ld
RM = rm -f

#-fwritable-strings option place string in .data, not in .text
CCFLAGS = -c -Wall -fno-builtin -nostdinc -fwritable-strings
ASFLAGS = -f aout

KERNEL_OBJS = head.o main.o vga.o kbd.o kbdriver.o i386.o
# 因为我们的映象是binary格式.
# 所以入口点函数应该是第一个.o文件的第一个函数


TOPDIR = ./

# 最终生成文件 bootimg
bootimg : fsector.bin setup.bin os.bin
cat fsector.bin setup.bin os.bin > bootimg


# Boot 是引导扇区内容,内核 os.bin 紧跟其后的扇区



# 将生成的操作系统文件从 elf 格式转换到 binary 格式
os.bin: os.o
objcopy -R .comment -R .note -S -O binary $< $@
objdump --line-numbers --source os.o >krnllst.txt
nm --line-numbers os.o | sort >krnlsym.txt
objdump -D os.o>kerasm.txt


os.o: $(KERNEL_OBJS)
$(LD) $(KERNEL_OBJS) -e Start_OS -Ttext 0xc0100000 -o $@

#$(LD) $(KERNEL_OBJS) -e Start_OS -Ttext 0x100000 -o $@
#$(LD) $(KERNEL_OBJS) -e Start_OS -Ttext 0xc0100000 -o $@


# 因为我们的映象是binary格式.
# 所以入口点函数应该是第一个.o文件的第一个函数


#使用自动规则
# %bin:$(dir)%.s
#等等
include ./kernel/kernel.mak
include ./boot/boot.mak

clean:
$(RM) *.bin
$(RM) *.o
$(RM) bootimg
$(RM) krnllst.txt
$(RM) krnlsym.txt
$(RM) kerasm.txt
$(RM) *.bak

install:
#partcopy bootimg 0 168000 -f0
dd if=bootimg of=/dev/fd0 bs=1k seek=0

编辑者: hyl (12/23/02 10:13)

文章选项:

yansm
(newbie)
12/20/02 17:16
Re: 在cygwin下演示 [re: guoyichao]  

怎么放到vmware上跑呢?


--------------------
共同进步

文章选项:

hyl
(enthusiast)
12/30/02 12:58
grub 教程 [re: hyl]  

grub 教程

文章选项:

chong2
(newbie)
01/10/03 15:34
Re: OSExperiment [re: hyl]  

想不到我的myos也被引用了,呵呵。我的主页被没收了,新主页搬到了成电学生提供的个人主页空间,地址是
http://os.my.freedim.net/
欢迎访问!:)
谢谢

文章选项:

Nemesis2k
(stranger)
03/04/03 16:28
Re: OSExperiment [re: chong2]  

我比较希望能有一个 Windows 2000 Clone 出来。。。

文章选项:

solaryi
(member)
03/04/03 23:09
Re: OSExperiment [re: Nemesis2k]  

have a look at ReactOS

文章选项:

hyl
(addict)
03/27/03 17:45
Use VGA without bios [re: hyl]  

vga without bios

编辑者: hyl (03/29/03 15:17)

文章选项:

hyl
(addict)
04/01/03 16:17
附加档案
写image工具---rawrite [re: hyl]  

rawrite -f bootimg -d a:

文章选项:

menp99
(stranger)
04/11/03 11:48
Re: OSExperiment--项目开通 [re: hyl]  

能不能把这个项目详细介绍一下呀,看得我一头雾水.

文章选项:

well
(enthusiast)
04/14/03 11:15
Re: OSExperiment--项目开通 [re: hyl]  

一位网友高中阶段自己开发了一个OS,完全可以独立运行,
可惜他的网站现在进不去,大家可以 查查 running 操作系统

文章选项:

softmaster
(newbie)
05/13/03 20:43
Re: ExpOS----支持AT&T的si脚本 [re: hyl]  

很多贴子都没了,能给出详细的编译和调试说明吗?


--------------------
我是一个文学青年,我的第一个作品叫做 hello,world !

文章选项:

hyl
(addict)
05/14/03 10:04
OS----编译调试指南 [re: hyl]  

开发环境

1. windows2000
2. vmWare
3. 在vmware中安装linux, 选上nasm, gcc,内核开发等组件(如redhat 9.0)
4. 如果使用vmware 4.0 可以使用共享文件夹功能, 把代码传入vmware中的linux进行编译, 在win下可以使用si编辑
5.make dep , make 之后会将bootimg.flp 拷贝到 /mnt/hgfs/osdev目录(vmware 共享文件夹目录, 即win下的某个目录)
6. 将bootimg.flp作为另一个虚拟机的软盘img 即可启动our OS
7 .同时生成vpc.img 可以启动vitural PC
8. 在linux 中make install 可以将软盘img写入真正的软盘, 可以用来启动真正的pc





其他常用工具

这些工具ExpOS编译工具包都有
NASM:
nasm
DJGPP:
精简的djgpp
djgpp完全版
djgpp home
PARTCOPY:
execpc.com/~geezer/johnfine/#zero
execpc.com
操作系统开发环境( 有两个at <-->intel 汇编的转换工具 )

早期编译环境

Classic env:
1. windows2000
2. vmWare
3. Djgpp
4. nasm
5. ld
6. partcopy
7. SourceInsight
8. TC2.0 (可能要用,增强bootloader的功能)



到sf.linuxfourm.net下载编译工具包, 然后, 解压缩到c:根目录, 改名为djgpp. 下载一个内核版本, 双击build目录的osgo.bat. 在控制台打make就可以编译.
编译完成后build目录生成一个 flp文件, 把vmware的a驱设置为这个文件, 就可以使用vmware启动.


ExpOS编译工具包

编辑者: hyl (10/21/04 10:27)

文章选项:

hyl
(addict)
05/15/03 09:37
国内相关项目 [re: hyl]  

Pagoda ooos

Osdiy

IA-32保护模式

操作系统开发杂志

ChinaOS

MiniGui

myos

linux 文档收集与整理---joyfire

操作系统研究和实践--CodeDiy

cnix--amcos

Vrix os相关

lingix--汉语支持--sunwen_ling

操作系统资源网

EdenOS使用vc tasm打造

野火操作系统

Jicama OS
jicama os是一个32位运行于intel 386以上的ibm pc及其兼容机的通用操作系统内核,jicama 立志做一个性能出众的GUI,并且能够支持linux下面的应用程序和更多的设备运行为实现目标,该计划目前还在进一步发展之中。

编辑者: hyl (10/28/04 14:28)

文章选项:

larry_li
(stranger)
07/02/03 10:25
implight [re: hyl]  

人工智能操作系统,基于Ada语言开发。
http://gro.clinux.org/projects/implight/

文章选项:

你可能感兴趣的:(系统设计)