Linux内核(0.12)-setup.S分析

函数功能介绍

  1. 利用ROM BIOS中断读取机器系统数据,并将这些数据保存到0x90000开始处(覆盖了bootsect程序所在地方),看下面的 “setup程序读取并保留参数”
  2. 将system模块从0x1000~0x8ffff移动到0x0000~0x7ffff处。
  3. 加载全局段描述符表(GDT)和中断段描述符表(IDT)
  4. 开启A20 地址线
  5. 重新设置两个中断控制芯片8295A,将硬件中断重新设置为0x20~0x2f。
  6. 设置CPU控制寄存器CR0(机器状态字),从而进入32位保护模式运行,跳转到位于system模块最前面的head.s程序。

setup程序读取并保留的参数

什么是GDT

参考:
http://blog.csdn.net/geekcome/article/details/6415261

EGA和VGA显示方式比较

VGA显示方式

VGA(Video Graphics Array)是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。

参考:http://baike.baidu.com/link?url=WzDv5fImHQSVFqgBweRUArs7kg8WNram1EO8uPcvJ-jhK4w5S_tnNXHlTn33ozjkjdp9IIlRu_jAMTXeNzuEOa

EGA显示方式

增强图形适配器(Enhanced Graphics Adapter或EGA)是IBM PC计算机显示标准定义,在显示性能方面(颜色和解析度)介于CGA和VGA之间。它是IBM在1984年为其新型PC-AT计算机引入的技术。EGA可以在高达640x350的分辨率下达到16色。

参考:http://baike.baidu.com/link?url=FLjsDdmQcopU1iEyqjwLzUoBuHqxBdfmf7L1v-Lpq7wuyWyQ3mXCkepnBq-jwzunYpYC0g-0r1mq6TYaSI-o0q

关于临时GDT表的设置问题

关于临时表的问题,说上面讲错了。看下面的注释你也许会理解的更加透彻。

gdt: ! 全局描述符表开始处。描述符表由多个8 字节长的描述符项组成。
! 这里给出了3 个描述符项。第1 项无用(206 行),但须存在。第2 项是系统代码段
! 描述符(208-211 行),第3 项是系统数据段描述符(213-216 行)。每个描述符的具体
! 含义参见列表后说明。
.word 0,0,0,0 ! dummy !1 个描述符,不用。
! 这里在gdt 表中的偏移量为0x08,当加载代码段寄存器(段选择符)时,使用的是这个偏移值。
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
! 这里在gdt 表中的偏移量是0x10,当加载数据段寄存器(如ds 等)时,使用的是这个偏移值。
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386


gdt_48:
    .word   0x800       ! gdt limit=2048, 256 GDT entries
    .word   512+gdt,0x9 ! gdt base = 0X9xxxx

参考:http://www.oldlinux.org/oldlinux/viewthread.php?tid=5903&extra=page%3D20

注释代码

!
!   setup.s     (C) 1991 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
! setup.s 负责从BIOS 中获取系统数据,并将这些数据放到系统内存的适当地方。
! 此时setup.ssystem 已经由bootsect 引导块加载到内存中。
!
! 这段代码询问bios 有关内存/磁盘/其它参数,并将这些参数放到一个
! “安全的”地方:0x90000-0x901FF,也即原来bootsect 代码块曾经在
! 的地方,然后在被缓冲块覆盖掉之前由保护模式的system 读取。
!

! NOTE! These had better be the same as in bootsect.s!
! 以下这些参数最好和bootsect.s 中的相同!
! 下面关于段,变量的定义代码和bootsect.S的代码都是相同的。
#include <linux/config.h>

INITSEG  = DEF_INITSEG  ! we move boot here - out of the way  ! 原来bootsect 所处的段。
SYSSEG   = DEF_SYSSEG   ! system loaded at 0x10000 (65536).   ! system0x10000(64k)处。 
SETUPSEG = DEF_SETUPSEG ! this is the current segment         ! 本程序所在的段地址。

!code段,data段和bss段都定义在一起。
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

