16位装载程序 - 实模式下访问4G地址空间

目的

要在实模式下访问4G地址空间是为了将系统文件事先读入内存,而不用切换到32位保护模式后,自己写硬盘和文件系统的驱动。因为在现阶段,很多设备的驱动还没写出来,当然,那种已经成熟的操作系统,各种设备的驱动程序基本都齐全了的,在哪个阶段载入都可以。但是,对我来说,是基本没有各种设备的驱动程序,只好现借用BIOS来完成磁盘读写。

实现的方法

进入保护模式,给fs装入一个段基址是0,段界限是4G的选择符,然后立刻切换回实模式,这样在以后的程序中,就可以用这个选择符来实现访问4G地址空间了

注意:在设置完FS后,就不要修改FS了

关键点

这个实现有几个关键的地方是,切换到保护模式,还有一个就是保证实模式和保护模式来回切换的时候,要保证程序运行

切换到保护模式

注意,这里是切换到16位的保护模式,并不是32位保护模式,所以要计算出对应的段基址,段界限就是64K,至于段的属性,大概是代码段可执行,数据段可读写,具体说明就留到书里写了,

源代码

这是一个演示用的程序,仅仅为了说明功能。
由于编译器的问题,还是采用了汇编和C混合的方式,在汇编里完成保护模式的初始化,也实现了访问4G空间的函数,其实在探测内存那里就有一部分。

汇编代码

这里有个地方会比较怪,就是那个返回实模式的代码,是一个手工构造的远跳转指令,但是段寄存设成了0,看上去会错,但实际上在Init4G的开头,已经往这个位置填入了当前CS的值,实际上已经构造出了
jmp cs:back_to_real_mode
的远跳转地址,并不是源程序上的
jmp 0:back_to_real_mode

; file name: rm4gasm.asm, robin @ 2019-10-25
.386p
.model tiny
;
EXTRN   _main:PROC
PUBLIC  _ReadByte4G
PUBLIC  _WriteByte4G
PUBLIC  _Init4G
;
DESC            STRUC
LimitL          DW      0 ;segment limit(BIT0-15)
BaseL           DW      0 ;segment base address(BIT0-15)
BaseM           DB      0 ;segment base address(BIT16-23)
Attributes      DB      0 ;segemtn attribues
LimitH          DB      0 ;segment limit (BIT16-19)(include segment attributes
                          ;high 4 bit)
BaseH           DB      0 ;segment base address(BIT24-31)
Desc            ENDS
;
PDesc           STRUC
Limit           DW      0 ;16-bit limit
Base            DD      0 ;32-bit base address
PDesc           ENDS
;
TEXT  SEGMENT USE16 PUBLIC 'CODE'
    assume cs:TEXT, ds:TEXT
    org 0100h
start:
    mov sp, 0FFFCh
    call _main;
    mov ax, 04C00h
    int 021h
;
;void WriteByte4G(byte_t nData, uint32_t nPhyAddr)
;
_WriteByte4G PROC
    mov     bx, sp
    mov     al, [bx + 2]
    mov     ecx, [bx + 4]
    mov     ebx, ecx
    mov     fs:[ebx], al
    ret
_WriteByte4G ENDP
;
;byte_t ReadByte4G(uint32_t nPhyAddr)
;
_ReadByte4G PROC
    mov     bx, sp
    xor     ax, ax
    mov     ecx, [bx + 2]
    mov     ebx, ecx
    mov     al, fs:[ebx]
    ret
_ReadByte4G ENDP
;
;void Init4G(void)
;
_Init4G PROC
    push    bp
    mov     bp, sp
    mov     ax, cs                       
    mov     bx, offset back_to_real_mode_segment
    mov     [bx], ax
;
    mov     bx,16                           ;  构造当前运行环境的段选择子
    mul     bx                              ;  计算当前段的段地址,包括数据段和代
    mov     word ptr temp_code.BaseL,ax     ;  码段,由于在这个环境中,代码段和数
    mov     byte ptr temp_code.BaseM,dl     ;  据段相同可以使用相同段基址
    mov     word ptr temp_data.BaseL,ax
    mov     byte ptr temp_data.BaseM,dl
    mov     bx,offset dummy                 ;  计算GDT地址
    add     ax,bx                           ;
    adc     dx,0                            ;
    mov     word ptr p_desc.Base,ax         ;
    mov     word ptr p_desc.Base + 2,dx     ;
