- [assembird] to chinatree :我不牛x,也是初学者。 12/08 18:20
- [chinatree] 沙发,没想到论坛里居然有楼主这样牛逼人物。 12/03 00:20
- [assembird] to chinatree: 相互学习,共同进步。 11/11 12:51
- [chinatree] 哥们,顶了。 11/11 01:06
- [chinatree] 那就好。 10/31 00:02
- [assembird] 已找到问题了,应该是16h号中断,功能0. 把 _get_key: mov 10/30 23:47
- [assembird] To chiantree:准备试一下。 10/30 21:35
- [chinatree] 要不就放dosbox里跑个试试。 10/29 23:52
[2014-10-08 22:14] 自制OS雏形
学习linux 2.4.0内核的小成果,里面借鉴了很多linux中的数据结构、函数、代码、原理等,并非原创。自己在xp上用vc和masm将内核启动的一小部分实现出来。引导,中断,启动内存管理,页表设置等内容,
都已经完成。当然问题多多,不是要写一个系统,而是学习的记录。如果你是大牛就pass吧。如果你是如我一样的菜鸟,学linux内核不知从哪下手,可以看看。在过程中碰到了很多困难,也解决了很多问题。总体觉得比捧着厚厚的内核书籍看,来的深刻,希望对你有所启发。
这是其中的汇编代码,先睹为快:
全部代码下载链接:http://download.csdn.net/detail/hello_pengzhen/8014331
系统引导代码:
;..\..\Bin\ML.EXE /Zm /c /Fl /Sa bootkerdev.asm
;..\..\Bin\LINK5.EXE bootkerdev.obj
assume cs:code
code segment
start:
mov ax,offset start
add ax,07c0h
mov ds,ax
mov si,offset hello
push si
call putstr
loadkernel:
;加载内核到0x80000,一个扇区
mov ax, 8000h ;kernel将要存放的内存的段地址
push ax
mov ax,02h ;扇区号
push ax
mov ax,09h ;扇区数
push ax
call loadfp
mov si,offset loadinfo
push si
call putstr
;加载32位初始化代码到0x90000,一个扇区
mov ax, 9000h ;kernel将要存放的内存的段地址
push ax
mov ax,0bh ;扇区号
push ax
mov ax,040h ;扇区数
push ax
call loadfp
mov si,offset loadinitinfo
push si
call putstr
DB 0eah ;操作码 ,jmp 到8000:0000
DW 0 ;16位偏移量
DW 8000h ;段值
s:
jmp s
;显示一个字符
;输入参数:一个ASCII码
;返回参数:无
bios_putchar PROC FAR
push bp
mov bp,sp
push ax
mov ax,[bp+6]
mov bl,BYTE PTR 0
mov ah,0eh
int 10h
pop ax
pop bp
ret 2
bios_putchar ENDP
putstr PROC FAR
push bp
mov bp,sp
push si
mov si,[bp+6]
d_disp:
TEST [si],0FFh
je d_exit
push [si]
call bios_putchar
inc si
jmp d_disp
d_exit:
pop si
pop bp
ret 2
putstr ENDP
;参数1:存放的段基址
;参数2:开始读的扇区号
;参数3:要的读的扇区个数
loadfp PROC FAR
push bp
mov bp,sp
push es
push ax
push bx
push dx
push cx
mov ax, [bp+0ah] ;要存放的内存的段基址,偏移地址都是0
mov es, ax ; 传递给ES
mov bx, 0 ; kernel偏移地址为0
mov dl, 0 ; 驱动器号为0h,为A驱
mov dh, 0 ; 磁头号为0
mov ch, 0 ;磁道号为0
mov cl, BYTE PTR[bp+8h] ;扇区号
mov al, BYTE PTR[bp+6h] ; 要读的扇区数
mov ah, 2 ; 调用读磁盘的中断程序
int 13h
pop cx
pop dx
pop bx
pop ax
pop es
pop bp
ret 6
loadfp ENDP
hello db 'The Simplest Boot Section',13,10,0
loadinfo db 'I Have Load The Kernel Program To 8000:0000',13,10,0
loadinitinfo db 'I Have Load The 32BIT Init Program To 9000:0000',13,10,0
db 510-($-start) dup (0)
db 055h
db 0aah
code ends
end start
保护模式初始化代码:
;名称:minikernel.asm
;功能:演示实方式和保护方式切换(切换到16位代码段)
;..\..\Bin\ML.EXE /c /I. /Zm /Fl /Sa minikernel.asm
;..\..\Bin\LINK5.EXE minikernel.obj
;系统启动后会把程序加载到8000:0000处
;----------------------------------------------------------------------------
.386P
include myos.inc
;----------------------------------------------------------------------------
GOFF EQU 8000h
CSEG SEGMENT USE16 ;16位代码段
ASSUME CS:CSEG;,DS:DSEG
org 80000h
;----------------------------------------------------------------------------
Start PROC
;lidt VGIDT
;lgdt VGDTR
;cli ;关中断
;EnableA20 ;打开地址线A20
;empty_8042
;mov al,0D1h
;out 064h,al
;empty_8042
;mov al,0dfh
;out 060,al
;empty_8042
;int 15h 获取物理内存
;es 的基地址是0
xor eax,eax
mov esi,ALT_MEM_K
mov es:[esi],eax
get_mem_e820:
xor ebx,ebx
mov di,E820_MAP
e820_start:
mov eax,00000e820h
mov edx,SMAP
mov ecx,20
;push ds
;pop es
int 015h
jc e820_fail
.if eax!=SMAP
jmp e820_fail
.endif
mov esi,E820_MAP_NR
mov al,es:[esi]
.if al>=E820_MAX
jmp e820_fail
.endif
mov esi,E820_MAP_NR
inc BYTE PTR es:[esi]
mov ax,di
add ax,20
mov di,ax
.if ebx!=0
jmp e820_start
.endif
e820_fail:
get_mem_e801:
mov ax,0e801h
int 015h
jc e801_fail
and edx,0ffffh
shl edx,6
mov esi,ALT_MEM_K
mov es:[esi],edx
and ecx,0ffffh
add es:[esi],ecx
e801_fail:
get_mem_88:
mov ah,088h
int 015h
mov esi,EXT_MEM_K
mov es:[esi],ax
;jmp $;!!!测试
lidt VGIDT
lgdt VGDTR
cli ;关中断
EnableA20 ;打开地址线A20
;切换到保护方式
mov eax,cr0
or eax,1
mov cr0,eax
;清指令预取队列,并真正进入保护方式
JUMP16 CODE16_SEL,<OFFSET ENTER_32BIT_PROTECTION>
ENTER_32BIT_PROTECTION: ;现在开始在保护方式下运行
mov ax,WORD PTR MINI_DATA_SEL
mov es,ax
mov ds,ax
mov fs,ax
mov gs,ax
mov ax,WORD PTR MINI_VIDEO_SEL
mov es,ax
;mov ax,WORD PTR MINI_STACK_SEL
;mov ss,ax
;mov sp,0FFFFh
JUMP16 MINI_CODE_SEL,930h ;930h 是生成的minikernel.map文件中CSEG32起始位置
Start ENDP
;----------------------------------------------------------------------------
GDT LABEL BYTE ;全局描述符表
DUMMY Desc <> ;空描述符
KERNEL_CODE_DESC Desc <0ffffh,0h,0h,ATCER+DPL0,GL+D32+0fh,0h> ;32位内核代码
MINI_DATA_DESC Desc <0ffffh,0h,0h,ATDW+DPL0, GL+D32+0fh,0h> ;32位的数据段
USER_CODE_DESC Desc <0ffffh,0h,0h,ATCER+DPL3, GL+D32+0fh,0h> ;32位用户代码段
USER_DATA_DESC Desc <0ffffh,0h,0h,ATDW+DPL3, GL+D32+0fh,0h> ;32位的数据段
CODE16_DESC Desc <0ffffh,0h,08h,ATCE,0fh,0h> ;16位初始化代码段
MINI_CODE_DESC Desc <0ffffh,0h,08h,ATCE,D32+0fh,0h> ;32位初始化代码段
MINI_VIDEO_DESC Desc <0ffffh,8000h,0bh,ATDW+DPL0,GL+D32+0fh,0h> ;视频输出段
LDT_DESC Desc <>
TSS_DESC Desc <>
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表长度
VGDTR PDesc <GDTLen-1,OFFSET GDT+80000h> ;伪描述符
;----------------------------------------------------------------------------
CODE16_SEL = CODE16_DESC -GDT ;代码段选择子
MINI_VIDEO_SEL = MINI_VIDEO_DESC -GDT ;源数据段选择子
MINI_CODE_SEL = MINI_CODE_DESC -GDT ;32位初始化代码选择子
MINI_DATA_SEL = MINI_DATA_DESC -GDT
KERNEL_CODE_SEL = KERNEL_CODE_DESC -GDT
USER_CODE_SEL = USER_CODE_DESC -GDT
USER_DATA_SEL = USER_DATA_DESC -GDT
LDT_SEL = LDT_DESC -GDT
TSS_SEL = TSS_DESC -GDT
IDT LABEL BYTE ;中断描述符表
qword 256 dup(0)
IDTLen = $-IDT
VGIDT PDesc <IDTLen-1,OFFSET IDT+080000h>
CODE16_DESCLen = $ - Start
CSEG ENDS ;代码段定义结束
CSEG32 SEGMENT USE32
ASSUME CS:CSEG32
C32_EBGIN PROC
BIG_JUMP32 KERNEL_CODE_SEL,090000h
C32_EBGIN ENDP
CSEG32 ENDS
END Start
系统建立,中断处理,进入C程序的代码
;名称:miniint.asm
;功能:32位代码段,初始化作用
;注意:此时已经工作在保护模式下
;..\..\Bin\ML.EXE /c /coff /I. /Fl /Sa miniinit.asm
;..\..\Bin\LINK.EXE /ALIGN:16 /BASE:0xC0090000 /ENTRY:kmain /NOLOGO /MAP:miniinit.map /SUBSYSTEM:CONSOLE miniinit.obj
;系统启动后会把程序加载到0x00090000处
;!!!必须链接到0xC0090000处!!!
;过程定义距离修饰符必须是NEAR,NEAR调用不会压入cs段选择符。
;一是由于C代码优化后所有的函数都是NEAR,所以保证C调用汇编的程序不会
;出错。二是由于32位的保护模式代码,只有一种FLAT模式,无所谓距离限制了。
.386p
.model flat,stdcall
option casemap: none
include myos.inc
SEAX equ 018h
SES equ 020h
ORIG_EAX equ 024h
SCS equ 02ch
EFLAGS equ 30h
VM_MASK equ 000020000h
.data
note byte "We open the page system !",0
int_msg byte "Unknown interrupt \n",0
GDT_TABLE_ADDR PDesc <0>
IDT_TABLE_ADDR PDesc <0>
sys_ni_syscall proto NEAR stdcall
sys_call_table dword 256 dup(offset sys_ni_syscall)
.code
align
org 0h
;外部变量声明
EXTERN idt_table:DWORD ;IDT指针
EXTERN gdt_table:DWORD ;GDT指针
;C代码中的过程声明
setup_idt proto NEAR stdcall
start_kernel proto NEAR stdcall
do_divide_error proto NEAR stdcall,:DWORD,:DWORD
do_debug proto NEAR stdcall,:DWORD,:DWORD
do_nmi proto NEAR stdcall,:DWORD,:DWORD
do_int3 proto NEAR stdcall,:DWORD,:DWORD
do_overflow proto NEAR stdcall,:DWORD,:DWORD
do_bounds proto NEAR stdcall,:DWORD,:DWORD
do_invalid_op proto NEAR stdcall,:DWORD,:DWORD
do_device_not_available proto NEAR stdcall,:DWORD,:DWORD
do_double_fault proto NEAR stdcall,:DWORD,:DWORD
do_coprocessor_segment_overrun proto NEAR stdcall,:DWORD,:DWORD
do_invalid_TSS proto NEAR stdcall,:DWORD,:DWORD
do_segment_not_present proto NEAR stdcall,:DWORD,:DWORD
do_stack_segment proto NEAR stdcall,:DWORD,:DWORD
do_general_protection proto NEAR stdcall,:DWORD,:DWORD
do_page_fault proto NEAR stdcall,:DWORD,:DWORD
do_coprocessor_error proto NEAR stdcall,:DWORD,:DWORD
do_simd_coprocessor_error proto NEAR stdcall,:DWORD,:DWORD
do_alignment_check proto NEAR stdcall,:DWORD,:DWORD
do_spurious_interrupt_bug proto NEAR stdcall,:DWORD,:DWORD
do_machine_check proto NEAR stdcall,:DWORD,:DWORD
do_IRQ proto NEAR stdcall,:PTR PT_REGS
;汇编代码中的过程声明
disp_anyting proto NEAR stdcall,straddr:DWORD
set_gate proto NEAR stdcall,gate_addr:DWORD,gtype:BYTE,dpl:BYTE,faddr:DWORD
inter_get_current proto NEAR stdcall
outb proto NEAR stdcall,value:BYTE,port:WORD
outb_p proto NEAR stdcall,value:BYTE,port:WORD
IRQ0x00_interrupt proto NEAR stdcall
IRQ0x01_interrupt proto NEAR stdcall
IRQ0x02_interrupt proto NEAR stdcall
IRQ0x03_interrupt proto NEAR stdcall
IRQ0x04_interrupt proto NEAR stdcall
IRQ0x05_interrupt proto NEAR stdcall
IRQ0x06_interrupt proto NEAR stdcall
IRQ0x07_interrupt proto NEAR stdcall
IRQ0x08_interrupt proto NEAR stdcall
IRQ0x09_interrupt proto NEAR stdcall
IRQ0x0a_interrupt proto NEAR stdcall
IRQ0x0b_interrupt proto NEAR stdcall
IRQ0x0c_interrupt proto NEAR stdcall
IRQ0x0d_interrupt proto NEAR stdcall
IRQ0x0e_interrupt proto NEAR stdcall
IRQ0x0f_interrupt proto NEAR stdcall
;异常与中断过程声明
divide_error proto NEAR stdcall
debug proto NEAR stdcall
nmi proto NEAR stdcall
int3 proto NEAR stdcall
overflow proto NEAR stdcall
bounds proto NEAR stdcall
invalid_op proto NEAR stdcall
device_not_available proto NEAR stdcall
double_fault proto NEAR stdcall
coprocessor_segment_overrun proto NEAR stdcall
invalid_TSS proto NEAR stdcall
segment_not_present proto NEAR stdcall
stack_segment proto NEAR stdcall
general_protection proto NEAR stdcall
coprocessor_error proto NEAR stdcall
simd_coprocessor_error proto NEAR stdcall
alignment_check proto NEAR stdcall
spurious_interrupt_bug proto NEAR stdcall
machine_check proto NEAR stdcall
page_fault proto NEAR stdcall
system_call proto NEAR stdcall
;--------------------------------------
kmain PROC NEAR
;从PD_BASE开始的12K清0
mov esi,PD_BASE
xor eax,eax
.repeat
mov [esi],eax
add esi,4
.until esi==INIT_PG_END
;设置页目录项
mov edi,PD_BASE
;0~4M
mov eax,PG0_BASE+INIT_PG_PE_ATTR
mov [edi],eax
mov [edi+4*768],eax
;4M~8M
mov eax,PG1_BASE+INIT_PG_PE_ATTR
mov [edi+4],eax
mov [edi+4*769],eax
mov edi,PG0_BASE ;从PG0_BASE位置填写页表项目,映射0~8MB空间
mov eax,INIT_PG_PE_ATTR
.repeat
mov [edi],eax
add eax,01000h
add edi,4h
.until edi==INIT_PG_END
mov eax,PD_BASE ;也目录表的位置
mov cr3,eax ;设置页表基地址,是物理地址
mov eax,cr0 ;开启分页
or eax,080000000h
mov cr0,eax
;jmp far ptr @f
;@@:
;!!!非常重要,可以立即以线性地址的形式运行
BIG_JUMP32 __KERNEL_CODE,<offset @after_page>
@after_page:
;设置堆栈
mov ax,__KERNEL_DATA
mov ds,ax
mov es,ax ;es 要及时更新为与ds相同的值,在C语言中的数据传输等功能都会用到
mov ss,ax
mov esp,INIT_TASK_UNION+INIT_STACK_SIZE
invoke disp_anyting,addr note
invoke setup_idt
;初始化,eflags
mov eax,0h
push eax
popfd
xor eax,eax
lldt ax
sgdt GDT_TABLE_ADDR
mov eax,GDT_TABLE_ADDR.Base
mov gdt_table,eax
lgdt GDT_TABLE_ADDR
lidt IDT_TABLE_ADDR
;test idt是否设置成功
;mov edi,0C8000000h
;mov eax,0h
;mov [edi],eax
;进入C代码内核设置阶段
invoke start_kernel
@@:jmp @b
kmain ENDP
disp_anyting PROC NEAR stdcall,straddr:DWORD
pushad
xor edi,edi
xor ax,ax
mov ah,9h
shl ah,4
or ah,4h
mov esi,straddr
mov edi,CONSOLE_BASE
.while BYTE PTR ds:[esi]!=0h
mov al,BYTE PTR ds:[esi]
mov WORD PTR es:[edi],ax
inc si
add di,2
.endw
popad
ret ;这个ret指令是必须的,ml 中要增加/Gz 选项
disp_anyting ENDP
ignore_int PROC NEAR stdcall
cld
push eax
push ecx
push edx
push es
push ds
mov ax,__KERNEL_DATA
mov ds,ax
mov es,ax
invoke disp_anyting,addr int_msg
pop ds
pop es
pop edx
pop ecx
pop eax
;pop eax;这里模拟,保护异常,有错误码,需先弹出
;hlt
iretd ;之前是iret
ignore_int ENDP
set_gate PROC NEAR stdcall,gate_addr:DWORD,gtype:BYTE,dpl:BYTE,faddr:DWORD
push edx
push eax
push edi
mov eax,gate_addr
mov edi,eax
mov edx,faddr
mov eax,__KERNEL_CODE
shl eax,16
mov ax,dx
mov dl,0
mov dl,dpl
shl dx,5
add dl,gtype
shl dx,8
add dx,8000h
mov [edi],eax
mov [edi+4],edx
pop edi
pop eax
pop edx
ret
set_gate ENDP
setup_idt PROC NEAR stdcall
;设置中断描述符
;edx高16位为过程入口点偏移值高16位
lea edx,ignore_int
;eax高16位为段描述符
mov eax,__KERNEL_CODE
shl eax,16
;eax低16位为过程入口点偏移值低16位
mov ax,dx
;edx低16位为属性值,高4位:P(1bit)=1,DPL(2bit)=0,e(5bit)=中断门,低4位为0
mov dx,08e00h
sidt IDT_TABLE_ADDR
mov edi,IDT_TABLE_ADDR.Base
mov idt_table,edi
mov ecx,0
.while ecx<256
;mov [edi],eax
;mov [edi+4],edx
;test set_gate
invoke set_gate,edi,0eh,0h,addr ignore_int
add edi,8
inc ecx
.endw
ret
setup_idt ENDP
page_fault PROC NEAR stdcall
push do_page_fault
jmp error_code
page_fault ENDP
error_code:
push ds
push eax
xor eax,eax
push ebp
push edi
push esi
push edx
dec eax ;eax=-1
push ecx
push ebx
cld
mov cx,es
mov esi,[esp+ORIG_EAX] ;提取错误码
mov edi,[esp+SES] ;提取处理函数
mov [esp+ORIG_EAX],eax ;错误码的位置变成-1
mov [esp+SES],cx ;处理函数的位置变成es的值
mov edx,esp ;edx 存放栈顶指针
push esi ;错误码压栈
push edx ;寄存器组指针压栈
mov ax,__KERNEL_DATA
mov ds,ax
mov es,dx ;
GET_CURRENT
call edi
jmp ret_from_exception
ret_from_exception:
ret_from_intr:
GET_CURRENT
;判断进入中断时CPU的模式,及特权级
;mov eax,[esp+EFLAGS]
;mov al,[esp+SCS] ;eflags 与 cs 混合
;test eax,VM_MASK OR 3
;jne ret_with_reschedule
jmp restore_all
restore_all:
RESTORE_ALL_M
common_interrupt:
SAVE_ALL_M
;构造一个CALL指令的调用
push esp ;do_IRQ 指向pt_regs指针,自己添加
push ret_from_intr
call_do_IRQ:
jmp do_IRQ
divide_error PROC NEAR stdcall
push 0 ;造一个错误码
push do_divide_error
jmp error_code
divide_error endp
debug PROC NEAR stdcall
push 0 ;造一个错误码
push do_debug
jmp error_code
debug endp
nmi PROC NEAR stdcall
push 0 ;造一个错误码
push do_nmi
RESTORE_ALL_M
nmi endp
int3 PROC NEAR stdcall
push 0 ;造一个错误码
push do_int3
jmp error_code
int3 endp
overflow PROC NEAR stdcall
push 0 ;造一个错误码
push do_overflow
jmp error_code
overflow endp
bounds PROC NEAR stdcall
push 0 ;造一个错误码
push do_bounds
jmp error_code
bounds endp
invalid_op PROC NEAR stdcall
push 0 ;造一个错误码
push do_invalid_op
jmp error_code
invalid_op endp
device_not_available PROC NEAR stdcall
device_not_available endp
double_fault PROC NEAR stdcall
double_fault endp
coprocessor_segment_overrun PROC NEAR stdcall
push 0 ;造一个错误码
push do_coprocessor_segment_overrun
jmp error_code
coprocessor_segment_overrun endp
invalid_TSS PROC NEAR stdcall
push do_invalid_TSS
jmp error_code
invalid_TSS endp
segment_not_present PROC NEAR stdcall
push do_segment_not_present
jmp error_code
segment_not_present endp
stack_segment PROC NEAR stdcall
push do_stack_segment
jmp error_code
stack_segment endp
general_protection PROC NEAR stdcall
push do_general_protection
jmp error_code
general_protection endp
coprocessor_error PROC NEAR stdcall
push 0 ;造一个错误码
push do_coprocessor_error
jmp error_code
coprocessor_error endp
simd_coprocessor_error PROC NEAR stdcall
push 0 ;造一个错误码
push do_simd_coprocessor_error
jmp error_code
simd_coprocessor_error endp
alignment_check PROC NEAR stdcall
push do_alignment_check
jmp error_code
alignment_check endp
spurious_interrupt_bug PROC NEAR stdcall
push 0 ;造一个错误码
push do_spurious_interrupt_bug
jmp error_code
spurious_interrupt_bug endp
machine_check PROC NEAR stdcall
push 0 ;造一个错误码
push do_machine_check
jmp error_code
machine_check endp
system_call proc NEAR stdcall
push eax ;保存eax
SAVE_ALL_M
GET_CURRENT
.if eax < NR_SYSCALLS
call [sys_call_table+eax*4]
mov [esp+SEAX],eax
.elseif
jmp badsys
.endif
system_call endp
ret_from_sys_call:
RESTORE_ALL_M
badsys:
mov eax,-1
mov [esp+SEAX],eax
jmp ret_from_sys_call
inter_get_current proc NEAR stdcall
GET_CURRENT
mov eax,ebx
ret
inter_get_current endp
outb proc NEAR stdcall,value:BYTE,port:WORD
push ax
push dx
mov al,value
mov dx,port
out dx,al
pop dx
pop ax
ret
outb endp
outb_p proc NEAR stdcall,value:BYTE,port:WORD
push ax
push dx
mov al,value
mov dx,port
out dx,al
jmp @f
@@:
jmp @f
@@:
jmp @f
@@:
jmp @f
@@:
pop dx
pop ax
ret
outb_p endp
IRQ0x00_interrupt proc NEAR stdcall
push 0-256
jmp common_interrupt
IRQ0x00_interrupt endp
IRQ0x01_interrupt proc NEAR stdcall
push 1-256
jmp common_interrupt
IRQ0x01_interrupt endp
IRQ0x02_interrupt proc NEAR stdcall
push 2-256
jmp common_interrupt
IRQ0x02_interrupt endp
IRQ0x03_interrupt proc NEAR stdcall
push 3-256
jmp common_interrupt
IRQ0x03_interrupt endp
IRQ0x04_interrupt proc NEAR stdcall
push 4-256
jmp common_interrupt
IRQ0x04_interrupt endp
IRQ0x05_interrupt proc NEAR stdcall
push 5-256
jmp common_interrupt
IRQ0x05_interrupt endp
IRQ0x06_interrupt proc NEAR stdcall
push 6-256
jmp common_interrupt
IRQ0x06_interrupt endp
IRQ0x07_interrupt proc NEAR stdcall
push 7-256
jmp common_interrupt
IRQ0x07_interrupt endp
IRQ0x08_interrupt proc NEAR stdcall
push 8-256
jmp common_interrupt
IRQ0x08_interrupt endp
IRQ0x09_interrupt proc NEAR stdcall
push 9-256
jmp common_interrupt
IRQ0x09_interrupt endp
IRQ0x0a_interrupt proc NEAR stdcall
push 0ah-256
jmp common_interrupt
IRQ0x0a_interrupt endp
IRQ0x0b_interrupt proc NEAR stdcall
push 0bh-256
jmp common_interrupt
IRQ0x0b_interrupt endp
IRQ0x0c_interrupt proc NEAR stdcall
push 0ch-256
jmp common_interrupt
IRQ0x0c_interrupt endp
IRQ0x0d_interrupt proc NEAR stdcall
push 0dh-256
jmp common_interrupt
IRQ0x0d_interrupt endp
IRQ0x0e_interrupt proc NEAR stdcall
push 0eh-256
jmp common_interrupt
IRQ0x0e_interrupt endp
IRQ0x0f_interrupt proc NEAR stdcall
push 0fh-256
jmp common_interrupt
IRQ0x0f_interrupt endp
end kmain
都已经完成。当然问题多多,不是要写一个系统,而是学习的记录。如果你是大牛就pass吧。如果你是如我一样的菜鸟,学linux内核不知从哪下手,可以看看。在过程中碰到了很多困难,也解决了很多问题。总体觉得比捧着厚厚的内核书籍看,来的深刻,希望对你有所启发。
这是其中的汇编代码,先睹为快:
全部代码下载链接:http://download.csdn.net/detail/hello_pengzhen/8014331
系统引导代码:
;..\..\Bin\ML.EXE /Zm /c /Fl /Sa bootkerdev.asm
;..\..\Bin\LINK5.EXE bootkerdev.obj
assume cs:code
code segment
start:
mov ax,offset start
add ax,07c0h
mov ds,ax
mov si,offset hello
push si
call putstr
loadkernel:
;加载内核到0x80000,一个扇区
mov ax, 8000h ;kernel将要存放的内存的段地址
push ax
mov ax,02h ;扇区号
push ax
mov ax,09h ;扇区数
push ax
call loadfp
mov si,offset loadinfo
push si
call putstr
;加载32位初始化代码到0x90000,一个扇区
mov ax, 9000h ;kernel将要存放的内存的段地址
push ax
mov ax,0bh ;扇区号
push ax
mov ax,040h ;扇区数
push ax
call loadfp
mov si,offset loadinitinfo
push si
call putstr
DB 0eah ;操作码 ,jmp 到8000:0000
DW 0 ;16位偏移量
DW 8000h ;段值
s:
jmp s
;显示一个字符
;输入参数:一个ASCII码
;返回参数:无
bios_putchar PROC FAR
push bp
mov bp,sp
push ax
mov ax,[bp+6]
mov bl,BYTE PTR 0
mov ah,0eh
int 10h
pop ax
pop bp
ret 2
bios_putchar ENDP
putstr PROC FAR
push bp
mov bp,sp
push si
mov si,[bp+6]
d_disp:
TEST [si],0FFh
je d_exit
push [si]
call bios_putchar
inc si
jmp d_disp
d_exit:
pop si
pop bp
ret 2
putstr ENDP
;参数1:存放的段基址
;参数2:开始读的扇区号
;参数3:要的读的扇区个数
loadfp PROC FAR
push bp
mov bp,sp
push es
push ax
push bx
push dx
push cx
mov ax, [bp+0ah] ;要存放的内存的段基址,偏移地址都是0
mov es, ax ; 传递给ES
mov bx, 0 ; kernel偏移地址为0
mov dl, 0 ; 驱动器号为0h,为A驱
mov dh, 0 ; 磁头号为0
mov ch, 0 ;磁道号为0
mov cl, BYTE PTR[bp+8h] ;扇区号
mov al, BYTE PTR[bp+6h] ; 要读的扇区数
mov ah, 2 ; 调用读磁盘的中断程序
int 13h
pop cx
pop dx
pop bx
pop ax
pop es
pop bp
ret 6
loadfp ENDP
hello db 'The Simplest Boot Section',13,10,0
loadinfo db 'I Have Load The Kernel Program To 8000:0000',13,10,0
loadinitinfo db 'I Have Load The 32BIT Init Program To 9000:0000',13,10,0
db 510-($-start) dup (0)
db 055h
db 0aah
code ends
end start
保护模式初始化代码:
;名称:minikernel.asm
;功能:演示实方式和保护方式切换(切换到16位代码段)
;..\..\Bin\ML.EXE /c /I. /Zm /Fl /Sa minikernel.asm
;..\..\Bin\LINK5.EXE minikernel.obj
;系统启动后会把程序加载到8000:0000处
;----------------------------------------------------------------------------
.386P
include myos.inc
;----------------------------------------------------------------------------
GOFF EQU 8000h
CSEG SEGMENT USE16 ;16位代码段
ASSUME CS:CSEG;,DS:DSEG
org 80000h
;----------------------------------------------------------------------------
Start PROC
;lidt VGIDT
;lgdt VGDTR
;cli ;关中断
;EnableA20 ;打开地址线A20
;empty_8042
;mov al,0D1h
;out 064h,al
;empty_8042
;mov al,0dfh
;out 060,al
;empty_8042
;int 15h 获取物理内存
;es 的基地址是0
xor eax,eax
mov esi,ALT_MEM_K
mov es:[esi],eax
get_mem_e820:
xor ebx,ebx
mov di,E820_MAP
e820_start:
mov eax,00000e820h
mov edx,SMAP
mov ecx,20
;push ds
;pop es
int 015h
jc e820_fail
.if eax!=SMAP
jmp e820_fail
.endif
mov esi,E820_MAP_NR
mov al,es:[esi]
.if al>=E820_MAX
jmp e820_fail
.endif
mov esi,E820_MAP_NR
inc BYTE PTR es:[esi]
mov ax,di
add ax,20
mov di,ax
.if ebx!=0
jmp e820_start
.endif
e820_fail:
get_mem_e801:
mov ax,0e801h
int 015h
jc e801_fail
and edx,0ffffh
shl edx,6
mov esi,ALT_MEM_K
mov es:[esi],edx
and ecx,0ffffh
add es:[esi],ecx
e801_fail:
get_mem_88:
mov ah,088h
int 015h
mov esi,EXT_MEM_K
mov es:[esi],ax
;jmp $;!!!测试
lidt VGIDT
lgdt VGDTR
cli ;关中断
EnableA20 ;打开地址线A20
;切换到保护方式
mov eax,cr0
or eax,1
mov cr0,eax
;清指令预取队列,并真正进入保护方式
JUMP16 CODE16_SEL,<OFFSET ENTER_32BIT_PROTECTION>
ENTER_32BIT_PROTECTION: ;现在开始在保护方式下运行
mov ax,WORD PTR MINI_DATA_SEL
mov es,ax
mov ds,ax
mov fs,ax
mov gs,ax
mov ax,WORD PTR MINI_VIDEO_SEL
mov es,ax
;mov ax,WORD PTR MINI_STACK_SEL
;mov ss,ax
;mov sp,0FFFFh
JUMP16 MINI_CODE_SEL,930h ;930h 是生成的minikernel.map文件中CSEG32起始位置
Start ENDP
;----------------------------------------------------------------------------
GDT LABEL BYTE ;全局描述符表
DUMMY Desc <> ;空描述符
KERNEL_CODE_DESC Desc <0ffffh,0h,0h,ATCER+DPL0,GL+D32+0fh,0h> ;32位内核代码
MINI_DATA_DESC Desc <0ffffh,0h,0h,ATDW+DPL0, GL+D32+0fh,0h> ;32位的数据段
USER_CODE_DESC Desc <0ffffh,0h,0h,ATCER+DPL3, GL+D32+0fh,0h> ;32位用户代码段
USER_DATA_DESC Desc <0ffffh,0h,0h,ATDW+DPL3, GL+D32+0fh,0h> ;32位的数据段
CODE16_DESC Desc <0ffffh,0h,08h,ATCE,0fh,0h> ;16位初始化代码段
MINI_CODE_DESC Desc <0ffffh,0h,08h,ATCE,D32+0fh,0h> ;32位初始化代码段
MINI_VIDEO_DESC Desc <0ffffh,8000h,0bh,ATDW+DPL0,GL+D32+0fh,0h> ;视频输出段
LDT_DESC Desc <>
TSS_DESC Desc <>
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表长度
VGDTR PDesc <GDTLen-1,OFFSET GDT+80000h> ;伪描述符
;----------------------------------------------------------------------------
CODE16_SEL = CODE16_DESC -GDT ;代码段选择子
MINI_VIDEO_SEL = MINI_VIDEO_DESC -GDT ;源数据段选择子
MINI_CODE_SEL = MINI_CODE_DESC -GDT ;32位初始化代码选择子
MINI_DATA_SEL = MINI_DATA_DESC -GDT
KERNEL_CODE_SEL = KERNEL_CODE_DESC -GDT
USER_CODE_SEL = USER_CODE_DESC -GDT
USER_DATA_SEL = USER_DATA_DESC -GDT
LDT_SEL = LDT_DESC -GDT
TSS_SEL = TSS_DESC -GDT
IDT LABEL BYTE ;中断描述符表
qword 256 dup(0)
IDTLen = $-IDT
VGIDT PDesc <IDTLen-1,OFFSET IDT+080000h>
CODE16_DESCLen = $ - Start
CSEG ENDS ;代码段定义结束
CSEG32 SEGMENT USE32
ASSUME CS:CSEG32
C32_EBGIN PROC
BIG_JUMP32 KERNEL_CODE_SEL,090000h
C32_EBGIN ENDP
CSEG32 ENDS
END Start
系统建立,中断处理,进入C程序的代码
;名称:miniint.asm
;功能:32位代码段,初始化作用
;注意:此时已经工作在保护模式下
;..\..\Bin\ML.EXE /c /coff /I. /Fl /Sa miniinit.asm
;..\..\Bin\LINK.EXE /ALIGN:16 /BASE:0xC0090000 /ENTRY:kmain /NOLOGO /MAP:miniinit.map /SUBSYSTEM:CONSOLE miniinit.obj
;系统启动后会把程序加载到0x00090000处
;!!!必须链接到0xC0090000处!!!
;过程定义距离修饰符必须是NEAR,NEAR调用不会压入cs段选择符。
;一是由于C代码优化后所有的函数都是NEAR,所以保证C调用汇编的程序不会
;出错。二是由于32位的保护模式代码,只有一种FLAT模式,无所谓距离限制了。
.386p
.model flat,stdcall
option casemap: none
include myos.inc
SEAX equ 018h
SES equ 020h
ORIG_EAX equ 024h
SCS equ 02ch
EFLAGS equ 30h
VM_MASK equ 000020000h
.data
note byte "We open the page system !",0
int_msg byte "Unknown interrupt \n",0
GDT_TABLE_ADDR PDesc <0>
IDT_TABLE_ADDR PDesc <0>
sys_ni_syscall proto NEAR stdcall
sys_call_table dword 256 dup(offset sys_ni_syscall)
.code
align
org 0h
;外部变量声明
EXTERN idt_table:DWORD ;IDT指针
EXTERN gdt_table:DWORD ;GDT指针
;C代码中的过程声明
setup_idt proto NEAR stdcall
start_kernel proto NEAR stdcall
do_divide_error proto NEAR stdcall,:DWORD,:DWORD
do_debug proto NEAR stdcall,:DWORD,:DWORD
do_nmi proto NEAR stdcall,:DWORD,:DWORD
do_int3 proto NEAR stdcall,:DWORD,:DWORD
do_overflow proto NEAR stdcall,:DWORD,:DWORD
do_bounds proto NEAR stdcall,:DWORD,:DWORD
do_invalid_op proto NEAR stdcall,:DWORD,:DWORD
do_device_not_available proto NEAR stdcall,:DWORD,:DWORD
do_double_fault proto NEAR stdcall,:DWORD,:DWORD
do_coprocessor_segment_overrun proto NEAR stdcall,:DWORD,:DWORD
do_invalid_TSS proto NEAR stdcall,:DWORD,:DWORD
do_segment_not_present proto NEAR stdcall,:DWORD,:DWORD
do_stack_segment proto NEAR stdcall,:DWORD,:DWORD
do_general_protection proto NEAR stdcall,:DWORD,:DWORD
do_page_fault proto NEAR stdcall,:DWORD,:DWORD
do_coprocessor_error proto NEAR stdcall,:DWORD,:DWORD
do_simd_coprocessor_error proto NEAR stdcall,:DWORD,:DWORD
do_alignment_check proto NEAR stdcall,:DWORD,:DWORD
do_spurious_interrupt_bug proto NEAR stdcall,:DWORD,:DWORD
do_machine_check proto NEAR stdcall,:DWORD,:DWORD
do_IRQ proto NEAR stdcall,:PTR PT_REGS
;汇编代码中的过程声明
disp_anyting proto NEAR stdcall,straddr:DWORD
set_gate proto NEAR stdcall,gate_addr:DWORD,gtype:BYTE,dpl:BYTE,faddr:DWORD
inter_get_current proto NEAR stdcall
outb proto NEAR stdcall,value:BYTE,port:WORD
outb_p proto NEAR stdcall,value:BYTE,port:WORD
IRQ0x00_interrupt proto NEAR stdcall
IRQ0x01_interrupt proto NEAR stdcall
IRQ0x02_interrupt proto NEAR stdcall
IRQ0x03_interrupt proto NEAR stdcall
IRQ0x04_interrupt proto NEAR stdcall
IRQ0x05_interrupt proto NEAR stdcall
IRQ0x06_interrupt proto NEAR stdcall
IRQ0x07_interrupt proto NEAR stdcall
IRQ0x08_interrupt proto NEAR stdcall
IRQ0x09_interrupt proto NEAR stdcall
IRQ0x0a_interrupt proto NEAR stdcall
IRQ0x0b_interrupt proto NEAR stdcall
IRQ0x0c_interrupt proto NEAR stdcall
IRQ0x0d_interrupt proto NEAR stdcall
IRQ0x0e_interrupt proto NEAR stdcall
IRQ0x0f_interrupt proto NEAR stdcall
;异常与中断过程声明
divide_error proto NEAR stdcall
debug proto NEAR stdcall
nmi proto NEAR stdcall
int3 proto NEAR stdcall
overflow proto NEAR stdcall
bounds proto NEAR stdcall
invalid_op proto NEAR stdcall
device_not_available proto NEAR stdcall
double_fault proto NEAR stdcall
coprocessor_segment_overrun proto NEAR stdcall
invalid_TSS proto NEAR stdcall
segment_not_present proto NEAR stdcall
stack_segment proto NEAR stdcall
general_protection proto NEAR stdcall
coprocessor_error proto NEAR stdcall
simd_coprocessor_error proto NEAR stdcall
alignment_check proto NEAR stdcall
spurious_interrupt_bug proto NEAR stdcall
machine_check proto NEAR stdcall
page_fault proto NEAR stdcall
system_call proto NEAR stdcall
;--------------------------------------
kmain PROC NEAR
;从PD_BASE开始的12K清0
mov esi,PD_BASE
xor eax,eax
.repeat
mov [esi],eax
add esi,4
.until esi==INIT_PG_END
;设置页目录项
mov edi,PD_BASE
;0~4M
mov eax,PG0_BASE+INIT_PG_PE_ATTR
mov [edi],eax
mov [edi+4*768],eax
;4M~8M
mov eax,PG1_BASE+INIT_PG_PE_ATTR
mov [edi+4],eax
mov [edi+4*769],eax
mov edi,PG0_BASE ;从PG0_BASE位置填写页表项目,映射0~8MB空间
mov eax,INIT_PG_PE_ATTR
.repeat
mov [edi],eax
add eax,01000h
add edi,4h
.until edi==INIT_PG_END
mov eax,PD_BASE ;也目录表的位置
mov cr3,eax ;设置页表基地址,是物理地址
mov eax,cr0 ;开启分页
or eax,080000000h
mov cr0,eax
;jmp far ptr @f
;@@:
;!!!非常重要,可以立即以线性地址的形式运行
BIG_JUMP32 __KERNEL_CODE,<offset @after_page>
@after_page:
;设置堆栈
mov ax,__KERNEL_DATA
mov ds,ax
mov es,ax ;es 要及时更新为与ds相同的值,在C语言中的数据传输等功能都会用到
mov ss,ax
mov esp,INIT_TASK_UNION+INIT_STACK_SIZE
invoke disp_anyting,addr note
invoke setup_idt
;初始化,eflags
mov eax,0h
push eax
popfd
xor eax,eax
lldt ax
sgdt GDT_TABLE_ADDR
mov eax,GDT_TABLE_ADDR.Base
mov gdt_table,eax
lgdt GDT_TABLE_ADDR
lidt IDT_TABLE_ADDR
;test idt是否设置成功
;mov edi,0C8000000h
;mov eax,0h
;mov [edi],eax
;进入C代码内核设置阶段
invoke start_kernel
@@:jmp @b
kmain ENDP
disp_anyting PROC NEAR stdcall,straddr:DWORD
pushad
xor edi,edi
xor ax,ax
mov ah,9h
shl ah,4
or ah,4h
mov esi,straddr
mov edi,CONSOLE_BASE
.while BYTE PTR ds:[esi]!=0h
mov al,BYTE PTR ds:[esi]
mov WORD PTR es:[edi],ax
inc si
add di,2
.endw
popad
ret ;这个ret指令是必须的,ml 中要增加/Gz 选项
disp_anyting ENDP
ignore_int PROC NEAR stdcall
cld
push eax
push ecx
push edx
push es
push ds
mov ax,__KERNEL_DATA
mov ds,ax
mov es,ax
invoke disp_anyting,addr int_msg
pop ds
pop es
pop edx
pop ecx
pop eax
;pop eax;这里模拟,保护异常,有错误码,需先弹出
;hlt
iretd ;之前是iret
ignore_int ENDP
set_gate PROC NEAR stdcall,gate_addr:DWORD,gtype:BYTE,dpl:BYTE,faddr:DWORD
push edx
push eax
push edi
mov eax,gate_addr
mov edi,eax
mov edx,faddr
mov eax,__KERNEL_CODE
shl eax,16
mov ax,dx
mov dl,0
mov dl,dpl
shl dx,5
add dl,gtype
shl dx,8
add dx,8000h
mov [edi],eax
mov [edi+4],edx
pop edi
pop eax
pop edx
ret
set_gate ENDP
setup_idt PROC NEAR stdcall
;设置中断描述符
;edx高16位为过程入口点偏移值高16位
lea edx,ignore_int
;eax高16位为段描述符
mov eax,__KERNEL_CODE
shl eax,16
;eax低16位为过程入口点偏移值低16位
mov ax,dx
;edx低16位为属性值,高4位:P(1bit)=1,DPL(2bit)=0,e(5bit)=中断门,低4位为0
mov dx,08e00h
sidt IDT_TABLE_ADDR
mov edi,IDT_TABLE_ADDR.Base
mov idt_table,edi
mov ecx,0
.while ecx<256
;mov [edi],eax
;mov [edi+4],edx
;test set_gate
invoke set_gate,edi,0eh,0h,addr ignore_int
add edi,8
inc ecx
.endw
ret
setup_idt ENDP
page_fault PROC NEAR stdcall
push do_page_fault
jmp error_code
page_fault ENDP
error_code:
push ds
push eax
xor eax,eax
push ebp
push edi
push esi
push edx
dec eax ;eax=-1
push ecx
push ebx
cld
mov cx,es
mov esi,[esp+ORIG_EAX] ;提取错误码
mov edi,[esp+SES] ;提取处理函数
mov [esp+ORIG_EAX],eax ;错误码的位置变成-1
mov [esp+SES],cx ;处理函数的位置变成es的值
mov edx,esp ;edx 存放栈顶指针
push esi ;错误码压栈
push edx ;寄存器组指针压栈
mov ax,__KERNEL_DATA
mov ds,ax
mov es,dx ;
GET_CURRENT
call edi
jmp ret_from_exception
ret_from_exception:
ret_from_intr:
GET_CURRENT
;判断进入中断时CPU的模式,及特权级
;mov eax,[esp+EFLAGS]
;mov al,[esp+SCS] ;eflags 与 cs 混合
;test eax,VM_MASK OR 3
;jne ret_with_reschedule
jmp restore_all
restore_all:
RESTORE_ALL_M
common_interrupt:
SAVE_ALL_M
;构造一个CALL指令的调用
push esp ;do_IRQ 指向pt_regs指针,自己添加
push ret_from_intr
call_do_IRQ:
jmp do_IRQ
divide_error PROC NEAR stdcall
push 0 ;造一个错误码
push do_divide_error
jmp error_code
divide_error endp
debug PROC NEAR stdcall
push 0 ;造一个错误码
push do_debug
jmp error_code
debug endp
nmi PROC NEAR stdcall
push 0 ;造一个错误码
push do_nmi
RESTORE_ALL_M
nmi endp
int3 PROC NEAR stdcall
push 0 ;造一个错误码
push do_int3
jmp error_code
int3 endp
overflow PROC NEAR stdcall
push 0 ;造一个错误码
push do_overflow
jmp error_code
overflow endp
bounds PROC NEAR stdcall
push 0 ;造一个错误码
push do_bounds
jmp error_code
bounds endp
invalid_op PROC NEAR stdcall
push 0 ;造一个错误码
push do_invalid_op
jmp error_code
invalid_op endp
device_not_available PROC NEAR stdcall
device_not_available endp
double_fault PROC NEAR stdcall
double_fault endp
coprocessor_segment_overrun PROC NEAR stdcall
push 0 ;造一个错误码
push do_coprocessor_segment_overrun
jmp error_code
coprocessor_segment_overrun endp
invalid_TSS PROC NEAR stdcall
push do_invalid_TSS
jmp error_code
invalid_TSS endp
segment_not_present PROC NEAR stdcall
push do_segment_not_present
jmp error_code
segment_not_present endp
stack_segment PROC NEAR stdcall
push do_stack_segment
jmp error_code
stack_segment endp
general_protection PROC NEAR stdcall
push do_general_protection
jmp error_code
general_protection endp
coprocessor_error PROC NEAR stdcall
push 0 ;造一个错误码
push do_coprocessor_error
jmp error_code
coprocessor_error endp
simd_coprocessor_error PROC NEAR stdcall
push 0 ;造一个错误码
push do_simd_coprocessor_error
jmp error_code
simd_coprocessor_error endp
alignment_check PROC NEAR stdcall
push do_alignment_check
jmp error_code
alignment_check endp
spurious_interrupt_bug PROC NEAR stdcall
push 0 ;造一个错误码
push do_spurious_interrupt_bug
jmp error_code
spurious_interrupt_bug endp
machine_check PROC NEAR stdcall
push 0 ;造一个错误码
push do_machine_check
jmp error_code
machine_check endp
system_call proc NEAR stdcall
push eax ;保存eax
SAVE_ALL_M
GET_CURRENT
.if eax < NR_SYSCALLS
call [sys_call_table+eax*4]
mov [esp+SEAX],eax
.elseif
jmp badsys
.endif
system_call endp
ret_from_sys_call:
RESTORE_ALL_M
badsys:
mov eax,-1
mov [esp+SEAX],eax
jmp ret_from_sys_call
inter_get_current proc NEAR stdcall
GET_CURRENT
mov eax,ebx
ret
inter_get_current endp
outb proc NEAR stdcall,value:BYTE,port:WORD
push ax
push dx
mov al,value
mov dx,port
out dx,al
pop dx
pop ax
ret
outb endp
outb_p proc NEAR stdcall,value:BYTE,port:WORD
push ax
push dx
mov al,value
mov dx,port
out dx,al
jmp @f
@@:
jmp @f
@@:
jmp @f
@@:
jmp @f
@@:
pop dx
pop ax
ret
outb_p endp
IRQ0x00_interrupt proc NEAR stdcall
push 0-256
jmp common_interrupt
IRQ0x00_interrupt endp
IRQ0x01_interrupt proc NEAR stdcall
push 1-256
jmp common_interrupt
IRQ0x01_interrupt endp
IRQ0x02_interrupt proc NEAR stdcall
push 2-256
jmp common_interrupt
IRQ0x02_interrupt endp
IRQ0x03_interrupt proc NEAR stdcall
push 3-256
jmp common_interrupt
IRQ0x03_interrupt endp
IRQ0x04_interrupt proc NEAR stdcall
push 4-256
jmp common_interrupt
IRQ0x04_interrupt endp
IRQ0x05_interrupt proc NEAR stdcall
push 5-256
jmp common_interrupt
IRQ0x05_interrupt endp
IRQ0x06_interrupt proc NEAR stdcall
push 6-256
jmp common_interrupt
IRQ0x06_interrupt endp
IRQ0x07_interrupt proc NEAR stdcall
push 7-256
jmp common_interrupt
IRQ0x07_interrupt endp
IRQ0x08_interrupt proc NEAR stdcall
push 8-256
jmp common_interrupt
IRQ0x08_interrupt endp
IRQ0x09_interrupt proc NEAR stdcall
push 9-256
jmp common_interrupt
IRQ0x09_interrupt endp
IRQ0x0a_interrupt proc NEAR stdcall
push 0ah-256
jmp common_interrupt
IRQ0x0a_interrupt endp
IRQ0x0b_interrupt proc NEAR stdcall
push 0bh-256
jmp common_interrupt
IRQ0x0b_interrupt endp
IRQ0x0c_interrupt proc NEAR stdcall
push 0ch-256
jmp common_interrupt
IRQ0x0c_interrupt endp
IRQ0x0d_interrupt proc NEAR stdcall
push 0dh-256
jmp common_interrupt
IRQ0x0d_interrupt endp
IRQ0x0e_interrupt proc NEAR stdcall
push 0eh-256
jmp common_interrupt
IRQ0x0e_interrupt endp
IRQ0x0f_interrupt proc NEAR stdcall
push 0fh-256
jmp common_interrupt
IRQ0x0f_interrupt endp
end kmain
评论次数(0) |
浏览次数(352) |
类型(默认类型) |
收藏此文 |