! 代码入口处
entry start
start:

! 下句将ds置成INITSEG0x9000)。这已经在bootsect程序中设置过,但是现在是setup程序,
! Linus觉得需要在重新设置一下,因为后面的设置信息都放在ds:[偏移量]位置。
    mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax

! Get memory size (extended mem, kB)
! 下面3 句取扩展内存的大小值(KB)。
! 是调用中断0x15,功能号ah = 0x88
! 返回:ax = 从0x100000(1M)处开始的扩展内存大小(KB)。
! 若出错则CF 置位,ax = 出错码。

    mov ah,#0x88
    int 0x15
    mov [2],ax          ! 将扩展内存数值存在0x90002 处(1 个字)

! check for EGA/VGA and some config parameters
! 检查显示方式(EGA/VGA)并取参数。
! 调用BIOS 中断0x10,附加功能选择 -取方式信息
! 功能号:ah = 0x12,bl = 0x10
! 返回:bh = 显示状态
! (0x00 - 彩色模式,I/O 端口=0x3dX)
! (0x01 - 单色模式,I/O 端口=0x3bX)
! bl = 安装的显示内存
! (0x00 - 64k, 0x01 - 128k, 0x02 - 192k, 0x03 = 256k)
! cx = 显示卡特性参数(参见程序后的说明)。

    mov ah,#0x12
    mov bl,#0x10
    int 0x10
    mov [8],ax          ! 0x90008 = ??
    mov [10],bx         ! 0x9000A = 安装的显示内存,0x9000B = 显示状态(彩色/单色)
    mov [12],cx         ! 0x9000C = 显示卡特性参数。
! 检测屏幕当前行列值。若显示卡是VGA卡时则请求用户选择显示行列值,并保存到0x9000E处。
    mov ax,#0x5019      !ax中预置屏幕默认行列值(ah=80列;al=25行)
    cmp bl,#0x10        ! 若中断返回bl值为0x10,则表示不是VGA显示卡,跳转。
    je  novga
    call    chsvga      ! 检测显示卡厂家和类型,修改显示行列值(这个代码后面有介绍)
novga:  mov [14],ax

! we get current cursor position and save it for posterity.
! 现在将光标位置保存以备今后使用
! 这段代码使用BIOS中断取屏幕当前光标位置(行,列),并保存在内存0x90000处(2字节)。
! 控制台初始化程序会到此处读取该值
! BIOS中断0x10功能号 ah = 0x03,读光标位置
! 输入:bh = 页号
! 返回: ch = 扫描开始线;cl = 扫描结束线;dh = 行号(0x00顶端);dl = 列号(0x00最左边)。
    mov ah,#0x03    ! read cursor pos
    xor bh,bh
    int 0x10        ! save it in known place, con_init fetches
    mov [0],dx      ! it from 0x90000.

! Get video-card data:
! 下面这段用于取显示卡当前显示模式。
! 调用BIOS 中断0x10,功能号 ah = 0x0f
! 返回:ah = 字符列数,al = 显示模式,bh = 当前显示页。
! 0x90004(1 字)存放当前页,0x90006 显示模式,0x90007 字符列数。

    mov ah,#0x0f        
    int 0x10
    mov [4],bx      ! bh = display page
    mov [6],ax      ! al = video mode, ah = window width

! Get hd0 data
! 取第一个硬盘的信息(复制硬盘参数表)。
!1 个硬盘参数表的首地址竟然是中断向量0x41 的向量值!而第2 个硬盘
! 参数表紧接第1 个表的后面,中断向量0x46 的向量值也指向这第2 个硬盘
! 的参数表首址。表的长度是16 个字节(0x10)。
! 下面两段程序分别复制BIOS 有关两个硬盘的参数表,0x90080 处存放第1! 硬盘的表,0x90090 处存放第2 个硬盘的表。

    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]         ! 取中断向量0x41的值,即hd0参数表的地址->ds:si
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0080          ! 传输的目的地址:0x9000:0x0080 -> es:di
    mov cx,#0x10            ! 共传输16字节
    rep
    movsb

