先说下我配的环境,msdos622,vpc2007,当我在执行pmtest7的时候,崩溃,无法执行,在此之后,我又通过bochs配置了环境,可以调试.com程序(见上一篇博客),调试了良久(不太会调试,bochs有些调试还是不会)模模糊糊知道是哪错了,我在下面帖出来,还请高手指正
1 ; ========================================== 2 ; pmtest7.asm 3 ; 编译方法:nasm pmtest7.asm -o pmtest7.com 4 ; ========================================== 5 6 %include "pm.inc" ; 常量, 宏, 以及一些说明 7 8 PageDirBase equ 200000h ; 页目录开始地址: 2M 9 PageTblBase equ 201000h ; 页表开始地址: 2M + 4K 10 11 org 0100h 12 jmp LABEL_BEGIN 13 14 [SECTION .gdt] 15 ; GDT 16 ; 段基址, 段界限 , 属性 17 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 18 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符 19 LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW ; Page Directory 20 LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 4096 * 8 - 1, DA_DRW ; Page Tables 21 LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段, 32 22 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16 23 LABEL_DESC_DATA: Descriptor 0, DataLen - 1, DA_DRW ; Data 24 LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位 25 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址 26 ; GDT 结束 27 28 GdtLen equ $ - LABEL_GDT ; GDT长度 29 GdtPtr dw GdtLen - 1 ; GDT界限 30 dd 0 ; GDT基地址 31 32 ; GDT 选择子 33 SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT 34 SelectorPageDir equ LABEL_DESC_PAGE_DIR - LABEL_GDT 35 SelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDT 36 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT 37 SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT 38 SelectorData equ LABEL_DESC_DATA - LABEL_GDT 39 SelectorStack equ LABEL_DESC_STACK - LABEL_GDT 40 SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT 41 ; END of [SECTION .gdt] 42 43 [SECTION .data1] ; 数据段 44 ALIGN 32 45 [BITS 32] 46 LABEL_DATA: 47 ; 实模式下使用这些符号 48 ; 字符串 49 _szPMMessage: db "In Protect Mode now. ^-^", 0Ah, 0Ah, 0 ; 进入保护模式后显示此字符串 50 _szMemChkTitle: db "BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0 ; 进入保护模式后显示此字符串 51 _szRAMSize db "RAM size:", 0 52 _szReturn db 0Ah, 0 53 ; 变量 54 _wSPValueInRealMode dw 0 55 _dwMCRNumber: dd 0 ; Memory Check Result 56 _dwDispPos: dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。 57 _dwMemSize: dd 0 58 _ARDStruct: ; Address Range Descriptor Structure 59 _dwBaseAddrLow: dd 0 60 _dwBaseAddrHigh: dd 0 61 _dwLengthLow: dd 0 62 _dwLengthHigh: dd 0 63 _dwType: dd 0 64 65 _MemChkBuf: times 256 db 0 66 67 ; 保护模式下使用这些符号 68 szPMMessage equ _szPMMessage - $$ 69 szMemChkTitle equ _szMemChkTitle - $$ 70 szRAMSize equ _szRAMSize - $$ 71 szReturn equ _szReturn - $$ 72 dwDispPos equ _dwDispPos - $$ 73 dwMemSize equ _dwMemSize - $$ 74 dwMCRNumber equ _dwMCRNumber - $$ 75 ARDStruct equ _ARDStruct - $$ 76 dwBaseAddrLow equ _dwBaseAddrLow - $$ 77 dwBaseAddrHigh equ _dwBaseAddrHigh - $$ 78 dwLengthLow equ _dwLengthLow - $$ 79 dwLengthHigh equ _dwLengthHigh - $$ 80 dwType equ _dwType - $$ 81 MemChkBuf equ _MemChkBuf - $$ 82 83 DataLen equ $ - LABEL_DATA 84 ; END of [SECTION .data1] 85 86 87 ; 全局堆栈段 88 [SECTION .gs] 89 ALIGN 32 90 [BITS 32] 91 LABEL_STACK: 92 times 512 db 0 93 94 TopOfStack equ $ - LABEL_STACK - 1 95 96 ; END of [SECTION .gs] 97 98 99 [SECTION .s16] 100 [BITS 16] 101 LABEL_BEGIN: 102 mov ax, cs 103 mov ds, ax 104 mov es, ax 105 mov ss, ax 106 mov sp, 0100h 107 108 mov [LABEL_GO_BACK_TO_REAL+3], ax 109 mov [_wSPValueInRealMode], sp 110 111 ; 得到内存数 112 mov ebx, 0 113 mov di, _MemChkBuf 114 .loop: 115 mov eax, 0E820h 116 mov ecx, 20 117 mov edx, 0534D4150h 118 int 15h 119 jc LABEL_MEM_CHK_FAIL 120 add di, 20 121 inc dword [_dwMCRNumber] 122 cmp ebx, 0 123 jne .loop 124 jmp LABEL_MEM_CHK_OK 125 LABEL_MEM_CHK_FAIL: 126 mov dword [_dwMCRNumber], 0 127 LABEL_MEM_CHK_OK: 128 129 ; 初始化 16 位代码段描述符 130 mov ax, cs 131 movzx eax, ax 132 shl eax, 4 133 add eax, LABEL_SEG_CODE16 134 mov word [LABEL_DESC_CODE16 + 2], ax 135 shr eax, 16 136 mov byte [LABEL_DESC_CODE16 + 4], al 137 mov byte [LABEL_DESC_CODE16 + 7], ah 138 139 ; 初始化 32 位代码段描述符 140 xor eax, eax 141 mov ax, cs 142 shl eax, 4 143 add eax, LABEL_SEG_CODE32 144 mov word [LABEL_DESC_CODE32 + 2], ax 145 shr eax, 16 146 mov byte [LABEL_DESC_CODE32 + 4], al 147 mov byte [LABEL_DESC_CODE32 + 7], ah 148 149 ; 初始化数据段描述符 150 xor eax, eax 151 mov ax, ds 152 shl eax, 4 153 add eax, LABEL_DATA 154 mov word [LABEL_DESC_DATA + 2], ax 155 shr eax, 16 156 mov byte [LABEL_DESC_DATA + 4], al 157 mov byte [LABEL_DESC_DATA + 7], ah 158 159 ; 初始化堆栈段描述符 160 xor eax, eax 161 mov ax, ds 162 shl eax, 4 163 add eax, LABEL_STACK 164 mov word [LABEL_DESC_STACK + 2], ax 165 shr eax, 16 166 mov byte [LABEL_DESC_STACK + 4], al 167 mov byte [LABEL_DESC_STACK + 7], ah 168 169 ; 为加载 GDTR 作准备 170 xor eax, eax 171 mov ax, ds 172 shl eax, 4 173 add eax, LABEL_GDT ; eax <- gdt 基地址 174 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址 175 176 ; 加载 GDTR 177 lgdt [GdtPtr] 178 179 ; 关中断 180 cli 181 182 ; 打开地址线A20 183 in al, 92h 184 or al, 00000010b 185 out 92h, al 186 187 ; 准备切换到保护模式 188 mov eax, cr0 189 or eax, 1 190 mov cr0, eax 191 192 ; 真正进入保护模式 193 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处 194 195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 196 197 LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里 198 mov ax, cs 199 mov ds, ax 200 mov es, ax 201 mov ss, ax 202 203 mov sp, [_wSPValueInRealMode] 204 205 in al, 92h ; ┓ 206 and al, 11111101b ; ┣ 关闭 A20 地址线 207 out 92h, al ; ┛ 208 209 sti ; 开中断 210 211 mov ax, 4c00h ; ┓ 212 int 21h ; ┛回到 DOS 213 ; END of [SECTION .s16] 214 215 216 [SECTION .s32]; 32 位代码段. 由实模式跳入. 217 [BITS 32] 218 219 LABEL_SEG_CODE32: 220 mov ax, SelectorData 221 mov ds, ax ; 数据段选择子 222 mov ax, SelectorData 223 mov es, ax 224 mov ax, SelectorVideo 225 mov gs, ax ; 视频段选择子 226 227 mov ax, SelectorStack 228 mov ss, ax ; 堆栈段选择子 229 230 mov esp, TopOfStack 231 232 233 ; 下面显示一个字符串 234 push szPMMessage 235 call DispStr 236 add esp, 4 237 238 push szMemChkTitle 239 call DispStr 240 add esp, 4 241 242 ;call DispMemSize ; 显示内存信息 243 244 call SetupPaging ; 启动分页机制 245 246 ; 到此停止 247 jmp SelectorCode16:0 248 249 ; 启动分页机制 -------------------------------------------------------------- 250 SetupPaging: 251 ; 根据内存大小计算应初始化多少PDE以及多少页表 252 xor edx, edx 253 mov eax, [dwMemSize] 254 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小 255 div ebx 256 mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数 257 test edx, edx 258 jz .no_remainder 259 inc ecx ; 如果余数不为 0 就需增加一个页表 260 .no_remainder: 261 push ecx ; 暂存页表个数 262 263 ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞. 264 265 ; 首先初始化页目录 266 mov ax, SelectorPageDir ; 此段首地址为 PageDirBase 267 mov es, ax 268 xor edi, edi 269 xor eax, eax 270 mov eax, PageTblBase | PG_P | PG_USU | PG_RWW 271 .1: 272 stosd 273 add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. 274 loop .1 275 276 ; 再初始化所有页表 277 mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase 278 mov es, ax 279 pop eax ; 页表个数 280 mov ebx, 1024 ; 每个页表 1024 个 PTE 281 mul ebx 282 mov ecx, eax ; PTE个数 = 页表个数 * 1024,,,,,这个地方我感觉错了,调试的时候发我先ecx=2000h,我把这个地方ecx直接赋值为2000h是可以执行的,还有这里的mul我感觉是错的,32位乘32位最后结果保存在edx和eax中,虽然在调试的时候发现edx是0,但是这里我还是不建议这么写。 283 xor edi, edi 284 xor eax, eax 285 mov eax, PG_P | PG_USU | PG_RWW 286 .2: 287 stosd 288 add eax, 4096 ; 每一页指向 4K 的空间 289 loop .2 290 291 mov eax, PageDirBase 292 mov cr3, eax 293 mov eax, cr0 294 or eax, 80000000h 295 mov cr0, eax 296 jmp short .3 297 .3: 298 nop 299 300 ret 301 ; 分页机制启动完毕 ---------------------------------------------------------- 302 303 304 305 DispMemSize: 306 push esi 307 push edi 308 push ecx 309 310 mov esi, MemChkBuf 311 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构 312 .loop: ;{ 313 mov edx, 5 ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员,共5个成员 314 mov edi, ARDStruct ; { // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type 315 .1: ; 316 push dword [esi] ; 317 call DispInt ; DispInt(MemChkBuf[j*4]); // 显示一个成员 318 pop eax ; 319 stosd ; ARDStruct[j*4] = MemChkBuf[j*4]; 320 add esi, 4 ; 321 dec edx ; 322 cmp edx, 0 ; 323 jnz .1 ; } 324 call DispReturn ; printf("\n"); 325 cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2 326 jne .2 ; { 327 mov eax, [dwBaseAddrLow] ; 328 add eax, [dwLengthLow] ; 329 cmp eax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize) 330 jb .2 ; 331 mov [dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow; 332 .2: ; } 333 loop .loop ;} 334 ; 335 call DispReturn ;printf("\n"); 336 push szRAMSize ; 337 call DispStr ;printf("RAM size:"); 338 add esp, 4 ; 339 ; 340 push dword [dwMemSize] ; 341 call DispInt ;DispInt(MemSize); 342 add esp, 4 ; 343 344 pop ecx 345 pop edi 346 pop esi 347 ret 348 349 %include "lib.inc" ; 库函数 350 351 SegCode32Len equ $ - LABEL_SEG_CODE32 352 ; END of [SECTION .s32] 353 354 355 ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式 356 [SECTION .s16code] 357 ALIGN 32 358 [BITS 16] 359 LABEL_SEG_CODE16: 360 ; 跳回实模式: 361 mov ax, SelectorNormal 362 mov ds, ax 363 mov es, ax 364 mov fs, ax 365 mov gs, ax 366 mov ss, ax 367 368 mov eax, cr0 369 and al, 11111110b 370 mov cr0, eax 371 372 LABEL_GO_BACK_TO_REAL: 373 jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值 374 375 Code16Len equ $ - LABEL_SEG_CODE16 376 377 ; END of [SECTION .s16code]
我看的书是自己动手第一版,但是在无意中我看到了第二版光盘中的资料里面有可用的freedos,于是拿来一试,结果pmtest7是可以运行的,顿时感觉我被环境坑了两天,不过这两天琢磨琢磨这,琢磨琢磨那总算是学到不少东西,就是进度慢了
建议大家学的时候用freedos吧,虽然不知道这和msdos的差别在那