When the machine is powered on, first force the CS:IP in the CPU to be set to: CS=0xF000, IP=0xFFF0. This is an agreement made between the CPU and BIOS developers, and is the logical starting point for the entire system to run.
0xFFFF0 is the beginning of the ROM area where the BIOS code is located. After the CS:IP points to this, the basic IO device check of the BIOS will be performed. After the BIOS check is completed, the boot device (such as disk, CD-ROM, floppy disk, etc.) The first sector of track 0 on disk 0 is loaded into the memory at 0x07C00. Finally, the BIOS jumps to 0x07C00 and transfers the control to the boot program of the operating system. Note that this 0x07C00 is an agreement of the BIOS, they can freely choose to copy the boot area of the operating system from the disk to that location. For example, in the source code of Seabios: https://git.seabios.org/cgit/seabios.git/tree/src/boot.c, this address is directly hardcoded:
And this 0 disk surface 0 track 1 sector is the boot area, and the 512-byte content of this sector is obtained after the compilation of bootsect.s assembly code.
Next, let's see what this code does:
The first is to define the segment base
addresses of several segments: BOOTSEG: BOOT segment address
INITSEG: Initialization address, in the future, the code of the boot area will be copied here to
SETUPSEG: setup start segment base address
SYSSEG: SYS segment base address
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
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
Next, enter start:
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
go: ···
This code is mainly to copy the 512-byte boot code of the BOOTSEG segment to the segment where INITSEG is located. Note that the movw instruction copies the contents of two bytes at a time, so the initial value of cx is 256.
And finally jmpi go,INITSEG
. The value of INITSEG is given to CS, and the offset of go is given to IP. The reason for this is because the above has completed the work of copying boot to segment 0x9000. There are two identical boot boot codes in the two segments 0x07c0 and 0x9000. INITSEG to the CS segment is transferred to the beginning of 0x9000 want to perform, and to compile amount go to the IP is to make the CPU directly followed by jmpi go,INITSEG
an instruction to continue after.
go: mov ax,cs
mov ds,ax
mov es,ax
! put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
After CS turns to the 0x9000 segment, set the three segment registers of ds, es, and ss successively, and set the value of the sp register at the same time, and set the top of the stack to 0x9FF00.
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
Then, use the 0x13 interrupt to start the second sector of the boot disk, and read the contents of a total of SETUPLEN(4) sectors to the address of es:0x020 (that is, 0x90200).
If the read is correct, jump to ok_load_setup
, otherwise reset dx, ax and execute load_setup in a loop.
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax
! Print some inane message
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#24
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! ok, we've written the message, now
! we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
call read_it
call kill_motor
The next step is to load the SYSTEM part, which is also done using the 0x13 interrupt, and load the SYSTEM part into the segment starting at 0x10000. At the same time, 0x10 will be used to print the prompt string of "Loading system...".
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
After reading the SYSTEM, it is necessary to prepare the machine data such as the root file system. This part is not important.
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
seg cs
mov ax,root_dev
cmp ax,#0
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov ax,#0x021c ! /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
undef_root:
jmp undef_root
root_defined:
seg cs
mov root_dev,ax
Finally, use to jmpi 0,SETUPSEG
jump to the code segment where Setup is located, that is: CS=0x90200, IP=0. And 0x90200 is just from the first to the fifth sector of the boot disk.
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG