VESA编程示例

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Simulated Linear Frame Buffer
; Christopher Giese <geezer@execpc.com>
; http://www.execpc.com/~geezer/os/
;
; Updated March 6, 2001: can now return to V86 mode when done
;
; Some stuff (actually, pmode VESA BIOS bank switching) fixed by
; Alexei A. Frounze <alexfru@chat.ru>
; http://alexfru.chat.ru
;
; You can do anything with this code but blame us for it.
;
; To run this code, you need a PC with a 32-bit processor (386SX or better)
; and DOS. This code has been tested with the following SVGA video boards:
;
;      bank-switch function
; buss  chipset   used by this code
; ----  -------   --------------------
; 16-bit ISA Cirrus 5422  Cirrus
; 32-bit PCI S3 86c765 (Trio 64V+) S3
; 32-bit PCI (?) STB Nitro (?)  S3
;
; To assemble this code, you need NASM (http://www.web-sites.co.uk/nasm/)
; nasm -f bin -o slfb.com slfb.asm
;
; bugs/to do:
; - test with other systems
;
; Here is an interesting link:
; http://marc.theaimsgroup.com/?m=88102879813311&w=2
; Also, look at the "vflat" files in the source code to the
; SciTech MGL graphics library
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LFB_BASE equ 0C0000000h

%if 1
VESA_MODE equ 101h ; 640x480x256
WIDTH  equ 640
HEIGHT  equ 480
%else
VESA_MODE equ 103h ; 800x600x256
WIDTH  equ 800
HEIGHT  equ 600
%endif

SEQN_INDX       equ     3C4h
SEQN_DATA       equ     3C5h
CRTC_INDX       equ     3D4h
CRTC_DATA       equ     3D5h
GRFX_INDX       equ     3CEh
GRFX_DATA       equ     3CFh

 BITS 16
 ORG 100h

; save CS value for return to real mode
 mov [real_mode_cs],cs

; there is no foolproof way to ID an Intel CPU, but we'll try.
; If we are in V86 mode, the VMM may interfere with our attempts
; to change the IOPL and NT bits
 pushf
  pushf
  pop ax
  xor ah,07h ; try changing b14 (NT) and b13:b12
  push ax  ; (IOPL) of FLAGS
  popf
  pushf
  pop bx
 popf   ; restore old FLAGS value
 cmp ah,bh
 je is_32bit
 mov si,not_32bit_msg
 jmp msg_and_exit

not_32bit_msg:
 db "Sorry; this code requires a 32-bit CPU (386SX or better)."
 db 13, 10, 0

is_32bit:
; check for supported video card. First, look for VESA 2.x BIOS
 call probe_vesa
 je vid_ok

; no VESA 2.x BIOS; try direct hardware probing for Cirrus 5422
 call probe_cirrus
 je vid_ok

; not Cirrus; try probing for S3
 call probe_s3
 je vid_ok
 mov si,sorry_msg
 jmp msg_and_exit

sorry_msg:
 db 13, 10, "Sorry, no supported video card was found.", 13, 10, 0

ok_msg:
 db "DETECTED", 13, 10, 0

vid_ok:
 mov si,ok_msg
 call wrstr

; our pmode code will use the stack DOS gave us, but we have
; to zero the top 16 bits of the stack pointer (ESP)
 xor eax,eax
 mov ax,sp
 mov esp,eax

; get the virtual-to-physical conversion value. Though we turn on paging,
; this code runs in memory that is identity-mapped (i.e. no page-based
; address translation). We do, however, use segment-based address
; translation, to give things the same addresses in both real and pmode.
 xor ebx,ebx
 mov bx,cs
 shl ebx,4

; set base addresses of the 16- and 32-bit code segment descriptors
 mov eax,ebx
 mov [gdt2 + 2],ax
 mov [gdt4 + 2],ax
 shr eax,16
 mov [gdt2 + 4],al
 mov [gdt4 + 4],al
 mov [gdt2 + 7],ah
 ; mov [gdt4 + 7],ah ; no; this one is 16-bit

; now do the same with the data/stack segment descriptors
 mov eax,ebx
 mov [gdt3 + 2],ax
 mov [gdt5 + 2],ax
 shr eax,16
 mov [gdt3 + 4],al
 mov [gdt5 + 4],al
 mov [gdt3 + 7],ah
 ; mov [gdt5 + 7],ah ; no; this one is 16-bit

; point the TSS descriptor to the LINEAR/PHYSICAL address of 'tss'
 mov eax,tss
 add eax,ebx
 mov [gdt6 + 2],ax
 shr eax,16
 mov [gdt6 + 4],al
 mov [gdt6 + 7],ah

; point the interrupt gates in the Interrupt Descriptor Table (IDT)
; to the default interrupt handler 'unhand'
 mov di,idt
 mov cx,(idt_end - idt) / 8 ; number of IDT entries (gates)
do_idt:
 mov eax,unhand  ; point all gates to 'unhand'
 mov [di],ax  ; set bits 15:0 of gate offset
 shr eax,16
 mov [di+6],ax  ; set bits 31:16 of gate offset
 add di,8  ; next gate
 loop do_idt

 mov di,idt_0e  ; IDT entry 0Eh -> page fault
 mov eax,page_fault
 mov [di],ax
 shr eax,16
 mov [di+16],ax

; point 'gdt_ptr' to LINEAR/PHYSICAL address of the GDT;
; 'idt_ptr' to LINEAR/PHYSICAL adr of the IDT
 add [gdt_ptr + 2],ebx
 add [idt_ptr + 2],ebx

; DOS doesn't load .COM or .EXE files on a page (4K) boundary, so
; we must now find a page boundary for the page directory/tables.
 xor esi,esi
 mov si,page_info ; SI = offset of page_info
 add esi,ebx  ; ESI = LINEAR/PHYSICAL adr of page_info
 add esi,4095  ; round to 4K boundary

 and esi,0FFFFF000h ; ESI = LINEAR/PHYSICAL adr of page dir
 mov edi,esi
 sub edi,ebx  ; DI = offset of page dir

; save page dir address for later use, by both VCPI and "raw" code
 mov [vcpi_cr3],esi

; EAX = LINEAR/PHYSICAL address of "framebuffer" page table
; (4K above the page directory)
 mov eax,esi
 add eax,4096
; create entry in page dir for "framebuffer" page table
 or al,7   ; Ring 3, writable, present
 mov [di + (((LFB_BASE >> 22) & 3FFh) << 2)],eax

; EAX = LINEAR/PHYSICAL address of "kernel" page table
; (8K above the page directory)
 mov eax,esi
 add eax,8192
; create entry in page dir for "kernel" page table
 or al,7   ; Ring 3, writable, present
 mov [di + 0],eax

; advance DI from page directory to "framebuffer" page table
 add edi,4096

; save the LINEAR/PHYSICAL pointer to the "framebuffer" page table
 mov eax,edi
 add eax,ebx
 mov [fb_page_table],eax

; Map the simulated linear framebuffer from virtual address LFB_BASE
; to the actual banked framebuffer at physical address 000A0000,
; repeating every 64K (16 pages)
;
; 1600x1200x256 display needs 1875K simulated framebuffer
; == 30 banks of 64K each == 469 pages of 4K each
;
; If the simulated framebuffer crosses a 4 meg boundary, or is larger
; than 4 meg, you will need more than one "framebuffer" page table.

 push di
  add di,(((LFB_BASE >> 12) & 3FFh) << 2)
  mov dl,30
map_fb1:
  mov ecx,16
  mov eax,0A0006h ; Ring 3, writable, NOT PRESENT
map_fb2:
  stosd
  add eax,4096 ; next page
  loop map_fb2
  dec dl
  jne map_fb1
 pop di

; advance DI to "kernel" page table
 add di,4096

; using the "kernel" page table, identity-map
; (virtual == physical) the bottom 4M of RAM
; With VCPI, we can skip this step, and simply take over EMM386's
; conventional memory page table using INT 67h AX=DE01h
 mov cx,1024
 mov eax,7  ; Ring 3, writable, present
identity_map:
 stosd
 add eax,4096  ; next page
 loop identity_map

; move DI back to "kernel" page table, in case we want to change it
 sub di,4096

; check for virtual 8086 mode (Windows DOS box or EMM386 loaded)
 smsw ax
 test al,1  ; look at PE bit of MSW (CR0)
 je real0

 mov si,v86_msg
 call wrstr

; if it's EMM386, we can use VCPI to switch to protected mode
; Testing for VCPI like this makes Win95 pop up a dialog suggesting
; "MS-DOS mode". This may or may not be a good thing.
 mov ax,0DE00h
 push bx   ; save EBX; not interested in VCPI version
  int 67h
 pop bx
 cmp ah,0
 je vcpi0

; no VCPI? probably Windows DOS box; check for that
 mov si,err_msg
 mov ax,1600h
 int 2Fh
 cmp al,0
 je msg_and_exit
 cmp al,80h
 je msg_and_exit
 mov si,win_msg  ; yup, it's 'Doze

msg_and_exit:
 call wrstr

; exit to DOS with errorlevel 1
 mov ax,4C01h
 int 21h

vcpi0:
; get "kernel" page table and partial GDT from the VCPI server.
; If we set up our own identity-mapped page table, this step is optional.
; (we still need to do INT 67h AX=DE01h to get the pmode entry
; point to the VCPI server -- so we can return to V86 mode later)
 mov si,gdt7
 mov ax,0DE01h
 push ebx
  int 67h
  mov [vcpi_entry],ebx
 pop ebx
 mov si,vcpi_err_msg
 cmp ah,0
 jne msg_and_exit

; set up the VCPI control block for the switch to pmode
 add [vcpi_gdtr],ebx
 add [vcpi_idtr],ebx

; await keypress; switch to graphics mode; disable interrupts
 mov si,vcpi_msg
 call setup
; OK, let's do it
 mov esi,vcpi_control_block
 add esi,ebx
 mov ax,0DE0Ch
; if all goes well, the interrupt will return at 'from_vcpi'
 int 67h

real0:
; await keypress; switch to graphics mode; disable interrupts
 mov si,real_msg
 call setup

; set vital registers: page directory base register (PDBR = CR3)
 mov eax,[vcpi_cr3]
 mov cr3,eax
; ...GDTR and IDTR
 lgdt [gdt_ptr]
 lidt [idt_ptr]
; ...enable paging and pmode
 mov eax,cr0
 or eax,80000001h
 mov cr0,eax
 jmp CODE_SEL:from_real

; we jump here (in 16-bit pmode) when returning to real mode
; Back-to-real-mode sequence from 14.5 of 386INTEL.TXT:
; 3. load segment registers with values "appropriate to real mode"
real2:
 mov ax,DATA_SEL16
 mov ss,ax
 mov ds,ax
 mov es,ax
 mov fs,ax
 mov gs,ax

; 4. disable interrupts
 ; (already done)
; 5. clear the PE bit
 mov eax,cr0
 and al,0FEh
 mov cr0,eax

; 6. far jump to true real mode
 mov word [real_mode_ip],real3
 jmp far [real_mode_ip]

; 7. load an IDT that is compatible with real-mode IVT
real3:
 lidt [real_idt_ptr]

; 9. restore real-mode segment registers
real4:
 mov ax,cs
 mov ss,ax
 mov ds,ax
 mov es,ax
 mov fs,ax
 mov gs,ax

; 8. enable interrupts
 sti

; go back to text mode
 mov ax,3
 int 10h

 mov si,done_msg
 jmp msg_and_exit

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; REAL MODE SUBROUTINES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   setup
; action:  1. calls wrstr
;   2. prompts the user to press a key, and waits for it
;   3. sets VESA graphics mode VESA_MODE
;      (terminates if mode-set fails)
;   4. zeroes EFLAGS register (disables interrupts,
;      sets IOPL=0, and clears NT bit)
; in:   0-terminated string at DS:SI
; out:   (nothing)
; modifies:  AX
; minimum CPU:  386SX
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

press_key_msg:
 db "Press a key to continue", 13, 10, 0

vesa_err_msg:
 db "Error setting VESA graphics mode", 13, 10, 0

setup:
 push bx
; display the message pointed to by SI
  call wrstr
; prompt user to press a key
  mov si,press_key_msg
  call wrstr
; wait for the key
  mov ah,0
  int 16h
; switch to VESA graphics mode
  mov ax,4F02h
  mov bx,VESA_MODE
  int 10h
  cmp ah,0
  je setup1
  cmp al,4Fh
  je setup1
  mov si,vesa_err_msg
  jmp msg_and_exit
setup1:
  push dword 0
  popfd
 pop bx
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   wrstr
; action:  writes ASCIZ string to text screen
; in:   0-terminated string at DS:SI
; out:   (nothing)
; modifies:  (nothing)
; minimum CPU:  8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

wrstr: push ax
 push bx
 push si
  mov ah,0Eh ; INT 10h: teletype output
  xor bx,bx ; video page 0
  jmp wrstr2
wrstr1:  int 10h
wrstr2:  lodsb
  or al,al
  jne wrstr1
 pop si
 pop bx
 pop ax
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   probe_vesa
; action:  detects VESA 2.x video BIOS
; in:   (nothing)
; out (success): ZF=1, [bank_switch_fn] set
; out (failure): ZF=0
; modifies:  EAX, BX, DI, SI
; minimum CPU:  386SX
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

vesa_msg:
 db "Checking for VESA 2.x video BIOS...", 0

probe_vesa:
 mov si,vesa_msg
 call wrstr
 push es
  mov ax,4F00h
  mov di,vesa_info
  int 10h
  cmp ah,0
  jne no_vesa
; locate the pmode bank-switch function in the video BIOS ROM
  mov ax,4F0Ah
  mov bx,1
  int 10h
  cmp ax,004Fh
  jne no_vesa
; Alexei fixed this for me:
; convert address of bank-switch function from seg:off to linear
  add di, [es:di] ; add function offset to table offset
  xor eax,eax
  mov ax,es
  sub ax,[real_mode_cs] ; our 32-bit code base address isn't 0,
     ; gotta adjust this!!!
  shl eax,4
  movzx edi, di ; yep, this is required as well :)
  add eax,edi