! Get hd1 data

    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x46]         ! 取中断向量0x46值,即hd1参数表的地址->ds:si
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0090          ! 传输的目的地址:0x9000:0x0090->es:di
    mov cx,#0x10
    rep
    movsb

! Check that there IS a hd1 :-)
! 检查系统是否存在第2 个硬盘,如果不存在则第2 个表清零。
! 利用BIOS 中断调用0x13 的取盘类型功能。
! 功能号 ah = 0x15;
! 输入:dl = 驱动器号(0x8X 是硬盘:0x80 指第1 个硬盘,0x81 第2 个硬盘)
! 输出:ah = 类型码; 00 --没有这个盘,CF 置位; 01 --是软驱,没有change-line 支持;
!                  02 --是软驱(或其它可移动设备),有change-line 支持; 03 --是硬盘。

    mov ax,#0x01500
    mov dl,#0x81
    int 0x13
    jc  no_disk1
    cmp ah,#3               ! 是硬盘吗?(类型 = 3 ?)
    je  is_disk1
no_disk1:
    mov ax,#INITSEG         !2个硬盘不存在,则对第2个硬盘表清零。
    mov es,ax
    mov di,#0x0090
    mov cx,#0x10
    mov ax,#0x00
    rep
    stosb           !AX中的数据装入ES:DI指向的存储单元,然后根据DF标志增减DI
is_disk1:

! now we want to move to protected mode ...
! 现在我们要进入保护模式中了……

    cli         ! no interrupts allowed !   ! 从此开始不允许中断。

! first we move the system to its rightful place
! 首先我们将system模块移动到正确的位置
! bootsect引导程序会把system模块读入到内存0x10000(64KB)开始的位置。由于当时假设
! system模块最大长度不会超过0x80000(512KB),即其末端不会超过内存地址0x90000,所以
! bootsect会把自己移动到0x90000开始的地方,并把setup加载到他的后面。下面这段程序的用
! 途是再把整个system模块移动到0x00000位置,即把从0x10000到0x8ffff的内存数据块
! (512KB)整块地向内存低端移动了0x10000(64KB)的位置。

    mov ax,#0x0000
    cld         ! 'direction'=0, movs moves forward
do_move:
    mov es,ax       ! destination segment ! es:di = 目的地址(初始为0x0000:0x0)
    add ax,#0x1000
    cmp ax,#0x9000  ! 已经把从0x8000 段开始的64k 代码移动完?
    jz  end_move
    mov ds,ax       ! source segment  ! ds:si = 源地址(初始为0x1000:0x0)
    sub di,di       ! di清零
    sub si,si       ! si清零  
    mov     cx,#0x8000  ! 移动0x8000 字(64k 字节)。
    rep
    movsw
    jmp do_move

! then we load the segment descriptors
! 此后,我们加载段描述符。
! 从这里开始会遇到32 位保护模式的操作,因此需要Intel 32 位保护模式编程方面的知识了,
! 有关这方面的信息请查阅列表后的简单介绍或附录中的详细说明。这里仅作概要说明。
!
! lidt 指令用于加载中断描述符表(idt)寄存器,它的操作数是6 个字节,0-1 字节是描述符表的
! 长度值(字节);2-5 字节是描述符表的32 位线性基地址(首地址)。中断描述符表中的每一个
! 表项(8 字节)指出发生中断时,需要调用的代码的信息,与中断向量有些相似,但要包含更多的信息。
!
! lgdt 指令用于加载全局描述符表(gdt)寄存器,其操作数格式与lidt 指令的相同。全局描述符
! 表中的每个描述符项(8 字节)描述了保护模式下数据和代码段(块)的信息。其中包括段的
! 最大长度限制(16 位)、段的线性基址(32 位)、段的特权级、段是否在内存、读写许可以及
! 其它一些保护模式运行的标志。

