本节主要讲解如何利用INT表显示一些信息.INT表中文名为中断表,在上节中利用int 15h求出了系统内存的信息,这就是一个中断程序.INT表中存储的是中断向量,我们可以自定义中断和中断向量表,然后利用int 指令显示自己想要的信息.
1.构建pm.inc文件,中断向量显示时需要的一些属性
DA_LDT EQU 82h;局部描述符表段类型值
DA_32 EQU 4000h ;32位段
DA_LIMIT_4K EQU 8000h ; 段界限粒度为 4K 字节
DA_DRW EQU 92h ; 存在的可读写数据段属性值
DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值
DA_C EQU 98h ;存在的只执行代码段属性
DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值
DA_DPL0 EQU 10h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
DA_386CGate EQU 8Ch ; 386 调用门类型值
DA_386IGate EQU 8Eh ; 386 中断门类型值
;----------------------------------------------------------------------------
; 分页机制使用的常量说明
;----------------------------------------------------------------------------
PG_P EQU 1 ; 页存在属性位
PG_RWW EQU 2 ; R/W 属性位值, 读/写/执行
PG_USU EQU 4 ; U/S 属性位值, 用户级
SA_RPL0 EQU 0h ;RPL = 0
SA_RPL1 EQU 1h ;RPL = 1
SA_RPL2 EQU 2h ;RPL = 2
SA_RPL3 EQU 3h ;RPL = 3
SA_TIG EQU 0 ; ┓TI
SA_TIL EQU 4 ; ┛
;GDT描述符定义,传进来的参数会自动装到对应的字节中
;usage:Descriptor Base,Limit,Attr
%macro Descriptor 3
dw %2&0FFFFh ;段界限1
dw %1&0FFFFh ;段基址1
db (%1>>16)&0FFh ;段基址2
dw ((%2>>8)&0F00h)|(%3&0F0FFh) ;属性1 + 段界限2 + 属性2
db (%1>>24)&0FFh ;段基址3
%endmacro ;共8个字节
; 门
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子 (2 字节)
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 (2 字节)
dw ((%2 >> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 共 8 字节
2.构建pmint.asm文件,主要利用中断在屏幕第一行先显示一个字符"!",然后显示一个字符"I",接着打开可屏蔽中断,利用时钟可屏蔽中断让后一个字符动态变化
%include "pm.inc" ;常量,宏,以及一些说明
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
; GDT 段基址, 段界限, 段属性
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_CR+DA_32 ; 可读执行代码, 32
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32 ;32位Stack
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
;GDT 结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen - 1 ;GDT界限
dd 0 ;GDT基地址
; GDT选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
;END of SECTION .gdt
;IDT
[SECTION .idt]
ALIGN 32
[BITS 32]
LABEL_IDT:
;门: 目标段选择子, 偏移,DCount,属性
%rep 32
Gate SelectorCode32,SupriousHandler, 0, DA_386IGate
%endrep
.020h: Gate SelectorCode32, ClockHandler, 0, DA_386IGate
%rep 95
Gate SelectorCode32,SupriousHandler, 0, DA_386IGate
%endrep
.080h: Gate SelectorCode32, UserIntHandler, 0, DA_386IGate
IdtLen equ $ - LABEL_IDT
IdtPtr dw IdtLen - 1 ;段界限
dd 0 ;idt段基地址
;End of section .idt
;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $ - LABEL_STACK - 1
;END of [SECTION .gs]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp,0100h
; 初始化 32 位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah
; 初始化堆栈段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DESC_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK + 4], al
mov byte [LABEL_DESC_STACK + 7], ah
; 为加载 GDTR 作准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
;为加载IDTR做准备
xor eax,eax
mov ax, ds
shl eax,4
add eax,LABEL_IDT
mov dword [IdtPtr + 2], eax;
; 加载 GDTR
lgdt [GdtPtr]
; 关中断
cli
;加载IDTR
lidt [IdtPtr]
; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al
; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
; 真正进入保护模式
jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorStack ;堆栈段选择子
mov ss, ax
mov esp, TopOfStack
call Init8259A
int 021h ;显示一个"!"
int 080h ;显示一个动态的字符
sti ;利用时钟中断显示一个动态的字符
jmp $
;Init8259A------
Init8259A:
mov al,011h
out 020h,al ;主8259,ICW1
call io_delay
out 0A0h,al ;从8259,ICW1
call io_delay
mov al,020h ;IRQ0对应中断向量0x20
out 021h,al ;主8259,ICW2
call io_delay
mov al,028h ;IRQ8对应中断向量0x28
out 0A1h,al ;从8259,ICW2
call io_delay
mov al,004h ;IRQ2对应从8259
out 021h,al ;从8259,ICW3
call io_delay
mov al,002h ;对应主8259的IR2
out 0A1h,al ;从8259,ICW3
call io_delay
mov al, 001h
out 021h,al ;主8259,ICW4
call io_delay
out 0A1h,al ;从8259,ICW4
call io_delay
mov al, 11111110b ;仅开启定时器中断
out 021h, al ;主8259, OCW1
call io_delay
mov al, 11111111b ;屏蔽从8259所有中断
out 0A1h,al ;从8259,OCW1
call io_delay
ret
;Init8259A结束---------
io_delay:
nop
nop
nop
nop
ret
;int handler
_ClockHandler:
ClockHandler equ _ClockHandler - $$
inc byte [gs: (80*0+70)*2] ;屏幕第0行
mov al, 20h
out 20h,al ;发送EOI
iretd
_UserIntHandler:
UserIntHandler equ _UserIntHandler - $$
mov ah,0Ch
mov al,'I'
mov [gs:((80*0+70)*2)], ax
iretd
_SupriousHandler:
SupriousHandler equ _SupriousHandler - $$
mov ah,0ch
mov al,'!'
mov [gs:((80*0+75)*2)],ax ;屏幕第0行,第75列
iretd
;--------
SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
3.构建MakeFileLDT文件用来编译程序
##################################################
# Makefile of pmtestx.asm (x=[1,2,3...])
##################################################
SRC:=pmint.asm
BIN:=$(subst .asm,.com,$(SRC)) #${SRC}中的.asm会替换成.com
#此句等价于pmprotect2real.com
.PHONY : everything
everything : $(BIN)
sudo mount -o loop pm.img /mnt/floppy/
sudo cp $(BIN) /mnt/floppy/ -fv
sudo umount /mnt/floppy/
$(BIN) : $(SRC)
nasm $< -o $@
4.执行make -f MakeFileLDT编译程序,调用命令bochs -f bochsrcdos执行程序得到下面的效果
源码地址:https://github.com/Foolegend/aos/tree/master/chapter03/INT