After booting, the bios is powered on, and the bios will copy the 512 bytes of the first sector of the boot disk to the location 0x7c00. Then the register CS:IP of CUP will point to the position of 0x7c00 and start running.
And the first sector puts the content of bootsect.s, that is, it starts to run from bootsect.s. That is to execute the initial command of the operating system.
bootsect.s
Here is the source code:
// boot/bootsect.s SETUPLEN = 4 ; nr of setup-sectors BOOTSEG = 0x07c0 ; original address of boot-sector INITSEG = 0x9000 ; we move boot here - out of the way SETUPSEG = 0x9020 ; setup starts here SYSSEG = 0x1000 ; system loaded at 0x10000 (65536). ENDSEG = SYSSEG + SYSSIZE ; where to stop loading ; ROOT_DEV: 0x000 - same type of floppy as boot. ; 0x301 - first partition on first drive etc. ROOT_DEV = 0x306 entry start start: mov ax, #BOOTSEG mov ds,ax mov ax, #INITSEG mov es,ax mov cx,#256 sub si,si sub di, di rep movw jmpi go, INITSEG
The above behavior is to move the 512-byte code originally at 0x7c00 to 0x90000.
jmpi go,INITSEG // Jump to 0x90000: go execution //Where go is a symbol, it will be converted to the relative offset address of the current file when compiling, //So it is executed in the place where go goes. go: mov ax,cs // cs = 0x9000 mov ds,ax // ds = 0x9000 mov es,ax // es = 0x9000 ; put stack at 0x9ff00. mov ss,ax // ss = 0x9000 mov sp,#0xFF00 ; arbitrary value >>512 // ss:sp = 0x9FF00 stack bottom position
The main thing that this code of go does is to assign values to each register. Review the role of the register:
ax | bx | cx | dx |
---|---|---|---|
cs code segment | ds data segment | ss stack segment | es additional segment |
sp stack segment pointer | bp base address pointer | si source index | di destination address |
ip instruction pointer | flags flag | idtr interrupt descriptor table index | gdtr global descriptor table index |
Here the values of various registers are set. Next continue:
load_setup: mov dx,#0x0000 ; drive 0, head 0 mov cx,#0x0002 ; sector 2, track 0 mov bx,#0x0200 ; address = 512, in INITSEG mov ax,#0x0200 + SETUPLEN ; service 2, nr of sectors int 0x13 ; read it jnc ok_load_setup ; ok - continue mov dx,#0x0000 mov ax,#0x0000 ; reset the diskette int 0x13 j load_setup
int 0x13 is an interrupt, and the assignment of the first four registers are all parameters for this interrupt program. Here it is constantly trying to load 2-5 sectors of the disk at address 0x90200. ok jumps to ok_load_setup to execute. Otherwise keep repeating.
ok_load_setup: ; Get disk drive parameters, specifically nr of sectors/track ... ; Print some inane message ... mov ax, #SYSSEG mov es,ax ; segment of 0x010000 call read_it ...
The first part of this part is to read the disk information and call int 0x13 interrupt. Then load the 240 sectors from the 6th sector to 0x10000. Finally start to jump, enter setup.s to execute:
jmpi 0,SETUPSEG // 0x90200 Enter the code part of setupseg;
setup.s
INITSEG = 0x9000 ; we move boot here - out of the way SYSSEG = 0x1000 ; system loaded at 0x10000 (65536). SETUPSEG = 0x9020 ; this is the current segment .globl begtext, begdata, begbss, endtext, enddata, endbss .text begtext: .data begdata: .bss begbss: .text entry start start: ; ok, the read went well so we get current cursor position and save it for ; posterity. mov ax,#INITSEG ; this is done in bootsect already, but... ax = 0x9000 mov ds,ax // ds = 0x9000 mov ah,#0x03 ; read cursor pos xor bh, bh int 0x10 ; save it in known place, con_init fetches mov [0],dx ; it from 0x90000.
int 0x10 The interrupt calls the display service interrupt routine. The following is to obtain various information:
; Get memory size (extended mem, kB) memory information mov ah,#0x88 int 0x15 mov[2],ax ; Get video-card data: video card information mov ah,#0x0f int 0x10 mov [4],bx ; bh = display page mov [6],ax ; al = video mode, ah = window width ; check for EGA/VGA and some config parameters mov ah,#0x12 mov bl,#0x10 int 0x10 mov[8],ax mov[10],bx mov[12],cx ; Get hd0 data No.1 HDD information mov ax,#0x0000 mov ds,ax lds si,[4*0x41] mov ax, #INITSEG mov es,ax mov di,#0x0080 mov cx,#0x10 rep movsb ; Get hd1 data 2 hard disk information mov ax,#0x0000 mov ds,ax lds si,[4*0x46] mov ax, #INITSEG mov es,ax mov di,#0x0090 mov cx,#0x10 rep movsb
If executed as above, various information can be stored in memory, and their distribution in memory is as follows:
Address | Length | Remarks | |
---|---|---|---|
0x90000 | 2 | Cursor position | |
0x90002 | 2 | extended memory Number | |
0x90004 | 2 | display page | |
0x90006 | 1 | display mode | |
0x90007 | 1 | number of character columns | td> |
0x90008 | 2 | Unknown | |
0x9000A | 1 | display memory | |
0x9000B | 1 | display status | |
0x9000C | 2 | Graphic card characteristic parameters | |
0x9000E | 1 | Screen rows | |
0x9000F | 1 | Screen columns | |
0x90080 | 16 | hard disk 1 parameter list | |
0x90090 | 16 | hard disk 2 parameter table | |
0x901FC | 2 | root device number |
The next thing to do is to overwrite the interrupt vector table of the BIOS, and introduce the interrupt table of the operating system itself. At the same time, move the memory to another location.
; now we want to move to protected mode... cli ; no interrupts allowed ; first turn off interrupts; ; first we move the system to it's rightful place mov ax,#0x0000 cld ; 'direction'=0, movs moves forward do_move: mov es,ax ; destination segment add ax,#0x1000 cmp ax,#0x9000 jz end_move mov ds,ax ; source segment sub di, di sub si,si mov cx,#0x8000 rep movsw jmp do_move
The content here is to move the memory from 0x10000 to 0x90000 to the beginning, and directly overwrite the original bios information: (the picture below is found from the Internet)
This organizes most of the memory distribution.
memory address | content |
---|---|
Stack top address | |
0x90200- | setup |
0x90000-0x901FF | Temporary variable |
0x0 – 0x80000 | The content of the operating system (sector 6-266) |
Enter protected mode in the next section.