仅在16位上有效,给别人写的.
作用:模拟如何加载一个APP
实际还要检索内存空间,这里省去
第2个被加载程序,修改了中断
加载器:
;读取硬盘端口 [读取硬盘这部分可直接忽略]
;0x1f0 数据端口
;0x1f1 错误端口
;0x1f2 设置扇区数量
;0x1f3 ~ 0x1f6 [ 0x1f6 中只占4 bit] 共28bit
;0x1f6 高4位 1110 , 111 -> LBA, 0 -> 主盘
;0x1f7 状态控制端口
; 写入 0x20 -> 准备读取,
; 读取后 and 0x88 , 如果等于 0x08则说明可以读取,否则等待
;这部分汇编可以忽略 , 具体看read_file
;被加载程序,从0x10000开始加载,所有段按16字节对齐
;被加载程序头部相当于简化版 PE/elf头
; read_file 读取硬盘,加载到内存
; read_header 读取首个扇区,来确定有N个扇区要读
; 当被加载程序全部加载完后, 需要确认所有段的段地址(+0x10000), 否则无法跳转至入口点(aop)
; 最后跳转至程序的入口开始执行被加载程序
APP_SECTOR_START_HIGH equ 0
APP_SECTOR_START_LOW equ 100 ;扇区起始位置
; vstart=0x7c00 , 标号从0x7c00 开始算
section app_load align=16 vstart=0x7c00
jmp far [section_addr]
section_addr dw start, section.app_load.start
app_load_addr dd 0x10000
start:
mov ax,cs
mov ds,ax
mov ax,[app_load_addr]
mov dx,[app_load_addr+2]
mov cx,16
div cx
mov ds,ax ; 把程序加载到这个位置
xor ax,ax
mov ss,ax
mov sp,ax
;读取头
call read_header
;读取首个512字节的头,获取假定的PE头
mov dx,[ds:2]
mov ax,[ds:0]
mov bx,512
div bx
cmp dx,0
jne check_1
; ax 剩余扇区
dec ax
; 检查是否读完, 或加载剩余扇区
check_1:
cmp ax,0
je process
mov cx,ax ; 剩余扇区
xor ax,ax
push ds
;读取剩余扇区
read_left_sector:
; 每次读取512字节, ds段地址每次加512 ( 0x20 << 4 )
mov dx,ds
add dx,0x20
mov ds, dx
; 读取 扇区+1 的位置
xor dx,dx
inc ax
add ax,APP_SECTOR_START_LOW
adc dx,APP_SECTOR_START_HIGH
push dx
push ax
call read_file
add sp,4
loop read_left_sector
pop ds
;到此剩余扇区全部读完, 处理段地址
process:
; 处理入口 , aop
mov dx,[ds:0x08]
mov ax,[ds:0x06]
push dx
push ax
call calc_each_segment_addr
add sp,4
; 写入aop段地址
mov dword [ds:0x06], 0
mov [ds:0x6],ax
;获取N个重定位表,以及首个重定位表的地址
mov cx,[ds:0xa]
mov si,0x0c
;处理其他段地址
process_left:
mov dx,[si+0x2]
mov ax,[si]
push dx
push ax
call calc_each_segment_addr
add sp,4
mov dword [ds:si],0
mov [ds:si],ax
add si,4
loop process_left
;转到被加载的APP中, ds:0x4 获取被加载的段地址:偏移
push ds
push dx
push cx
call far [ds:0x04]
pop cx
pop dx
pop ds
done:
jmp done
;重新计算生成16位段地址 参数: [bp+6] 高位, [bp+4]低位 , ax 返回值
calc_each_segment_addr:
push bp
mov bp, sp
; 重新计算被加载的段地址,ax低位相加,进位加到dx, 形成20位段地址
mov ax,[bp+4]
mov dx,[bp+6]
add ax,[cs:app_load_addr]
adc dx,[cs:app_load_addr+2]
shr ax,4 ; >> 4 生成16位段地址
shl dx,12 ; 高位只保留4位
and dx,0xf000
or ax,dx
mov sp,bp
pop bp
ret
read_header:
push APP_SECTOR_START_HIGH
push APP_SECTOR_START_LOW
call read_file
add sp,4
ret
;读取程序 [bp+2] -> ip , [bp+4] -> 低16位扇区, [bp+6] -> 高16 扇区
read_file:
push bp
mov bp, sp
push ax
push bx
push cx
push dx
xor bx,bx ; 内存索引
; 设置每次读一个扇区
mov dx,0x1f2
mov al,1
out dx,al
;dx=0x1f3
inc dx
mov ax,[bp+2]
out dx,al
;dx 0x1f4
inc dx
mov al,ah
out dx,al
;dx 0x1f5
inc dx
mov ax,[bp+4]
out dx,al
;dx 0x1f6
inc dx
mov al,0xe0
and ah,0x0f
or al,ah ; 处理最后4位
out dx,al
;设置读, dx 0x1f7
inc dx
mov al,0x20
out dx,al
;开始等待硬盘响应,是否准备好
waiting_read:
in al,dx
and al,0x88
cmp al,0x08
jne waiting_read
; 一次读一个扇区 256 * 2字节
mov cx,256
;设置读取端口
mov dx,0x1f0
; bx 为数据段索引
begin_read:
in ax,dx
mov [ds:bx],ax
add bx,2
loop begin_read
pop dx
pop cx
pop bx
pop ax
pop bp
ret
times 510-($-$$) db 0
dw 0xaa55
被加载程序 :
; 被加载程序,从0x10000开始加载
;header段地址: 0x10000
section header vstart=0
;app字节
app_len dd app_end ; [0]
;入口
entry dw start ;[0x4]
dd section.code_main.start ;
;每个段重定位表长
table_len dd (header_end-code_segment_main) /4 ;[0xa]
;函数段地址
code_segment_main dd section.code_main.start ; [0xc]
;函数段地址
code_segment_func dd section.code_func.start
;数据段地址
data1 dd section.data1.start
;数据段地址
data2 dd section.data2.start
;stack段地址
stack_segment dd section.stack.start
;header段地址
header_segment dd section.header.start
header_end:
;定义stack
section stack align=16 vstart=0
resb 128 ; times 128 db ?
stack_end:
;加载器最后一条指令 : call far [ds:0x04]
;本程序其他段寄存器需要重置,否则还指向加载程序.
section code_main align=16 vstart=0
start:
;保存加载器的ss,sp,用于返回
mov dx,ss
mov cx,sp
mov ax,[ds:header_segment] ;es 指向header段, [1000:header_segment]
mov es,ax
mov ax,[es:stack_segment]
mov ss,ax
mov sp,stack_end
;保存原来的 ss,sp 在自己stack
push dx
push cx
mov ax,[data1]
mov ds,ax
;retf => pop ip, pop cs
; 相当于 call far ( dw init_start, code_segment_func )
push word [es:code_segment_func]
mov ax,init_start
push ax
retf
done:
;返回到加载器
pop cx
pop dx
mov ss,dx
mov sp,cx
retf
section code_func align=16 vstart=0
init_start:
push bp
mov bp,sp
push word [es:code_segment_main]
push word [es:code_segment_func]
push word [es:data1]
push word [es:data2]
push word [es:stack_segment]
push word [es:header_segment]
mov sp,bp
pop bp
push word [es:code_segment_main]
mov ax,done
push ax
retf
section data1 align=16 vstart=0
msg1 db 'fuck',0
section data2 align=16 vstart=0
msg2 db 'you',0x0d,0x0a,0
section tail align=16
app_end:第二个被加载程序:
section header vstart=0
app_len dd app_end ;[0x0]
entry dw start
dd section.code_section.start ;[0x04]
;表个数
table_len dw (header_end - code_segment) / 4 ;[0x0a]
; 重定位表
code_segment dd section.code_section.start
data_segment dd section.data_section.start
stack_segment dd section.stack_section.start
header_segment dd section.header.start
header_end:
section code_section vstart=0 align=16
start:
;保留加载器ss,sp
mov dx,ss
mov cx,sp
; es 指向 header段
mov ax,[ds:header_segment]
mov es,ax
; 初始化stack
mov ax,[es:stack_segment]
mov ss,ax
mov sp,stack_section_end
; 初始化ds
mov ax,[es:data_segment]
mov ds,ax
; 把加载器ss,sp压入stack
push dx
push cx
;保存原来的0x70入口
mov ax,0
mov es,ax
push word [es:0x70 * 4]
pop word [ds:addr_off]
push word [es:0x70 * 4 + 2]
pop word [ds:addr_seg]
;设置新入口
cli
mov word [es:0x70 * 4], new_int_func
mov word [es:0x70 * 4 + 2], cs
;设置RTC 周期每秒一次中断, 一秒一次中断 , 这段直接照抄,很麻烦
mov al,0x0b ;RTC寄存器B
or al,0x80 ;阻断NMI
out 0x70,al
mov al,0x12 ;设置寄存器B,禁止周期性中断,开放更新结束后中断,BCD码,24小时制
out 0x71,al
mov al,0x0c
out 0x70,al
in al,0x71 ;读RTC寄存器C,复位未决的中断状态
;开启RTC中断
in al,0xa1 ;读8259从片的IMR寄存器
and al,0xfe ;清除bit 0(此位连接RTC)
out 0xa1,al ;写回此寄存器
sti
; 睡眠 , 直到中断发生
sleep_func:
hlt
jmp sleep_func
done:
mov ax,[ds:addr_off]
mov word [es:0x70 * 4],ax
mov ax,[ds:addr_seg]
mov word [es:0x70 *4 + 2],ax
;跳回加载器
pop cx
pop dx
mov sp,cx
mov ss,dx
retf
new_int_func:
push ax
push bx
push cx
push dx
push es
is_safe:
in al,0x71 ;读寄存器A
test al,0x80 ;测试第7位UIP
jnz is_safe
in al,0x71 ;每次都要读一下RTC的寄存器C,否则只发生一次中断
;中断完成
mov al,0x20 ;中断结束命令EOI
out 0xa0,al ;从片
out 0x20,al ;主片
pop es
pop dx
pop cx
pop bx
pop ax
iret
code_section_end:
section data_section vstart=0 align=16
addr_off dw 0
addr_seg dw 0
data_section_end:
section stack_section vstart=0 align=16
resb 256
stack_section_end:
section trail
app_end:版权声明:本文为dashoumeixi原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。