代码拉取完成,页面将自动刷新
org 0x900
jmp CODE16_START
; 段属性的一些定义,暂时定义一些目前用得到的属性
DA_32 equ 0x4000 ; D/B = 1
DA_DR equ 0x90 ; 数据,只读
DA_DRW equ 0x92 ; 数据,可读/写
DA_DRWA equ 0x93 ; 数据,可读/写,已访问
DA_C equ 0x98 ; 代码,仅执行
DA_CR equ 0x9A ; 代码,可执行,可读
DA_CCO equ 0x9C ; 代码,一致性段,仅执行
DA_CCOR equ 0x9E ; 代码,一致性段,可执行,可读,已访问
DA_LDT equ 0x82 ; TYPE : LDT
; 门属性的一些定义
DA_TSS equ 0x89 ; 32 位 TSS (可用)
; 段选择符属性定义
SA_RPL0 equ 0 ; RPL = 0
SA_RPL1 equ 1 ; RPL = 1
SA_RPL2 equ 2 ; RPL = 2
SA_RPL3 equ 3 ; RPL = 3
SA_TIG equ 0 ; TI = 0, GDT
SA_TIL equ 4 ; TI = 1, LDT
DA_DPL0 equ 0x00 ; DPL = 0
DA_DPL1 equ 0x20 ; DPL = 1
DA_DPL2 equ 0x40 ; DPL = 2
DA_DPL3 equ 0x60 ; DPL = 3
; 段描述符定义
%macro Descriptor 3 ; 有三个参数:段基址、段界限、段属性
dw %2 & 0xFFFF ; 段界限 1 (2 字节)
dw %1 & 0xFFFF ; 段基址 1 (2 字节)
db (%1 >> 16) & 0xFF ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 段属性 1 + 段界限 2 + 段属性 2 (2 字节)
db (%1 >> 24) & 0xFF ; 段基址 3 (1 字节)
%endmacro ; 共 8 个字节
; 门描述符定义
%macro Gate 4 ; 有四个参数:选择子、偏移地址、参数个数、属性
dw (%2 & 0xFFFF) ; 偏移地址1
dw %1 ; 选择子
dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00) ; 属性
dw ((%2 >> 16) & 0xFFFF) ; 偏移地址2
%endmacro
; 门属性的一些定义
DA_CALL_GATE equ 0x8C ; 调用门
DA_INTR_GATE equ 0x8E ; 中断门
DA_TRAP_GATE equ 0x8F ; 陷阱门
; 全局描述符表定义
; 段基址 段界限 段属性
GDT_BASE : Descriptor 0, 0, 0
CODE32_DESC : Descriptor 0, CODE32_SEG_LEN - 1, DA_C + DA_32
VIDEO_DESC : Descriptor 0xB8000, 0xBFFFF - 0xB8000, DA_DRWA + DA_32 + DA_DPL3
DATA_DESC : Descriptor 0, DATA_SEG_LEN - 1, DA_DR + DA_32
STACK32_DESC : Descriptor 0, TOP_OF_STACK32, DA_DRW + DA_32
TASK_A_LDT_DESC : Descriptor 0, TASK_A_LDT_LEN - 1, DA_LDT
TSS_DESC : Descriptor 0, TSS_LEN -1, DA_TSS + DA_DPL0
; ...
GDT_LEN equ $ - GDT_BASE ; GDT 长度 = 当前地址 - GDT_BASE 地址
GDT_PTR:
dw GDT_LEN - 1
dd GDT_BASE
; 段选择符定义,RPL = 0; TI = 0
CODE32_SELECTOR equ (0x0001 << 3) + SA_RPL0 + SA_TIG
VIDEO_SELECTOR equ (0x0002 << 3) + SA_RPL0 + SA_TIG
DATA_SELECTOR equ (0x0003 << 3) + SA_RPL0 + SA_TIG
STACK32_SELECTOR equ (0x0004 << 3) + SA_RPL0 + SA_TIG
TASK_A_LDT_SELECTOR equ (0x0005 << 3) + SA_RPL0 + SA_TIG
TSS_SELECTOR equ (0x0006 << 3) + SA_RPL0 + SA_TIG
; IDT 中断描述符表定义
; 选择子, 偏移地址, 参数个数, 属性
IDT_BASE:
%rep 32
Gate CODE32_SELECTOR, DefaultHandler_Offset, 0, DA_INTR_GATE
%endrep
Gate CODE32_SELECTOR, TimerHandler_Offset, 0, DA_INTR_GATE
%rep 95
Gate CODE32_SELECTOR, DefaultHandler_Offset, 0, DA_INTR_GATE
%endrep
Gate CODE32_SELECTOR, Int0x80Handler_Offset, 0, DA_INTR_GATE
%rep 127
Gate CODE32_SELECTOR, DefaultHandler_Offset, 0, DA_INTR_GATE
%endrep
IDT_LEN equ $ - IDT_BASE
IDT_PTR :
dw IDT_LEN - 1
dd IDT_BASE
msg db "Loader..." ; 使用 db 定义数据
msgLen equ $-msg ; 字符串 msg 长度
[bits 16]
CODE16_START :
xor ax, ax ; xor 指令与 and or 指令类似,两个操作数的每一对对应位都应用如下操作原则:
; 如果两个位的值相同(同为 0 或同为 1),则结果位等于 0;否则结果位等于 1
; xor ax, ax 等于 ax = 0
mov ss, ax ; 栈段 ss = 0
mov ds, ax ; 数据段 ds = 0
mov es, ax ; 附加数据段 es = 0
mov sp, 0x900 ; 设置栈,跟 0x7c00 一样,0x900 地址以下也有一段安全可用内存
; 设置光标位置 (dl, dh)
mov ah, 0x02 ; AH 功能号 = 0x02,设置光标
mov dh, 0x01 ; 设置光标行号
mov dl, 0x00 ; 设置光标列号
mov bh, 0x00 ; 页码
int 0x10
; 打印 msg
mov ax, msg
mov cx, msgLen
call print
; 初始化段描述符中的段基址
mov esi, CODE32_START
mov edi, CODE32_DESC
call InitDescItem
mov esi, DATA_SEGMENT
mov edi, DATA_DESC
call InitDescItem
mov esi, STACK32_SEGMENT
mov edi, STACK32_DESC
call InitDescItem
mov esi, TSS_SEGMENT
mov edi, TSS_DESC
call InitDescItem
mov esi, TASK_A_LDT_BASE
mov edi, TASK_A_LDT_DESC
call InitDescItem
mov esi, TASK_A_CODE32_SEGMENT
mov edi, TASK_A_CODE32_DESC
call InitDescItem
mov esi, TASK_A_DATA32_SEGMENT
mov edi, TASK_A_DATA32_DESC
call InitDescItem
mov esi, TASK_A_STACK32_SEGMENT
mov edi, TASK_A_STACK32_DESC
call InitDescItem
; 打开 A20 地址线
in al, 0x92
or al, 0000_0010B
out 0x92, al
; 加载描述符表
;mov eax, 0
;mov ax, ds
;shl eax, 4
;add eax, GDT_BASE
;mov dword [GDT_PTR + 2], eax
lgdt [GDT_PTR]
lidt [IDT_PTR]
; 通知 CPU 进入保护模式,即将 CR0 寄存器的 PE(bit0) 位置 1
mov eax, cr0
or eax, 0x01
mov cr0, eax
; 刷新流水线
; 跳转到 32 位代码段继续执行
jmp dword CODE32_SELECTOR:0
; 参数: esi --> 代码段标签
; 参数: edi --> 段描述符标签
InitDescItem:
push eax
mov eax, 0
mov ax, cs
shl eax, 4
add eax, esi
mov word [edi + 2], ax
shr eax, 16
mov byte [edi + 4], al
mov byte [edi + 7], ah
pop eax
ret
; 打印字符串
; ax : 输入参数,字符串地址
; cx : 输入参数,字符串长度
print:
; 入栈
push ax
push bp
push bx
mov bp, ax ; 保存字符串地址
mov ax, 0x1301 ; 子功能号 0x13 是写字符串
mov bx, 000fh ; 页号位 0,黑底白字
int 0x10
; 出栈
pop bx
pop bp
pop ax
ret
[bits 32]
CODE32_START :
; 设置栈寄存器
mov ax, STACK32_SELECTOR
mov ss, ax
mov eax, TOP_OF_STACK32
mov esp, eax
; 设置显存段
mov ax, VIDEO_SELECTOR
mov gs, ax
; 设置数据段
mov ax, DATA_SELECTOR
mov ds, ax
mov ebp, msg2Offset
mov bl, 0x0F ; 打印属性,黑底白字
; 坐标 (0, 2)
mov dl, 0x00
mov dh, 0x02
call print_str_32
; int 0x80
call pic_init
; call EnableTimer
; mov al, '0' ; 默认从 '0' 开始打印
; sti
; 加载任务状态段 TSS
mov ax, TSS_SELECTOR
ltr ax
; 加载局部段描述符表
mov ax, TASK_A_LDT_SELECTOR
lldt ax
push TASK_A_STACK32_SELECTOR
push TASK_A_TOP_OF_STACK32
push TASK_A_CODE32_SELECTOR
push 0
retf
jmp $
; 使用显存方式打印字符串
; ds:ebp --> 打印的数据起始地址(相对于段基址的偏移地址)
; bl --> 打印属性
; dx --> 打印起始坐标 (dl, dh)
print_str_32:
push ebp
push eax
push edi
push cx
push dx
; 循环
s:
mov cl, [ds:ebp] ; 取地址 ds:ebp 中的数据存到 cl 寄存器
cmp cl, 0 ; 比较 cl 是否为 0
je end ; 若为 0 ,就结束打印
; 根据坐标 (dl, dh) 计算出偏移量,存入 eax 中
; (80 * dh + dl)*2 ; 每行最多显示 80 个字符
mov eax, 80
mul dh
add al, dl
shl eax, 1 ; eax = eax*2
mov edi, eax ; edi :显存中的偏移量
mov ah, bl ; 显示属性
mov al, cl ; 要打印的字符
mov [gs:edi], ax ; 显示数据放入显存
inc ebp ; 自增
inc dl ; 自增
jmp s ; 循环
; 打印结束
end:
pop dx
pop cx
pop edi
pop eax
pop ebp
ret
DefaultHandler:
iret
DefaultHandler_Offset equ DefaultHandler-CODE32_START
Int0x80Handler:
mov ebp, msg_int0x80_offset
mov bl, 0x0F ; 打印属性,黑底白字
; 坐标 (0, 3)
mov dl, 0x00
mov dh, 0x03
call print_str_32
iret
Int0x80Handler_Offset equ Int0x80Handler-CODE32_START
TimerHandler:
sti
cmp al, '9' ; 判断 al 是否为 '9'
je .to_0 ; 若果为 al=9,则跳转到 .to_0 处执行
inc al
jmp .display ; 跳转到 .display 处执行
.to_0:
mov al, '0' ; al = '0'
.display: ; 使用现存方式打印,al 为要打印的字符,ah 为打印属性
mov ah, 0x0F ; 打印属性,黑底白字
mov [gs:(80*4+0)*2], ax ; (80 * 4 + 0)*2 ; 坐标 (0, 4)
call write_m_EOI ; 手动结束中断
iret
TimerHandler_Offset equ TimerHandler-CODE32_START
; 初始化可编程中断控制器 8259A - 级联
pic_init:
push ax
.8259a_m_init: ; 8259A 主片初始化
mov al, 0x11 ; ICW1:边沿触发,级联,需要 ICW4
out 0x20, al
mov al, 0x20 ; ICW2:起始中断向量号为 0x20
out 0x21, al
mov al, 0x04 ; ICW3:IRQ2 接从片
out 0x21, al
mov al, 0x11 ; ICW4:8086模式,正常 EOI,非缓冲模式,特殊全嵌套模式
out 0x21, al
.8259a_s_init: ; 8259A 从片初始化
mov al, 0x11 ; ICW1:边沿触发,级联,需要 ICW4
out 0xA0, al
mov al, 0x28 ; ICW2:起始中断向量号为 0x28
out 0xA1, al
mov al, 0x02 ; ICW3:设置从片连接到主片 IRQ2 引脚
out 0xA1, al
mov al, 0x01 ; ICW4:8086模式,正常 EOI,非缓冲模式,全嵌套模式
out 0xA1, al
pop ax
ret
; 手动结束主片中断
write_m_EOI:
push ax
mov al, 0x20 ; OCW2:固定优先级方式,结束中断,清 ISR 相应位
out 0x20, al ; 端口号:0x20
pop ax
ret
; 手动结束从片中断
write_s_EOI:
push ax
mov al, 0x20 ; OCW2:固定优先级方式,结束中断,清 ISR 相应位
out 0xA0, al ; 端口号:0xA0
pop ax
ret
; 读主片 ISR 寄存器的值,返回值存入 al 寄存器
read_m_ISR:
mov al, 0x0B ; OCW3:RR=1,RIS=1,读取 ISR 寄存器的值
out 0x20, al ; 端口号:0x20
jmp $+2 ; jmp 指令占 2 个字节,$ 表示当前位置,这里就是起延时作用,给与端口处理时间
in al, 0x20 ; 端口号:0x20
ret
; 读从片 ISR 寄存器的值,返回值存入 al 寄存器
read_s_ISR:
mov al, 0x0B ; OCW3:RR=1,RIS=1,读取 ISR 寄存器的值
out 0xA0, al ; 端口号:0xA0
jmp $+2 ; jmp 指令占 2 个字节,$ 表示当前位置,这里就是起延时作用,给与端口处理时间
in al, 0xA0 ; 端口号:0xA0
ret
; 读主片 IRR 寄存器的值,返回值存入 al 寄存器
read_m_IRR:
mov al, 0x0A ; OCW3:RR=1,RIS=0,读取 IRR 寄存器的值
out 0x20, al ; 端口号:0x20
jmp $+2 ; jmp 指令占 2 个字节,$ 表示当前位置,这里起延时作用,给与端口处理时间
in al, 0x20 ; 端口号:0x20
ret
; 读从片 IRR 寄存器的值,返回值存入 al 寄存器
read_s_IRR:
mov al, 0x0A ; OCW3:RR=1,RIS=0,读取 IRR 寄存器的值
out 0xA0, al ; 端口号:0xA0
jmp $+2 ; jmp 指令占 2 个字节,$ 表示当前位置,这里起延时作用,给与端口处理时间
in al, 0xA0 ; 端口号:0xA0
ret
; 读主片 IMR 寄存器的值,返回值存入 al 寄存器
read_m_IMR:
in al, 0x21 ; 端口号:0x21
ret
; 将 al 寄存器的值写入主片 IMR 中
write_m_IMR:
out 0x21, al ; 端口号:0x21
ret
; 读从片 IMR 寄存器的值,返回值存入 al 寄存器
read_s_IMR:
in al, 0xA1 ; 端口号:0xA1
ret
; 将 al 寄存器的值写入从片 IMR 中
write_s_IMR:
out 0xA1, al ; 端口号:0xA1
ret
; 设置主片工作在特殊屏蔽模式
set_m_smm:
push ax
mov al, 0x68 ; ESMM=1,SMM=1
out 0x20, al ; 端口号:0x20
pop ax
ret
EnableTimer:
push ax
call read_m_IMR
and al, 0xFE
call write_m_IMR
pop ax
ret
CODE32_SEG_LEN equ $-CODE32_START
DATA_SEGMENT:
msg2 db "Enter protection", 0 ; 以 0 为字符串结束标志
msg2Offset equ msg2 - DATA_SEGMENT ; msg2 在数据段 DATA_SEGMENT 中的偏移量
msg_int0x80 db "int 0x80", 0 ; 以 0 为字符串结束标志
msg_int0x80_offset equ msg_int0x80 - DATA_SEGMENT ; msg_int0x80 在数据段 DATA_SEGMENT 中的偏移量
DATA_SEG_LEN equ $ - DATA_SEGMENT
STACK32_SEGMENT:
times 1024 * 4 db 0 ; 开辟 4K 的内存空间当做栈
STACK32_SEG_LEN equ $ - STACK32_SEGMENT
TOP_OF_STACK32 equ STACK32_SEG_LEN - 1 ; 栈顶
TSS_SEGMENT:
dd 0
dd TOP_OF_STACK32 ; esp0
dd STACK32_SELECTOR ; ss0
dd 0 ; esp1
dd 0 ; ss1
dd 0 ; esp2
dd 0 ; ss2
times 4 * 18 dd 0
dw 0
dw $ - TSS_SEGMENT + 2
db 0xFF
TSS_LEN equ $ - TSS_SEGMENT
; ======================================
; Task A
; ======================================
; Task A 局部段描述符表定义
; 段基址 段界限 段属性
TASK_A_LDT_BASE :
TASK_A_CODE32_DESC : Descriptor 0, TASK_A_CODE32_SEG_LEN - 1, DA_C + DA_32 + DA_DPL3
TASK_A_DATA32_DESC : Descriptor 0, TASK_A_DATA32_SEG_LEN - 1, DA_DR + DA_32 + DA_DPL3
TASK_A_STACK32_DESC : Descriptor 0, TASK_A_STACK32_SEG_LEN - 1, DA_DRW + DA_32 + DA_DPL3
; ...
TASK_A_LDT_LEN equ $ - TASK_A_LDT_BASE ; 长度 = 当前地址 - TASK_A_LDT_BASE 地址
; Task A 局部段选择符定义,RPL = 3; TI = 1
TASK_A_CODE32_SELECTOR equ (0x0000 << 3) + SA_RPL3 + SA_TIL
TASK_A_DATA32_SELECTOR equ (0x0001 << 3) + SA_RPL3 + SA_TIL
TASK_A_STACK32_SELECTOR equ (0x0002 << 3) + SA_RPL3 + SA_TIL
TASK_A_DATA32_SEGMENT:
TASK_A_STRING db "Task A", 0
TASK_A_STRING_OFFSET equ TASK_A_STRING - TASK_A_DATA32_SEGMENT ; 相对于段基址的偏移地址
TASK_A_DATA32_SEG_LEN equ $ - TASK_A_DATA32_SEGMENT
TASK_A_STACK32_SEGMENT:
times 1024 db 0
TASK_A_STACK32_SEG_LEN equ $ - TASK_A_STACK32_SEGMENT
TASK_A_TOP_OF_STACK32 equ TASK_A_STACK32_SEG_LEN - 1
TASK_A_CODE32_SEGMENT:
mov ax, VIDEO_SELECTOR
mov gs, ax
mov ax, TASK_A_STACK32_SELECTOR
mov ss, ax
mov eax, TASK_A_TOP_OF_STACK32
mov esp, eax
mov ax, TASK_A_DATA32_SELECTOR
mov ds, ax
mov ebp, TASK_A_STRING_OFFSET
mov bl, 0x0F
mov dh, 0x03
mov dl, 0x00
; call TaskAPrintString
; call FUNC_PRINT_SELECTOR : 0
; call FUNC_SELECTOR : printOffset
mov ebp, TASK_A_STRING_OFFSET
mov bl, 0x0F ; 打印属性,黑底白字
; 坐标 (0, 3)
mov dl, 0x00
mov dh, 0x03
call TASK_A_print_str
jmp $
; 使用显存方式打印字符串
; ds:ebp --> 打印的数据起始地址(相对于段基址的偏移地址)
; bl --> 打印属性
; dx --> 打印起始坐标 (dl, dh)
TASK_A_print_str:
push ebp
push eax
push edi
push cx
push dx
; 循环
.task_a_s:
mov cl, [ds:ebp] ; 取地址 ds:ebp 中的数据存到 cl 寄存器
cmp cl, 0 ; 比较 cl 是否为 0
je .task_a_end ; 若为 0 ,就结束打印
; 根据坐标 (dl, dh) 计算出偏移量,存入 eax 中
; (80 * dh + dl)*2 ; 每行最多显示 80 个字符
mov eax, 80
mul dh
add al, dl
shl eax, 1 ; eax = eax*2
mov edi, eax ; edi :显存中的偏移量
mov ah, bl ; 显示属性
mov al, cl ; 要打印的字符
mov [gs:edi], ax ; 显示数据放入显存
inc ebp ; 自增
inc dl ; 自增
jmp .task_a_s ; 循环
; 打印结束
.task_a_end:
pop dx
pop cx
pop edi
pop eax
pop ebp
ret
TASK_A_printOffset equ TASK_A_print_str - TASK_A_CODE32_SEGMENT
TASK_A_CODE32_SEG_LEN equ $ - TASK_A_CODE32_SEGMENT
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。