; ...and store it
  mov [bank_switch_fn],eax
; return ZF=1 for success
  xor ax,ax
no_vesa:
 pop es
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   probe_s3
; action:  detects S3 video board
; in:   (nothing)
; out (success): ZF=1, [bank_switch_fn] set
; out (failure): ZF=0
; modifies:  EAX, SI
; minimum CPU:  386SX
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

s3_msg:
 db 13, 10, "no; checking for S3 video...", 0

probe_s3:
 mov si,s3_msg
 call wrstr
 push dx
; lock the enhanced registers
  mov dx,CRTC_INDX
  mov al,039h
  out dx,al

  inc dx
  mov al,00h
  out dx,al

  dec dx
  mov al,38h
  out dx,al

  inc dx
  mov al,00h
  out dx,al
; see if CRTC reg 35h is read-only
  dec dx
  mov al,35h
  out dx,al

  inc dx
  in al,dx
  mov ah,al

  xor al,0Fh
  out dx,al

  in al,dx
  cmp al,ah
  jne no_s3
; unlock S3 registers
  dec dx
  mov al,38h
  out dx,al

  inc dx
  mov al,48h
  out dx,al

  dec dx
  mov al,39h
  out dx,al

  inc dx
  mov al,0A5h
  out dx,al
; can CRTC reg 35h be changed now?
  dec dx
  mov al,35h
  out dx,al

  inc dx
  in al,dx
  mov ah,al

  xor al,0Fh
  out dx,al

  in al,dx
  cmp al,ah
  je no_s3
; found S3; restore old value of reg 35h
  mov al,ah
  out dx,al
; set [bank_switch_fn] to linear address of set_s3_bank
  mov dword [bank_switch_fn],set_s3_bank
; return ZF=1 for success
  xor ax,ax
no_s3:
 pop dx
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   probe_cirrus
; action:  detects video board with Cirrus 5422 chipset
; in:   (nothing)
; out (success): ZF=1, [bank_switch_fn] set
; out (failure): ZF=0
; modifies:  EAX, SI
; minimum CPU:  386SX
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

cirrus_msg:
 db 13, 10, "no; checking for Cirrus 5422 video...", 0

probe_cirrus:
 mov si,cirrus_msg
 call wrstr
 push dx
; unlock Cirrus registers
  mov dx,SEQN_INDX
  mov al,06h
  out dx,al
  inc dx
  mov al,12h
  out dx,al
; lock reg should now read as 12h
  in al,dx
  cmp al,12h
  jne no_cirrus
; check the chip ID
  mov dx,CRTC_INDX
  mov al,27h
  out dx,al
  inc dx
  in al,dx
  shr al,2
  cmp al,22h
  jb no_cirrus
  cmp al,28h
  ja no_cirrus
; found a Cirrus board
; set [bank_switch_fn] to linear address of set_cirrus_bank
  mov dword [bank_switch_fn],set_cirrus_bank