end_move:
    mov ax,#SETUPSEG    ! right, forgot this at first. didn't work :-)
    mov ds,ax           ! ds 指向本程序(setup)段。
    lidt    idt_48      ! load idt with 0,0         ! 加载IDT寄存器
    lgdt    gdt_48      ! load gdt with whatever appropriate        ! 加载GDT寄存器

! that was painless, now we enable A20
! 以上的操作很简单,现在我们开启A20 地址线。参见程序列表后有关A20 信号线的说明。
! 为了能够访问和使用1MB以上物理内存,我们需要首先开启A20地址线。参见本程序列表后有关
! A20信号线的说明。关于所涉及的一些端口和命令,可参考kernel/chr_drv/keyblard.S程序
! 后对键盘接口的说明。至于机器是否真正开启了A20地址线,我们还需要在进入保护模式之后(能
! 访问1MB以上内存之后)在测试一下。这个工作放在了head.S程序中。

    call    empty_8042  ! 测试8042状态寄存器,等待输入缓冲器空
                        ! 只有当输入缓冲器为空时,才可以对其执行写命令。
    mov al,#0xD1        ! command write  ! 0xD1命令码-表示要写数据到
    out #0x64,al        ! 8042p2端口。p2端口位1用于A20线的选通。
    call    empty_8042  ! 等待输入缓冲器空,看命令是否被接受。
    mov al,#0xDF        ! A20 on  ! 选通A20地址线的参数。
    out #0x60,al        ! 数据要写到0x60口。
    call    empty_8042  ! 若此时输入缓冲器为空,则表示A20线已经选通

! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won t mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven t been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259 s, and it isn t fun.
! 希望以上一切正常。现在我们必须重新对中断进行编程??
! 我们将它们放在正好处于intel 保留的硬件中断后面,在int 0x20-0x2F! 在那里它们不会引起冲突。不幸的是IBM 在原PC 机中搞糟了,以后也没有纠正过来。
! PC 机的bios 将中断放在了0x08-0x0f,这些中断也被用于内部硬件中断。
! 所以我们就必须重新对8259 中断控制器进行编程,这一点都没劲。

! 在程序中的".word 0x00eb,0x00eb",为直接近跳转指令的操作码,第1个字节的相对偏移指令。
! 也就是说,"0x00eb" = "jmp 代码所在指令+2"

    mov al,#0x11        ! initialization sequence
    out #0x20,al        ! send it to 8259A-1    ! 发送到8259A主芯片
! 两条跳转指令,跳到下一条指令,起延时作用。
    .word   0x00eb,0x00eb       ! jmp $+2, jmp $+2       ! '$'表示当前指令的地址
    out #0xA0,al        ! and to 8259A-2        ! 在发送到8259A 从芯片
    .word   0x00eb,0x00eb
! Linux系统硬件中断号被设置成从0x20开始。硬件中断请求信号与中断对应表。
    mov al,#0x20        ! start of hardware int's (0x20)
    out #0x21,al        ! 送主芯片ICW2 命令字,起始中断号,要送奇地址。
    .word   0x00eb,0x00eb
    mov al,#0x28        ! start of hardware int's 2 (0x28)
    out #0xA1,al        ! 送从芯片ICW2 命令字,从芯片的起始中断号。
    .word   0x00eb,0x00eb
    mov al,#0x04        ! 8259-1 is master
    out #0x21,al        ! 送主芯片ICW3 命令字,主芯片的IR2 连从芯片INT。
    .word   0x00eb,0x00eb
    mov al,#0x02        ! 8259-2 is slave
    out #0xA1,al        ! 送从芯片ICW3 命令字,表示从芯片的INT 连到主芯
    .word   0x00eb,0x00eb
    mov al,#0x01        ! 8086 mode for both
    out #0x21,al        ! 送主芯片ICW4 命令字。8086 模式;普通EOI 方式,
