进行了VESA编程试验之二。启动了分页机制,在保护模式下访问图形模式的显存。加载了IDT。
借用了赵炯博士的linux-0.00-redhat的boot.s代码。编译要用minix所用的as86和ld86(apt-get install bin86)。boot.s构成了
512字节的引导扇区,在ld86链接之后要跳过32字节的a.out头。其他的代码(视为loader)添加在其之后构成boot.img,在512字节之外。
boot.s读取loader到0x10000,然后被移动到0x1000处。之后jmp 0x100:0跳到它。
boot.s( for bochs ):
! boot.s
!
! It then loads the system at 0x10000, using BIOS interrupts. Thereafter
! it disables all interrupts, changes to protected mode, and calls the
BOOTSEG = 0x07c0
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
SYSLEN = 18 ! sectors occupied.
entry start
start:
jmpi go,#BOOTSEG
go: mov ax,cs
mov ds,ax
mov ss,ax
mov sp,#0x400 ! arbitrary value >>512
! ok, we've written the message, now
load_system:
mov dx,#0x0000
mov cx,#0x0002
mov ax,#SYSSEG
mov es,ax
xor bx,bx
mov ax,#0x200+SYSLEN
int 0x13
!jmp 0x0:0
!jmp 0x1000:0
jnc ok_load
! now we want to move to protected mode ...
ok_load:
cli ! no interrupts allowed !
mov ax, #SYSSEG
mov ds, ax
!xor ax, ax
mov ax, #0x100
mov es, ax
mov cx, #0x2000
sub si,si
sub di,di
rep
movw
jmp 0x100:0
mov ax, #BOOTSEG
mov ds, ax
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate
! absolute address 0x00000, in 32-bit protected mode.
mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is it!
jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
gdt: .word 0,0,0,0 ! dummy
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0x00000
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0x00000
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
idt_48: .word 0 ! idt limit=0
.word 0,0 ! idt base=0L
gdt_48: .word 0x7ff ! gdt limit=2048, 256 GDT entries
.word 0x7c00+gdt,0 ! gdt base = 07xxx
.org 510
.word 0xAA55
boot3.S中打开了分页机制,现在的做法是试验性的,把线性地址映射到同样的物理地址。页目录存放在地址0x200000起始处,
页表存放在$0x201000起始处。 (lidt idt_48不知为何未能成功,只好从栈上内存加载6个字节。)
boot3.S( for bochs ):
.code16gcc
.global _start
.text
.align 4
_start:
###### A20 ########
pushl %eax
pushl %ecx # needed to save, (demo code is like this)
cli
xor %eax, %eax
#movl $0, number
movw $0x2401 , %ax
#movw $0x2400 , %ax
int $0x15 #al 00-disabled 01-enabled
movw $0x2402 , %ax
int $0x15 #al 00-disabled 01-enabled
sti
popl %ecx
popl %eax
###################
##### VESA #######
pushw %di
pushl %eax
pushl %ecx
pushw %bx
movw $0x8000, %di
movw $0x4115, %bx
movw $0x4f02, %ax
int $0x10
movb %ah, number
popw %bx
popl %ecx
popl %eax
popw %di
#########################
######## protected mode ########
cli
xor %ax, %ax
mov %ax, %ds
call setup_idt
lgdt gdt_48
movl %cr0, %eax
orl $1 , %eax
movl %eax, %cr0
ljmp $0x8, $protcseg
#################################
####### write video ram ####
.code32
write_vram:
sti
pushl %eax
pushw %ds
pushw %di
movl $0xe0000000, %eax
addl $720000, %eax
movw %fs, %cx
movw %cx, %di
calll main2
jmp . # remove these two lines to show assembly drawing
movl $800, %ecx
loop:
movl $0x00ff0000, (%eax)
addl $3, %eax
subl $1, %ecx
cmp $0, %ecx
jnz loop
popw %di
popw %ds
popl %eax
jmp .
##################
.code16gcc
##### base code ###
base:
pushw %ds
pushw %es
pushw %ss
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
pushw %sp
movw $stack, %ax
movw %ax, %sp
calll main2
popw %sp
movw $stack, %sp
popw %ss
popw %es
popw %ds
# movw $0x4c00, %ax
# int $0x21 #return to DOS
hlt
message:
.string "Hello, world!\n\0"
.global display_str
display_str:
.org .+0x40
stack:
.code32
protcseg:
sti
movw $0x10, %ax
movw %ax, %ds # this is indispensable
movw %ax, %ss
######## paging ###################
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
movl $1024, %edx
movl $0x201, %eax
shl $12, %eax
orl $0x7, %eax
movl $0x200000, %ebx
movl $4096, %ecx
loop_page_dir:
movl %eax , (%ebx)
addl %ecx, %eax
addl $4, %ebx
subl $1, %edx
cmp $0, %edx
jnz loop_page_dir
####
movl $(1024*1024), %edx
movl $0, %eax
shl $12, %eax
orl $0x7, %eax
movl $0x201000, %ebx
movl $4096, %ecx
loop_page_table:
movl %eax , (%ebx)
addl %ecx, %eax
addl $4, %ebx
subl $1, %edx
cmp $0, %edx
jnz loop_page_table
popl %edx
popl %ecx
popl %ebx
popl %eax
mov $0x200000, %eax
mov %eax, %cr3
mov %cr0, %eax
or $0x80000000, %eax
mov %eax, %cr0
###########################
######### return to real mode ######
#.code16gcc
# cli
# movl %cr0, %eax
# andl $0xfffffffe , %eax
# movl %eax, %cr0
#jmp $0x0, $write_vram #ljmp $0x08, $write_vram #jmp write_vram
jmp write_vram #ljmp $0x08, $write_vram #jmp write_vram
####################################
.code16gcc
setup_idt:
lea ignore_int, %edx
movl $0x00080000, %eax
movw %dx,%ax
movw $0x8e00, %dx
lea idt, %edi
mov $256, %ecx
rp_idt: movl %eax,(%edi)
movl %edx, 4(%edi)
addl $8, %edi
dec %ecx
jne rp_idt
movl $0, -4(%esp)
movl $0, -8(%esp)
lea idt, %edi
movl %edi, -4(%esp)
movw $2047,-6(%esp)
lidt -6(%esp)
# lidt idt_48
retw
#setup_gdt:
# movl $0, -4(%esp)
# movl $0, -8(%esp)
# lea gdt, %edi
# movl %edi, -4(%esp)
# movw $0x1f,-6(%esp)
# lgdt -6(%esp)
# retw
.align 8
ignore_int:
push %ds
pushl %eax
movl $0x10, %eax
mov %ax, %ds
movl $67, %eax
#call write_char
popl %eax
pop %ds
iretw
.code32
gdt: .word 0,0,0,0
.word 0xffff
.word 0x0000
.word 0x9a00
.word 0x00cf
.word 0xffff
.word 0x0000
.word 0x9200
.word 0x00cf
.word 0,0,0,0
gdt_48: .word 0x1f
.long gdt
.data
.align 8
idt: .fill 256,8,0
idt_48:
.word 256*8-1
.long idt
boot3.c( for bochs ):
__asm__(".code32\n");
void display_str(char* str, short length);
int number ;
int number2;
void main2()
{
char* str;
number2 = number;
char* pAddress = (char*)0xe0000000;
pAddress +=300*800 + 100*3;
for(int i = 0; i < 2400-200*3; i++)
{
*pAddress = 255;
pAddress += 1;
}
while(1)
;
}
编译脚本如下,loader链接指定起始位置为0x1000,即其被移动到的位置:
make3.sh(for bochs):
#!/bin/bash
rm -f boot *.o *.out boot.img disassem.asm
as86 -o boot.o boot.s
ld86 -s -o boot boot.o
as --32 boot3.S -o boot3_S.o
gcc -c -m32 boot3.c -o boot3_c.o
ld -Ttext 0x1000 -m elf_i386 boot3_S.o boot3_c.o -o boot.elf
#ld -Ttext 0x7c00 -m elf_i386 boot3_S.o boot3_c.o -o boot.elf
objdump -D -m i8086 boot.elf > disassem.asm
objcopy -j .text -j .rodata -O binary boot.elf boot.out
dd bs=32 if=boot of=Image skip=1
cat boot.out >> Image
mv Image boot.img
运行bochs配置文件如下:
bochsrc(for bochs):
config_interface: textconfig
#display_library: sdl
#romimage: file=/usr/share/bochs/BIOS-bochs-latest, address=0xf0000
romimage: file=/usr/share/bochs/BIOS-bochs-latest
megs: 32
vgaromimage: file=/usr/share/vgabios/vgabios.bin
floppya: type=1_44, 1_44="boot.img", status=inserted
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata1: enabled=0, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
#ata0-master: type=disk, path="", mode=flat, cylinders=1024, heads=16, spt=63
ata0-slave: type=cdrom, path="/dev/cdrom", status=inserted
boot: floppy
#ips: 1000000
floppy_bootsig_check: disabled=0
log: /dev/stdout
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
debugger_log: -
com1: enabled=1, dev=/dev/ttyS0
parport1: enabled=1, file="/dev/lp0"
sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=/dev/stdout, dmatimer=600000
vga_update_interval: 300000
keyboard_serial_delay: 250
keyboard_paste_delay: 100000
#floppy_command_delay: 500
mouse: enabled=1
private_colormap: enabled=0
#ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
#keyboard_mapping: enabled=0, map=/usr/share/bochs/keymaps/x11-pc-de.map
#keyboard_type: mf
#user_shortcut: keys=ctrlaltdel
#magic_break: enabled=1
#cmosimage: cmos.img
#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log
#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img
#i440fxsupport: enabled=1
#usb1: enabled=1, ioaddr=0xFF80, irq=10
#text_snapshot_check: enable
运行命令:bochs -f ./bochsrc。Bochs调试命令:info tab及page linear_address。
效果图和以前是一样的。如前图:
(汇编版)
(c语言版)
至此试验之二就告完成。结论是:启动虚拟存储后,可写0xe0000000线性地址的VESA (LFB模式的)显存。
仍然参阅了试验一中的书籍和一些网文等,
表示谢意。
源文件打包在此:
boot3_bochs_0.9994.tgz。