;
   ;initial pm 保护模式初始化
    lgdt    p_desc                          ;  装填GDT
    pushf
    cli
    mov     eax,cr0                         ;  打开保护模式,就是将CR0寄存器的0位设
    or      eax,1                           ;1。在开启保护模式后,地址定位方式
    mov     cr0,eax                         ;  已经变化,需要按新的段选择子来取指令
;
    db      0eah                            ;  构造调转指令,完成清空指令预取队列
    dw      offset init                     ;  由于CPU有指令预取功能,因此这条指令会在
    dw      temp_code_sel                   ;  打开保护模式前取得
init:
 ;
 ;  这个时候不是设置段寄存器,而是设置段选择符
 ;
    mov     ax, data_sel
    push    ax
    push    ax
    pop     fs
    pop     gs
    mov     eax, CR0
    and     eax, 0FFFFFFFEh
    mov     CR0, eax
    db      0EAh
    dw      offset back_to_real_mode;
back_to_real_mode_segment:
    dw      0
back_to_real_mode:
    popf
    mov     sp, bp
    pop     bp
    ret
_Init4G ENDP
;
align qword
gdt label byte
    dummy DESC <>   ;空描述符
    temp_code DESC <0ffffh,     ,   ,9ah,  0h,0h> ; 8
    temp_data DESC <0ffffh,     ,   ,92h,  0h,0h> ; 16
    code_desc DESC <0ffffh,  00h, 0h,9ah,0cfh,0h> ; 24, 4G
    data_desc DESC <0ffffh,  00h, 0h,92h,0cfh,0h> ; 32, 4G
    vbuf_desc DESC <0ffffh,8000h,0bh,92h, 40h,0h> ; 40
    gdt_len       = $ - gdt
    temp_code_sel = temp_code - gdt
    temp_data_sel = temp_data - gdt
    code_sel      = code_desc - gdt
    data_sel      = data_desc - gdt
    vbuf_sel      = vbuf_desc - gdt  
    p_desc PDesc <gdt_len -1,>
    data_len = $ - gdt
TEXT ENDS
END  start

C代码

由于是编译成COM格式,标准的printf不能用,就另外写了一个直接写屏的print来显示信息。程序就是往4M的地方写入一个字节,然后在读出来,比较是否一致,一致则显示succeed,也就表明在实模式下访问4G空间成功。

//  file name: rm4g.c, robin @ 2019-10-25
#include "stdio.h"
// WriteByte4G, ReadByte4G, Init4G imply in rm4gasm.asm
void            WriteByte4G(unsigned char nData, unsigned long nPhyAddr);
unsigned char   ReadByte4G(unsigned long nPhyAddr);
void            Init4G(void);
#define VBUF  (unsigned short far *)(0xB8000000l)
//
void print(int x, int y, char * msg)
{
    unsigned loc = y * 80 + x;
    unsigned short far * vbuf = VBUF;
    while( *msg ){
        vbuf[loc++] = 0x700 + *msg++;
    }
}
//
int main(void)
{
    Init4G();
//  read/write address 0x400000, over 64K
    WriteByte4G(0xA5,4l*1024l*1024l);
    if( 0xA5 == ReadByte4G(4l*1024l*1024l) )
        print(10,10,"succeed");
    else
        print(10,10,"failed");
//
    return 0;
}

编译

编译还是有点啰嗦的,还是贴出来了吧,虽然只是一个演示程序用的

tasm rm4gasm.asm
bcc -c -mt -zCTEXT -zDTEXT -zRTEXT -zTCODE -zBCODE rm4g.c
tlink /t rm4gasm.obj rm4g.obj
del rm4g.com
rename rm4gasm.com rm4g.com

运行结果

运行结果显示成功了实模式访问4G空间

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值