! 需发送指令来复位。初始化结束,芯片就绪。
    .word   0x00eb,0x00eb
    out #0xA1,al        !送从芯片ICW4 命令字,内容同上。
    .word   0x00eb,0x00eb
    mov al,#0xFF        ! mask off all interrupts for now
    out #0x21,al        ! 屏蔽主芯片所有中断请求。
    .word   0x00eb,0x00eb
    out #0xA1,al        !屏蔽从芯片所有中断请求。

! well, that certainly wasnt fun :-(. Hopefully it works, and we dont
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it s less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's' the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.
!
! 哼,上面这段当然没劲??,希望这样能工作,而且我们也不再需要乏味的BIOS 了(除了
! 初始的加载?。BIOS 子程序要求很多不必要的数据,而且它一点都没趣。那是“真正”的
! 程序员所做的事。

! 这里设置进入32 位保护模式运行。首先加载机器状态字(lmsw - Load Machine Status Word),也称
! 控制寄存器CR0,其比特位01 将导致CPU 工作在保护模式。
    mov ax,#0x0001  ! protected mode (PE) bit
    lmsw    ax      ! This is it!
    jmpi    0,8     ! jmp offset 0 of segment 8 (cs)
! 我们已经将system 模块移动到0x00000 开始的地方,所以这里的偏移地址是0。这里的段
! 值的8 已经是保护模式下的段选择符了,用于选择描述符表和描述符表项以及所要求的特权级。
! 段选择符长度为16 位(2 字节);位0-1 表示请求的特权级0-3linux 操作系统只
! 用到两级:0 级(系统级)和3 级(用户级);位2 用于选择全局描述符表(0)还是局部描
! 述符表(1);位3-15 是描述符表项的索引,指出选择第几项描述符。所以段选择符
! 8(0b0000,0000,0000,1000)表示请求特权级0、使用全局描述符表中的第1 项,该项指出
! 代码的基地址是0(参见209 行),因此这里的跳转指令就会去执行system 中的代码。

! This routine checks that the keyboard command queue is empty
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn t proceed anyway.
! 下面这个子程序检查键盘命令队列是否为空。这里不使用超时方法 - 如果这里死机,
! 则说明PC 机有问题,我们就没有办法再处理下去了。
!
! 只有当输入缓冲器为空时(状态寄存器位2 = 0)才可以对其进行写命令。
empty_8042:
    .word   0x00eb,0x00eb
    in  al,#0x64    ! 8042 status port    !AT 键盘控制器状态寄存器。
    test    al,#2       ! is input buffer full?    ! 测试位2,输入缓冲器满?
    jnz empty_8042  ! yes - loop
    ret

! Routine trying to recognize type of SVGA-board present (if any)
! and if it recognize one gives the choices of resolution it offers.
! If one is found the resolution chosen is given by al,ah (rows,cols).
! 下面是用于识别SVGA显示卡(若有的话)的子程序。若识别出一块就是向用户提供选择分辨率的机会,
! 并把分辨率放入到寄存器alah(行,列)中返回。
!
! 注意下面320--670行代码涉及到众多显示卡端口信息,因此计较复杂。但由于这段代码与内核运行关系
! 不大,因此可以跳过不看。
! //////////////////////////////////////////////////////////////////////
第320行…………
chsvga: cld
    push    ds
    push    cs
    pop ds
    mov     ax,#0xc000
    mov es,ax
    lea si,msg1
    call    prtstr

省略~~~~~~
省略~~~~~~

docr:   push    ax
    push    cx
    mov bh,#0x00
    mov ah,#0x0e
    mov al,#0x0a
    mov cx,#0x01
    int 0x10
    mov al,#0x0d
    int 0x10
    pop cx
    pop ax
    ret670行…………
! //////////////////////////////////////////////////////////////////////

gdt: 
! 全局描述符表开始处。描述符表由多个8 字节长的描述符项组成。
! 这里给出了3 个描述符项。第1 项无用,但须存在。第2 项是系统代码段
! 描述符,第3 项是系统数据段描述符。每个描述符的具体
! 含义参见列表后说明。

    .word 0,0,0,0 ! dummy !1 个描述符,不用。

! 这里在gdt 表中的偏移量为0x08,当加载代码段寄存器(段选择符)时,使用的是这个偏移值。
    .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word 0x0000 ! base address=0
    .word 0x9A00 ! code read/exec
    .word 0x00C0 ! granularity=4096, 386

! 这里在gdt 表中的偏移量是0x10,当加载数据段寄存器(如ds 等)时,使用的是这个偏移值。
    .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word 0x0000 ! base address=0
    .word 0x9200 ! data read/write
    .word 0x00C0 ! granularity=4096, 386

! 下面是加载中断描述符表寄存器idtr的指令lidt要求的6字节操作数。前2字节的IDT表的
! 限长,后4字节是idt表在线性地址空间中的32位基地址。CPU要求在进入保护模式之前需设
!IDT表,因此这里先设置一个长度为0的空表。
idt_48:
    .word   0           ! idt limit=0
    .word   0,0         ! idt base=0L

! 这是加载全局描述符表寄存器的gdtr的指令lgdt要求的6字节操作数。前2字节是gdt表的限
! 长,后4字节是gdt表的线性基地址。这里全局表长度设置为2KB0x7ff即可),因为每8字节
! 组成一个段描述符项,所以表中共有256项目。4字节的线性基地址为0x0009<<16+0x0200+gdt!0x90200+gdt。(符号gdt是全局表在本程序段中的偏移地址)
gdt_48:
    .word   0x800       ! gdt limit=2048, 256 GDT entries
    .word   512+gdt,0x9 ! gdt base = 0X9xxxx

! 显示信息
msg1:       .ascii  "Press <RETURN> to see SVGA-modes available or any other key to continue."
        db  0x0d, 0x0a, 0x0a, 0x00
msg2:       .ascii  "Mode: COLSxROWS:"
        db  0x0d, 0x0a, 0x0a, 0x00
msg3:       .ascii  "Choose mode by pressing the corresponding number."
        db  0x0d, 0x0a, 0x00

! 下面是4个显示卡的特征数据串。
idati:      .ascii  "761295520"
idcandt:    .byte   0xa5
idgenoa:    .byte   0x77, 0x00, 0x66, 0x99
idparadise: .ascii  "VGA="

! 下面是各种显示卡可使用的扩展模式个数和相应的模式号列表。
! 格式如下:
! Manufacturer:   Numofmodes:   Mode:
! 厂家:           模式数量:      模式列表:
moati:      .byte   0x02,   0x23, 0x33 
moahead:    .byte   0x05,   0x22, 0x23, 0x24, 0x2f, 0x34
mocandt:    .byte   0x02,   0x60, 0x61
mocirrus:   .byte   0x04,   0x1f, 0x20, 0x22, 0x31
moeverex:   .byte   0x0a,   0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
mogenoa:    .byte   0x0a,   0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
moparadise: .byte   0x02,   0x55, 0x54
motrident:  .byte   0x07,   0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
motseng:    .byte   0x05,   0x26, 0x2a, 0x23, 0x24, 0x22
movideo7:   .byte   0x06,   0x40, 0x43, 0x44, 0x41, 0x42, 0x45

! 下面是各种牌子VGA显示卡可使用的模式对应的列,行值列表。
!           msb = Cols lsb = Rows:
!           高字节 = 列数   低字节 = 行数

dscati:     .word   0x8419, 0x842c
dscahead:   .word   0x842c, 0x8419, 0x841c, 0xa032, 0x5042
dsccandt:   .word   0x8419, 0x8432
dsccirrus:  .word   0x8419, 0x842c, 0x841e, 0x6425
dsceverex:  .word   0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
dscgenoa:   .word   0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
dscparadise:    .word   0x8419, 0x842b
dsctrident: .word   0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
dsctseng:   .word   0x503c, 0x6428, 0x8419, 0x841c, 0x842c
dscvideo7:  .word   0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c

.text
endtext:
.data
enddata:
.bss
endbss:

参考:

linux内核完全剖析

你可能感兴趣的:(linux,kernel)