1:实模式下内存寻址 段首地址*16+偏移量=物理地址(段寄存器左移四位+offset) 2:保护模式下寻址 1)段寄存器中存放段选择子Selector 2)GDTR(全局描述符表寄存器)中存放段描述符首地址 3)通过选择子与GDTR中首地址,找到对应的段描述符 4)段描述符中有段的物理首地址,就得到段在内存中的首地址 5)加上偏移量,就得到这个段中存放的数据的真正物理地址 3:程序说明 ;宏定义Descriptor可看做是表示描述符的结构体,由3个参数构成,这些Descriptor组成全局描述符表GDT %macro Descriptor 3 dw %2 & 0FFFFh dw %1 & 0FFFFh db (%1>>16) & 0FFh dw ((%2>>8) & 0F00h) | (%3 & 0F0FFh) db (%1>>24) & 0FFh %endmacro DA_32 EQU 4000h DA_C EQU 98h DA_DRW EQU 92h ;org 07c00h ;直接加载.bin文件时用07c00h地址(小于512bytes时) org 0100h ;大于512bytes时制作成.com文件,通过freedos来加载.com文件,得到运行的效果 jmp LABEL_BEGIN [SECTION .gdt] LABEL_GDT: Descriptor 0, 0, 0 ;描述符的三个参数分别表示段物理首地址,段界限,段属性,下面定义的三个描述符LABEL_GDT,CODE32和VIDEO构成描述符表 LABEL_DESC_CODE32: Descriptor 0,SegCode32Len - 1, DA_C + DA_32 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW GdtLen equ $ - LABEL_GDT ;描述符表长度 GdtPtr dw GdtLen - 1 ;GdtPtr结构共48位,低16位为段界限,高32位为0,以后会重置,高32位的访问通过GdtPtr+2进行,高32位存放GDT的物理地址 dd 0 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT ;定义段选择子,即当前描述符相对全局描述符表的偏移量 SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT [SECTION .s16] [BITS 16] ;告诉编译器这是一个16位代码段,此段代码初始化所以段描述符表中的段物理首地址 LABEL_BEGIN: ;程序的入口处 mov ax,cs ;ds,es,ss与cs代码段基地址都一样 mov ds,ax mov es,ax mov ss,ax mov sp,0100h xor eax,eax ;自己与自己异或,将eax清零 mov ax,cs ;eax是32位寄存器,ax是eax的低16位 shl eax,4 ;左移四位,相当于乘以16,实模式下计算物理地址 add eax,LABEL_SEG_CODE32 ;加上段相对代码段的偏移地址,eax中得到32位的CODE32段的物理地址 mov word [LABEL_DESC_CODE32 + 2],ax ;用实际物理地址填充LABEL_DESC_CODE32段描述符的段物理首地址字段,其中低16位分别放在2,3字节,高16位中的低8位放在第4字节,高16位中的高8位放在第7字节. shr eax,16 mov byte [LABEL_DESC_CODE32 + 4],al mov byte [LABEL_DESC_CODE32 + 7],ah ;得到段描述符表的物理地址,存放到GDTR寄存器中 xor eax,eax mov ax,ds ;GDT的段地址为数据寄存器DS shl eax,4 add eax,LABEL_GDT ;计算出实际物理地址 mov dword [GdtPtr + 2],eax ;将实际物理地址直接放到GdtPtr+2中(和实际GDTR寄存器结构相同) lgdt [GdtPtr] ;通过lgdt指令将GdtPtr中内容加载到GDTR寄存器 cli ;关中断(实模式和保护模式下中断处理不同,故关中断防止出错) in al,92h ;打开A20地址线 or al,00000010b out 92h,al mov eax,cr0 ;将cr0寄存器的第0位(PE位,决定CPU运行于实模式还是保护模式)置为1 or eax,1 mov cr0,eax jmp dword SelectorCode32:0 ;跳入code32执行,加dword告诉编译器这句代码要编译成32位代码 [SECTION .s32] [BITS 32] LABEL_SEG_CODE32: mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符 mov gs,ax xor edi,edi mov edi,(80*11 + 79)*2 ;屏幕的第11行,79列 mov ah,0Ch ;0000黑底,1100红字 mov al,'P' mov [gs:edi],ax jmp $ SegCode32Len equ $ - LABEL_SEG_CODE32 这里使用freeos加载运行,从bochs下载freedos压缩文件,解压,将其中的a.img放到工作目录下并更名位freedos.img,用bximage新建一个pm.img. 修改.bochsrc配置文件,floppya:.......freedos.img floppyb:......pm.img boot=a 运行bochs,freedos运行成功后格式化b盘;format b: 将0100h的编译版本用nasm编译生成pmtest1.com文件 在/mnt/下新建一个目录floppy, mount -o loop pm.img /mnt/floppy cp ./pmtest1.com /mnt/floppy umount /mnt/floppy 在freedos中运行dir b:查看b盘内容,再运行b:\pmtest1.com即可完成此次实验 若采用第一个实验中的加载方式而不采用freedos方式,只需要将0100地址改为07c00,并且编译成.bin文件直接运行即可 进入保护模式的主要步骤总结: 1)准备GDT(包括设置各个段描述符内容) 2)用lgdt加载GDTR 3)关中断,打开A20地址线 4)置cr0的PE位 5)跳转,进入保护模式