boot.asm
; boot.s is loaded at 7c00h by the bios-startup routines, and moves itself ; out of the way to address 90000h, and jumps there. ; ; It then loads the system at 10000h, using BIOS interrupts. Thereafter ; it disables all interrupts, changes to protected mode, and calls the ; start of system. System then must RE-initialize the protected mode in ; it‘s own tables, and enable interrupts as needed. .model small .386p SEGBIOS SEGMENT AT 8 BIOS LABEL BYTE SEGBIOS ENDS BOOTSEG SEGMENT AT 7C0H org 5 BOOT LABEL BYTE BOOTSEG ENDS .code SYSSEG equ 1000h ; system loaded at 10000h (65536). SYSLEN equ 17 ; sectors occupied. start: jmp far ptr BOOT go: mov ax,cs mov ds,ax mov es,ax mov ss,ax mov sp,400h ; we want to load the system (at 10000h) load_system: mov dx,0 mov cx,2 mov ax,SYSSEG mov es,ax mov bx,0 mov ax,200h+SYSLEN int 13h jnc ok_load mov dx,0 mov ax,0 int 13h jmp load_system ok_load: ; now we want to move to protected mode ... cli ; no interrupts allowed ; ; then we load the segment descriptors assume ds:@code mov ax,cs ; right, forgot this at first. didn‘t work mov ds,ax lidt fword ptr idt_48 ; load idt with 0,0 lgdt fword ptr gdt_48 ; load gdt with whatever appropriate ; well, that went ok, I hope. Now we have to reprogram the interrupts ; we put them right after the intel-reserved hardware interrupts, at ; int 20-2F. There they won‘t mess up anything. Sadly IBM really ; messed this up with the original PC, and they haven‘t been able to ; rectify it afterwards. Thus the bios puts interrupts at 08-0f, ; which is used for the internal hardware interrupts as well. We just ; have to reprogram the 8259‘s, and it isn‘t fun. mov al,11h ; initialization sequence out 20h,al ; send it to 8259A-1 out 0A0h,al ; and to 8259A-2 mov al,20h ; start of hardware int‘s (20h) out 21h,al mov al,28h ; start of hardware int‘s 2 (28h) out 0A1h,al mov al,04h ; 8259-1 is master out 21h,al mov al,02h ; 8259-2 is slave out 0A1h,al mov al,01h ; 8086 mode for both out 21h,al out 0A1h,al mov al,0FFh ; mask off all interrupts for now out 21h,al out 0A1h,al ; well, that certainly wasn‘t fun :-(. Hopefully it works, and we don‘t ; need no steenking BIOS anyway (except for the initial loading :-). ; The BIOS-routine wants lots of unnecessary data, and it‘s less ; "interesting" anyway. This is how REAL programmers do it. ; ; Well, now‘s the time to actually move into protected mode. To make ; things as simple as possible, we do no register set-up or anything, ; we let the gnu-compiled 32-bit programs do that. We just jump to ; absolute address 00000, in 32-bit protected mode. mov bx,SYSSEG ; loaded place. mov ax,0001h ; protected mode (PE) bit lmsw ax ; This is it; jmp FAR PTR bios ; jmp offset 0 of segment 8 (cs) gdt: dw 0,0,0,0 ; dummy dw 07FFh ; 8Mb - limit=2047 (2048*4096=8Mb) dw 0000h ; base address=10000h dw 9A01h ; code read/exec dw 00C0h ; granularity=4096 dw 07FFh ; 8Mb - limit=2047 (2048*4096=8Mb) dw 0000h ; base address=10000h dw 9201h ; data read/write dw 00C0h ; granularity=4096 idt_48: dw 0 ; idt limit=0 dw 0,0 ; idt base=0L gdt_48: dw 7ffh ; gdt limit=2048, 256 GDT entries dw 7c00h+gdt,0 ; gdt base = 07xxx msg1: db 13,10 db "Loading system ..." db 13,10,13,10 org 510 db 55h,0AAh end start
head.asm
; ; head.s contains the 32-bit startup code. ; Two L3 task multitasking. The code of tasks are in kernel area, ; just like the Linux. The kernel code is located at 0x10000. ; KRN_BASE = 10000h TSS0_SEL = 20h LDT0_SEL = 28h TSS1_SEL = 30h LDT1_SEL = 38h .386p .model huge ts0seg segment at 20h ts0lab label byte ts0seg ends ts1seg segment at 30h ts1lab label byte ts1seg ends .code startup_32: assume ds:@code mov eax,10h mov ds,eax mov es,eax mov fs,eax mov gs,eax lss esp,FWORD PTR [stack_ptr] ; setup base fields of descriptors. mov ebx,KRN_BASE mov ecx,gdt lea eax,tss0 mov edi,TSS0_SEL call set_base lea eax,ldt0 mov edi,LDT0_SEL call set_base lea eax,tss1 mov edi,TSS1_SEL call set_base lea eax,ldt1 mov edi,LDT1_SEL call set_base call setup_idt call setup_gdt mov eax,10h ; reload all the segment registers mov ds,eax ; after changing gdt. mov es,eax mov fs,eax mov gs,eax lss esp,FWORD PTR [stack_ptr] ;设置8253定时芯片。把计数器通道0设置成每隔10毫秒向中断控制器发送一个中断请求信号 ;8253具有3个独立的计数通道,采用减1计数方式。 ;方式3为:方波发生器,最适合计算机。 ; setup up timer 8253 chip. mov al,36h ;设置通道0工作在方式3 mov edx,43h ;8253芯片控制寄存器写端口 out dx,al mov eax,11930 ; timer frequency 100 HZ 8253芯片的时钟输入频率是1.193180mhz mov edx,40h ;通道0的端口 out dx,al ;分两次把初始计数值写入通道0 mov al,ah out dx,al ; setup timer & system call interrupt descriptors. mov eax,00080000h mov ax,1A0h;timer_interrupt mov dx,08E00h mov ecx,20h lea esi,[ecx*8+idt] mov [esi],eax mov [esi+4],edx mov ax,1e4h;[system_interrupt] mov dx,0ef00h mov ecx,080h lea esi,[ecx*8+idt] mov [esi],eax mov [esi+4],edx ; unmask the timer interrupt. mov edx,21h in al,dx and al,0feh out dx,al ; Move to user mode (task 0) db 9ch;pushf and dword ptr [esp],0ffffbfffh db 9dh;popf mov eax,TSS0_SEL ltr ax mov eax,LDT0_SEL lldt ax mov dword ptr [current],0 sti push 17h push stack0_ptr db 9ch;pushf push 0fh push task0 db 0cfh;iret setup_gdt: lgdt fword ptr [lgdt_opcode] ret setup_idt: lea edx,ignore_int mov eax,00080000h mov ax,dx ; selector = 0x0008 = cs mov dx,8E00h ; interrupt gate - dpl=0, present lea edi,idt mov ecx,256 rp_sidt: mov [edi],eax mov [edi+4],edx add edi,8 dec ecx jne rp_sidt lidt fword ptr [lidt_opcode] ret ; in: eax - logic addr; ebx = base addr ; ; ecx - table addr; edi - descriptors offset. set_base: add eax,ebx add edi,ecx mov [edi+2],ax ror eax,16 mov [edi+4],al mov [edi+7],ah ror eax,16 ret write_char: push gs push ebx push eax mov ebx,18h mov gs,bx mov ebx,[scr_loc] shl ebx,1 assume gs:nothing mov gs:[ebx],al shr ebx,1 inc ebx cmp ebx,2000 jb f mov ebx,0 f: mov scr_loc,ebx pop eax pop ebx pop gs ret ; This is the default interrupt "handler" align DWORD ignore_int: push ds push eax mov eax,10h mov ds,eax mov eax,67 ; print ‘C‘ call write_char pop eax pop ds db 0cfh;iret ;Timer interrupt handler ;align dword dw 0 timer_interrupt: push ds push edx push ecx push ebx push eax mov eax,10h mov ds,eax mov al,20h out 20h,al ;允许其他硬件中断,则向8253发送EOI命令 mov eax,1 cmp current,eax je ts0 mov [current],eax jmp far ptr ts1lab jmp ts2 ts0: mov dword ptr [current],0 ;若当前任务是1,则把0存入current,并跳转到任务0 jmp far ptr ts0lab ts2: pop eax pop ebx pop ecx pop edx pop ds db 0cfh;iret ;system call handler align DWORD system_interrupt: push ds push edx push ecx push ebx push eax mov edx,10h mov ds,edx call write_char pop eax pop ebx pop ecx pop edx pop ds db 0cfh;iret current dd 0 scr_loc dd 0 align DWORD dw 0 lidt_opcode: dw 256*8-1 ; idt contains 256 entries dd idt + KRN_BASE ; This will be rewrite by code. align DWORD dw 0 lgdt_opcode: dw (end_gdt-gdt)-1 ; so does gdt dd gdt + KRN_BASE ; This will be rewrite by code. align DWORD dd 0 idt: dd 512 dup(0) ; idt is uninitialized gdt: dd 00000000h ; NULL descriptor dd 00000000h dd 000007ffh ; 8Mb 0x08, base = 0x10000 dd 00c09a01h dd 000007ffh ; 8Mb 0x10 dd 00c09201h dd 80000002h ; screen 0x18 - for display dd 00c0920bh dd 00000068h ; TSS0 descr 0x20 dd 0000e901h dd 00000040h ; LDT0 descr 0x28 dd 0000e201h dd 00000068h ; TSS1 descr 0x30 dd 0000e901h dd 00000040h ; LDT1 descr 0x38 dd 0000e201h end_gdt: dd 128 dup(0) stack_ptr: dd stack_ptr dw 10h align DWORD ldt0: dd 00000000h dd 00000000h dd 000003ffh ; 0x0f, base = 0x10000 dd 00c0fa01h dd 000003ffh ; 0x17 dd 00c0f201h tss0: dd 0 ; back link dd stack0_krn_ptr,10h ; esp0, ss0 dd 0, 0 ; esp1, ss1 dd 0, 0 ; esp2, ss2 dd 0 ; cr3 dd task0 ; eip dd 200h ; eflags dd 0, 0, 0, 0 ; eax, ecx, edx, ebx dd stack0_ptr,0, 0, 0 ; esp, ebp, esi, edi dd 17h,0fh,17h,17h,17h,17h ; es, cs, ss, ds, fs, gs dd LDT0_SEL ; ldt dd 8000000h ; trace bitmap dd 128 dup(0) stack0_krn_ptr: dd 0 align DWORD dd 0 ldt1: dd 00000000h dd 00000000h dd 000003ffh ; 0x0f, base = 0x10000 dd 00c0fa01h dd 000003ffh ; 0x17 dd 00c0f201h tss1: dd 0 ; back link dd stack1_krn_ptr, 10h ; esp0, ss0 dd 0, 0 ; esp1, ss1 dd 0, 0 ; esp2, ss2 dd 0 ; cr3 dd task1 ; eip dd 200h ; eflags dd 0, 0, 0, 0 ; eax, ecx, edx, ebx dd stack1_ptr, 0, 0, 0 ; esp, ebp, esi, edi dd 17h,0fh,17h,17h,17h,17h ; es, cs, ss, ds, fs, gs dd LDT1_SEL ; ldt dd 8000000h ; trace bitmap dd 128 dup(0) stack1_krn_ptr: dd 0 task0: mov eax,17h mov ds,ax mov eax,65 ; print ‘A‘ int 80h mov ecx,0fffh s0: loop s0 jmp task0 dd 128 dup(0) stack0_ptr: dd 0 task1: mov eax,17h mov ds,ax mov eax,66 ; print ‘B‘ int 80h mov ecx,0fffh s1: loop s1 jmp task1 dd 128 dup(0) stack1_ptr: dd 0 org 2000h-2 dw 0 end startup_32