; return ZF=1 for success
  xor ax,ax
no_cirrus:
 pop dx
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-BIT PROTECTED MODE SUBROUTINES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 BITS 32

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   set_cirrus_bank
; action:  sets 64K video bank (Cirrus 5422 video only)
; in:   bank number in DL
; out:   (nothing)
; modifies:  (nothing)
; minimum CPU:  386SX
; notes:  EDX and EAX are used only to make this code
;   smaller. DX and AX could be used instead,
;   if you want code to run on 16-bit CPUs.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

set_cirrus_bank:
 push edx
 push eax
  mov eax,edx

  and al,0Fh
  shl al,4
  mov ah,al

  mov dx,GRFX_INDX
  mov al,09h
  out dx,al

  inc edx
  mov al,ah
  out dx,al
 pop eax
 pop edx
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   set_s3_bank
; action:  sets 64K video bank (S3 video only)
; in:   bank number in DL
; out:   (nothing)
; modifies:  (nothing)
; minimum CPU:  386SX
; notes:  EDX and EAX are used only to make this code
;   smaller. DX and AX could be used instead,
;   if you want code to run on 16-bit CPUs.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

set_s3_bank:
 push edx
 push eax
  mov eax,edx

  and al,0Fh
  mov ah,al

; grrrr...mode-set locked the S3 registers, so unlock them again
; xxx - do this after mode-set
  mov dx,CRTC_INDX
  mov al,38h
  out dx,al

  inc edx
  mov al,48h
  out dx,al

  dec edx
  mov al,39h
  out dx,al

  inc edx
  mov al,0A5h
  out dx,al

; now: do the bank-switch
  mov dx,CRTC_INDX
  mov al,35h
  out dx,al

  inc edx
  in al,dx
  and al,0F0h

  or al,ah
  out dx,al
 pop eax
 pop edx
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   unhand
; action:  handler for otherwise unhandled exceptions;
;   draws short red diagonal line on SVGA screen
; in:   (nothing)
; out:   (nothing)
; modifies:  (nothing)
; minimum CPU:  386SX
; notes:  DOES NOT RETURN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; write to physical address A0000 instead of virtual address
; LFB_BASE to avoid more page faults
unhand:
 mov ax,LINEAR_SEL
 mov es,ax
 mov byte [es:0A0000h + (WIDTH + 1) * 0],4
 mov byte [es:0A0000h + (WIDTH + 1) * 1],4
 mov byte [es:0A0000h + (WIDTH + 1) * 2],4
 mov byte [es:0A0000h + (WIDTH + 1) * 3],4

 jmp $

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   page_fault
; action:  handler for INT 0Eh (page fault); performs
;   SVGA bank-switching for faults that come
;   from the simulated linear framebuffer
; in:   (nothing)
; out:   (nothing)
; modifies:  (nothing)
; minimum CPU:  386SX
; notes:  This is an interrupt handler, so be careful.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

page_fault:
 pusha

; did it come from the LFB? jump to 'unhand' if not
  mov eax,cr2
  sub eax,LFB_BASE
  jc unhand
  cmp eax, WIDTH*HEIGHT
  jnc unhand  ; some extra checking :)

; set SVGA bank
  mov edx,cr2
  shr edx,16
  mov bh,0  ; bh=0 - set bank no. (VESA only)

  call [bank_switch_fn]

; unmap all pages in the LFB
; xxx - this is slow; need only unmap the pages that are currently mapped
  mov ebx,[fb_page_table]
  mov ecx,1024
unmap:
  and byte [es:ebx],0FEh ; Present=0
  add ebx,4
  loop unmap

; bits 31:22 of faulting adr are page dir index
; bits 21:12 of faulting adr are page table index
  mov ebx,cr2
  shr ebx,12
  and ebx,3FFh
  shl ebx,2
  add ebx,[fb_page_table]

; map in 16 4K pages (one 64K bank)
  mov ecx,16
map_fb:
  or byte [es:ebx],01h ; Present=1
  add ebx,4
  loop map_fb

; we changed the page tables, so reload CR3 to flush the page
; table cache (the TLB). 486+ could use INVLPG instruction instead
  mov eax,cr3
  mov cr3,eax
 popa

; drop error code
 add esp,4
 iret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:   draw_lines
; action:  draw green and blue 'X' on the screen,
;   return after a delay
; in:   (nothing)
; out:   (nothing)
; modifies:  ECX, EBX
; minimum CPU:  386SX
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Note the absence of bank-switching code here. Simple, eh?
; xxx - this code for 256-color screen only

draw_lines:
 mov ecx,HEIGHT
 mov ebx,LFB_BASE + (WIDTH - HEIGHT) / 2
ul_lr:
 mov byte [es:ebx],1 ; blue
 add ebx,WIDTH + 1
 loop ul_lr

 mov ecx,HEIGHT
 mov ebx,LFB_BASE + (WIDTH - HEIGHT) / 2 + HEIGHT
ur_ll:
 mov byte [es:ebx],2 ; green
 add ebx,WIDTH - 1
 loop ur_ll

; xxx - await keypress here -- somehow
 mov ecx,03FFFFFFh
spin:
 loop spin
 ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; MAIN 32-BIT PMODE CODE, here from VCPI
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

from_vcpi:
 mov ax,DATA_SEL
 mov ss,ax
 mov ds,ax
 mov ax,LINEAR_SEL
 mov es,ax
 mov fs,ax
 mov gs,ax

 call draw_lines

; return to V86 mode
 mov eax,0000DE0Ch

; from E-67DE0C of Ralf Brown's list:
;   DS = segment selector mapping entire linear
;  address space obtained via AX=DE01h
; I'm not sure what that means, but I peeked inside EMM386.EXE,
; and it sets DS for me, so I don't have to.

 movzx ebx,word [real_mode_cs]
 mov ebp,esp
 push ebx   ; GS
 push ebx   ; FS
 push ebx   ; DS
 push ebx   ; ES

 push ebx   ; SS
 push ebp   ; ESP
 push dword 00023020h  ; EFLAGS
 push ebx   ; CS
 push dword real4  ; EIP

 call far [vcpi_entry]

; should not reach this
 jmp short $

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; MAIN 32-BIT PMODE CODE, here from "raw" mode (real mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

from_real:
 mov ax,DATA_SEL
 mov ss,ax
 mov ds,ax
 mov ax,LINEAR_SEL
 mov es,ax
 mov fs,ax
 mov gs,ax

 call draw_lines

; back-to-real-mode sequence from 14.5 of 386INTEL.TXT
; 1. TURN OFF PAGING
; 1a. jump to code memory which is identity-mapped
 ; (already done)
; 1b. clear the PG bit
 mov eax,cr0
 and eax,7FFFFFFFh
 mov cr0,eax

; 1c. "Move zeros to CR3 to clear out the paging cache."
 xor eax,eax
 mov cr3,eax

; 2. jump to 16-bit code segment (real2 is above, in the BITS 16 section)
 jmp CODE_SEL16:real2

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DATA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

v86_msg:
 db "CPU is in virtual 8086 mode...", 13, 10, 0

err_msg:
 db "...and I don't know why (no EMM386, no Windows).", 13, 10
 db "Sorry, can't switch to protected mode.", 13, 10, 0

win_msg:
 db "Sorry, this code won't run in a Windows DOS box.", 13, 10
 db "Re-start your computer in 'MS-DOS mode'.", 13, 10, 0

vcpi_msg:
 db "VCPI server detected (probably EMM386).", 13, 10
 db "I will try to use VCPI to switch to protected mode.", 13, 10, 0

vcpi_err_msg:
 db "Error getting VCPI server entry point.", 13, 10, 0

real_msg:
 db "CPU is in real mode.", 13, 10, 0

done_msg:
 db "Back to DOS, wheee!", 13, 10, 0

; Even if you don't use TSS-based task-switching, you need one
; TSS to hold the user stack pointer.
tss:
 dw 0, 0   ; back link

 dd 0   ; ESP0
 dw DATA_SEL, 0  ; SS0, reserved

 dd 0   ; ESP1
 dw 0, 0   ; SS1, reserved

 dd 0   ; ESP2
 dw 0, 0   ; SS2, reserved

 dd 0   ; CR3
 dd 0, 0   ; EIP, EFLAGS
 dd 0, 0, 0, 0  ; EAX, ECX, EDX, EBX
 dd 0, 0, 0, 0  ; ESP, EBP, ESI, EDI
 dw 0, 0   ; ES, reserved
 dw 0, 0   ; CS, reserved
 dw 0, 0   ; SS, reserved
 dw 0, 0   ; DS, reserved
 dw 0, 0   ; FS, reserved
 dw 0, 0   ; GS, reserved
 dw 0, 0   ; LDT, reserved
 dw 0, 0   ; debug, IO perm. bitmap

; null descriptor
gdt:
 dw 0   ; limit 15:0
 dw 0   ; base 15:0
 db 0   ; base 23:16
 db 0   ; type
 db 0   ; limit 19:16, flags
 db 0   ; base 31:24
LINEAR_SEL      equ     $-gdt
 dw 0FFFFh
 dw 0
 db 0
 db 92h   ; present, ring 0, data, expand-up, writable
 db 0CFh   ; page-granular, 32-bit
 db 0
CODE_SEL equ $-gdt
gdt2:
 dw 0FFFFh
 dw 0
 db 0
 db 9Ah   ; present, ring 0, code, non-conforming, readable
 db 0CFh   ; page-granular, 32-bit
 db 0
DATA_SEL equ $-gdt
gdt3:
 dw 0FFFFh
 dw 0
 db 0
 db 92h   ; present, ring 0, data, expand-up, writable
 db 0CFh   ; page-granular, 32-bit
 db 0
CODE_SEL16 equ $-gdt
gdt4:
 dw 0FFFFh
 dw 0
 db 0
 db 9Ah   ; present, ring 0, code, non-conforming, readable
 db 0   ; byte-granular, 16-bit
 db 0
DATA_SEL16 equ $-gdt
gdt5:
 dw 0FFFFh
 dw 0
 db 0
 db 92h   ; present, ring 0, data, expand-up, writable
 db 0   ; byte-granular, 16-bit
 db 0
TSS_SEL  equ $-gdt
gdt6:
 dw 103
 dw 0
 db 0
 db 089h   ; Ring 0 available 32-bit TSS
 db 0
 db 0
VCPI_SEL equ $-gdt
gdt7:
 dd 0, 0   ; dummy descriptors used by VCPI

 dd 0, 0

 dd 0, 0
gdt_end:

idt:
 %rep 14

 dw 0   ; low 16 bits of ISR address
 dw CODE_SEL  ; selector
 db 0   ; word count
 db 8Eh   ; access byte: Present, Ring 0, '386 intr gate
 dw 0   ; high 16 bits of ISR

 %endrep
idt_0e:
 %rep 18

 dw 0   ; low 16 bits of ISR address
 dw CODE_SEL  ; selector
 db 0   ; word count
 db 8Eh   ; access byte: Present, Ring 0, '386 intr gate
 dw 0   ; high 16 bits of ISR

 %endrep
idt_end:

gdt_ptr:
 dw gdt_end - gdt - 1 ; GDT limit
 dd gdt   ; linear, physical address of GDT

idt_ptr:
 dw idt_end - idt - 1 ; IDT limit
 dd idt   ; linear, physical address of IDT

real_idt_ptr:
 dw 3FFh   ; limit 1023
 dd 0   ; IDT (IVT, actually) at address 0

bank_switch_fn:
 dd 0

krnl_page_table:
 dw 0

fb_page_table:
 dd 0

real_mode_ip:
 dw 0
real_mode_cs:
 dw 0

page_info:
 times 4096 db 0         ; padding to 4K boundary
 times 4096 db 0         ; page dir somewhere in here
 times 4096 db 0         ; "framebuffer" page table for SLFB
 times 4096 db 0         ; "kernel" page table for bottom 4 meg

vcpi_entry:
 dd 0
 dw VCPI_SEL

vcpi_control_block:
vcpi_cr3:
 dd 0
vcpi_gdtr:
 dd gdt_ptr
vcpi_idtr:
 dd idt_ptr
vcpi_ldtr:
 dw 0
vcpi_tr:
 dw TSS_SEL
vcpi_eip:
 dd from_vcpi
vcpi_cs:
 dw CODE_SEL

vesa_info:
 db "VBE2"
vesa_minor:
 db 0
vesa_major:
 db 0
 times 506 db 0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值