📄 pm1.asm
字号:
; pm1.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pm1.asm - protected-mode demo code
; Christopher Giese <geezer[AT]execpc.com>
; http://www.execpc.com/~geezer/os
;
; Release date 10/7/98. Distribute freely. ABSOLUTELY NO WARRANTY.
; Assemble with NASM: nasm -o pm1.com pm1.asm
;
; Run this program in "raw" MS-DOS mode, not a Windows DOS box. Do not
; load EMM386 or other memory managers. Do not load HIMEM.SYS (see
; below). Clear the screen and hit Enter a few times before running it.
;
; What you should see:
; "012345 !"
; " still in real mode!"
; " ES,DS still real mode!"
; " Finally in protected mode!"
; " ES,DS still protected mode!"
; " back to BORING old real mode"
;
; Running the program puts a 4G limit in the segment descriptor
; cache for ES. This makes 32-bit addresses legal in real mode,
; when used with ES. Running the program a second time will add
; an 'R' to the top line.
;
; If you see the 'R' instead of the '!' the first time you run it,
; it is because a previous protected-mode program has munged the
; segment descriptor cache for ES. The most likely culprit is
; HIMEM.SYS, which is loaded silently and automatically by DOS 7.
;
; This code was tested with NASM version 0.93 and NASM version 0.97.
; Please let me know if you have problems with other versions.
;
; This code was tested on an Intel 486SX-based system and an Intel
; Pentium-based system. Please let me know of possible problems with
; other CPUs, especially the 386 or 386SX.
;
; Though I have tried to be as clear and accurate as possible, there
; may be errors. Constructive criticism is welcome.
;
; Demonstrates:
; - Basic 32-bit protected mode.
; - Linear (flat) memory.
; - Access to text-mode video memory.
; - Return to real mode.
; - "Unreal" mode (flat real, big real).
; - Effects of the segment descriptor cache.
; See other tutorials for:
; - Interrupts/exceptions
; - Virtual 8086 mode
; - CPU detection (>= 386)
; - Local Descriptor Tables (LDTs)
; - Running code in XMS
; - A20 gate
; - Multitasking
; - Task state segments (TSSes)
; - Privilege
; - Gates
; Sources:
; INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986
; http://www.execpc.com/~geezer/os/386intel.zip
; ftp://ftp.cdrom.com/.20/demos/code/hardware/cpu/386intel.zip
; http://www.intercom.net/user/jeremyfo/SamOS/Files/386intel.zip
; Robert Collins' "Intel Secrets" web site:
; http://www.x86.org
;
[ORG 0x100]
[BITS 16]
;
;
; If you want to skip this commentary and go directly to the code,
; search for the label "start:"
;
; How to get into protected mode? First, you need a GLOBAL DESCRIPTOR
; TABLE (GDT). There is one at the end of this file, at address "gdt:"
; The GDT contains 8-byte DESCRIPTORS for each protected-mode segment.
; Each descriptor contains a 32-bit segment base address, a 20-bit segment
; limit, and 12 bits describing the segment type. The descriptors look
; like this:
;
; MSB bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 LSB
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 0 | bit 7<---------------- segment limit------------------->bit 0 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 1 |bit 15<---------------- segment limit------------------->bit 8 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 2 | bit 7<---------------- segment base-------------------->bit 0 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 3 |bit 15<---------------- segment base-------------------->bit 8 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 4 |bit 23<---------------- segment base-------------------->bit 16|
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 5 | P | DPL | <----------- segment type ----------> |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; P is the Segment Present bit. It should always be 1.
;
; DPL is the DESCRIPTOR PRIVILEGE LEVEL. For simple code like this, these
; two bits should always be zeroes.
;
; Segment Type (again, for simple code like this) is hex 12 for data
; segments, hex 1A for code segments.
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 6 | G | B | 0 | avail | bit 19<-- seg limit--->bit 16 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; G is the Limit Granularity. If zero, the segment limit is in bytes
; (0 to 1M, in 1-byte increments). If one, the segment limit is in 4K PAGES
; (0 to 4G, in 4K increments). For simple code, set this bit to 1, and
; set the segment limit to its highest value (FFFFF hex). You now have
; segments that are 4G in size! The Intel CPUs can address no more than
; 4G of memory, so this is like having no segments at all. No wonder
; protected mode is popular.
;
; B is the Big bit; also called the D (Default) bit. For code segments,
; all instructions will use 32-bit operands and addresses by default
; (BITS 32, in NASM syntax, USE32 in Microsoft syntax) if this bit is set.
; 16-bit protected mode is not very interesting, so set this bit to 1.
;
; None of these notes apply to the NULL descriptor. All of its bytes
; should be set to zero.
;
; +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 7 |bit 31<---------------- segment base------------------->bit 24 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; Build a simple GDT with four descriptors: NULL (all zeroes), linear data
; (lets you use 32-bit addresses), code, and data/stack. (An extra
; descriptor or two is needed to return to real mode.) For simplicity,
; the limits of all descriptors (except NULL and the real-mode descriptors)
; are FFFFF hex, the largest possible limit.
;
; In a real-mode .COM file, CS=DS. To make addressing identical in real
; mode and protected mode, we set the base of the code and data descriptors
; to DS * 16. This number is computed at run-time; the other numbers in
; the GDT can be done at assemble-time.
;
start: xor ebx,ebx
mov bx,ds ; BX=segment
shl ebx,4 ; BX="linear" address of segment base
mov eax,ebx
mov [gdt2 + 2],ax ; set base address of 32-bit segments
mov [gdt3 + 2],ax
mov [gdt4 + 2],ax ; set base address of 16-bit segments
mov [gdt5 + 2],ax
shr eax,16
mov [gdt2 + 4],al
mov [gdt3 + 4],al
mov [gdt4 + 4],al
mov [gdt5 + 4],al
mov [gdt2 + 7],ah
mov [gdt3 + 7],ah
mov [gdt4 + 7],ah
mov [gdt5 + 7],ah
;
; Now we have a valid GDT. The CPU has a 48-bit register (GDTR) which must
; contain the base address of the GDT, and its limit. We will put those
; values at the address "gdtr", then load the GDTR register from that
; address. The GDT limit is a fixed value:
; number of descriptors * 8 - 1
; This code uses 5 descriptors (null, linear, code, data, real-mode),
; so the GDT limit is 39. (If you look at the code after "gdtr:", you
; will see that it can accommodate more or fewer descriptors
; "automatically".)
;
; What about the GDT base? This won't work:
;
; lea eax,[gdt] ; This address is relative to
; mov [gdtr + 2],eax ; the segment base.
;
; The address of the GDT base (the address to be loaded into the GDTR
; register) is a PHYSICAL address, one that is not translated by
; segmentation. So, we do the translation ourselves, by adding the
; 32-bit segment base address that we put in EBX:
;
lea eax,[gdt + ebx] ; EAX=PHYSICAL address of gdt
mov [gdtr + 2],eax
;
; That's a bit confusing, but eventually you'll get it. (I have been
; hacking at protected mode for over a year, and it STILL confuses me.)
;
; This demo code neither demonstrates nor supports interrupts.
; Shut them off. (My thanks to John Fine for pointing out that INT 0Dh
; is also IRQ 5, and his suggestion that I move the 'cli' here.)
;
cli
;
; Before entering pmode, we will try to use 32-bit addressing while still
; in real mode. To do this, we have to take over the vector for INT 0Dh
; (the "pseudo GPF").
;
xor ax,ax
mov es,ax
mov edx,[es:0x0D * 4] ; INT 0Dh vector -> EDX
mov [es:0x0D * 4 + 2],cs
lea ax,[trap]
mov [es:0x0D * 4],ax
;
; Try using a 32-bit address in real mode. Depending on "where the CPU's
; been", this may or may not cause interrupt 0Dh.
;
mov ebx,0xB809A ; ES still 0
mov byte [es:ebx],'R' ; 'R' in upper right corner of screen
;
; Did it work? There's more info on 32-bit addressing in real mode below.
; Note: the second 'mov' above is 5 bytes long. Modify the 'trap' routine
; below if the 'mov' changes.
;
; If we want to return to real mode when we're done, we must save the
; contents of the CS register. To simplify the return to real mode,
; we also store the return-to-real-mode address, do_rm.
;
mov ax,cs
mov [RealModeCS],ax
lea ax,[do_rm]
mov [RealModeIP],ax
;
; To (literally) see this code in action, we need a way to access
; text-mode video memory. This memory is at real-mode address B800:0000
; (hex). Let's put the segment for this memory in ES:
;
mov ax,0xB800
mov es,ax
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -