Source: Linux kernel 0.11 full comments
! Bootsect. s 2011-04-10
! Setup. S is responsible for Retrieving System data from the bios and placing the data in the appropriate location of the system memory.
! At this time, setup. s and system have been loaded to the memory by bootsect boot.
! This code asks about BIOS memory/Disk/other parameters and puts these parameters in a "safe" place:
! 0x90000-0x901ff, that is, the place where the original bootsect code block was located, and before it was overwritten by the buffer Block
! Read by system in Protected Mode
Initseg = 0x9000
Sysseg = 0x1000
Setupseg = 0x9020
. Global begtext, begdata, begbss, endtext, enddata, endbss
. Text
Begtext:
. Data:
Begdata:
. BSS
Begbss:
. Text
Entry start
Start:
! OK. The entire disk reading process is normal. Now save the cursor position for future use.
MoV ax, # initseg
MoV ds, ax
MoV ah, #0x03! BiOS interrupts the read cursor function of 0x10. Ah = 0x03
! Input: bH = page number
! Return: CH = scanning start line Cl = scanning slave line
! DH = row number (0x00 is the top) dL = column number (0x00 is the left side)
Xor bh, BH
Int 0x10
MoV [0], dx! Store the cursor position information at 0x90000.
! The following three lines of code are used to obtain the extended memory size (KB)
! Call interrupt No. 0x15, function no. Ah = 0x88
! Returns the size of the expanded memory (Kb) starting from 0x100000 (1 MB)
! If an error occurs, set the CF bit, Ax = Error Code
MoV ah, #0x88
Int 0x15
MoV [2], ax! Store the extended memory value at 0x90002 (1 word)
! The following section is used to obtain the current display mode of the display card.
! 0x10 for BIOS interruption, function ID Ah = 0x0f
! Return: Ah = character series Al = display mode bH = current display page
! 0x90004 (1 Word) stores the current page, 0x90006 display mode 0x90007 character series
MoV ah, # 0x0f
Int 0x10
Mov [4], bx
Mov [6], ax
! Check the display mode (Measures/VGA) and take the parameters
! Call the BIOS to interrupt 0x10. Select the "Retrieve Method" option for the additional function.
! Function No.: ah = 0x12 bl = 0x10
! Return: bh = display status
! (0x00-color mode, I/O port = 0x3dX)
! (0x01-monochrome mode, I/O port = 0x3bx)
! Bl = installed Display memory
! (0x00-64 k, 0x01-128 k, 0x02-192 k, 0x03-256 k)
! Cx = display card feature parameters
Mov ah, #0x12
Mov bl, #0x10
Int 0x10
Mov [8], ax
Mov [10], bx! 0x9000A = Display memory 0x9000B = display status (color/monochrome)
Mov [12], cx! 0x9000C = display card feature parameters
! Take the information of the first hard disk (copy the hard disk parameter table)
! The first address of the 1st hard disk parameter table is the Vector Value of the interrupt vector 0x41.
! The 2nd hard disk parameters are followed by the 1st tables. The Vector Value of the interrupt vector 0x46 also points to the first address of the 2nd hard disk parameter table.
! The table length is 16 bytes (0x10)
! The following two procedures copy the BIOS parameter table about two hard disks:
! 0x90080: tables with 1st hard disks 0x90090: tables with 2nd Hard Disks
Mov ax, #0x0000
Mov ds, ax
Lds si, [4*0x41]! Take the value of the interrupt vector 0x41, that is, the address of the hd0 parameter table-> ds: si
Mov ax, # INITSEG
Mov es, ax
Mov di, #0x0080! Destination Address of the transfer: 0x9000: 0x0080-> es: di
Mov cx, #0x10! 0x10 bytes in total
Rep
Movsb
Mov ax, #0x0000
Mov ds, ax
Lds si, [4*0x46]
Mov ax, # INITSEG
Mov es, ax
Mov di, #0x0090
Mov cx, #0x10
Rep
Movsb
! Check whether the system has 2nd hard disks. If not, the 2nd tables are cleared.
! Use BIOS to interrupt the call to the disk picking function of 0x13
! Function number ah = 0x15:
! Input: dl = drive letter (0x8X indicates hard disk 0x80 indicates 1st hard disks 0x81 2nd hard disks)
! Output: ah = type code; 00 -- this disk is not available, CF is set
! 01 -- soft drive, not supported by changge-line
! 02 -- soft drive (or other removable devices), supported by change-line
! 03 -- Hard Disk
Mov ax, #0x01500
Mov dl, #0x81
Int 0x13
Jc no_disk1
Cmp ah, #3! Is it a hard disk?
Je is_disk1
No_disk1:
Mov ax, # INITSEG! If 2nd hard disks do not exist, the 2nd hard disk tables are cleared.
Mov es, ax
Mov di, #0x0090
Mov cx, #0x10
Mov ax, #0x00
Rep
Stosb
Is_disk1:
! Now we are entering the protection mode...
Cli! Interruption is not allowed at this time.
! We move the system module to the correct position.
! The bootsect pilot program reads the system module from the starting position of 0x10000. Assume that the system module
! The maximum length is no more than 0x80000, that is, its end does not exceed the memory address 0x90000, so bootsect will
! Move yourself to the address starting from 0x90000 and load setup to the end of it.
! The user of the following program moves the entire system module to the 0x0000 position, that is, the user of 0x10000 to 0x8ffff
! Memory block (512 k), the whole block moves to the low end of the memory 0x10000 (64 k) Position
Mov ax, #0x0000
Cld
Do_move:
Mov es, ax! Es: di --> Destination Address (initially 0x0000: 0x0)
Add ax, #0x1000
Cmp ax, #0x9000! The 64 K code starting from 0x8000 has been moved
Jz end_move
Mov ds, ax! Ds: si --> source address (initially 0x1000: 0x0)
Sub di, di
Sub si, si
Mov cx, #0x8000! Move 0 x characters (64 K bytes)
Rep
Movsw
Jmp do_move
! After that, we will load the segment descriptor
! The lidt command is used to load the Interrupt Descriptor Table (idt) registers. Its operands are 6 bytes:
! 0-1 bytes are the table length values; 2-5 bytes are the 32-bit linear base address (first address) of the table ).
! Each table item in the interrupt description table (8 bytes) indicates the information of the Code to be called when an interrupt occurs.
! The lgdt command is used to load the gdt register, which is in the same format as the lidt command.
! Each descriptor Item (8 bytes) in the Global Descriptor Table describes the information of data and code segments in protected mode.
! Including the maximum length limit of segments (16 bits), linear base address of segments (32 bits), privileged level of segments, whether segments are in memory,
! Read/write licenses and other protection Modes
End_move:
Mov ax, # SETUPSEG
Mov ds, ax
Lidt idt_48! Load the Interrupt Descriptor Table (idt) Register. idt_48 is the position of the 6-byte operand.
Lgdt gdt_48! Load the gdt register. gtd_48 is the position of the 6-byte operand.
! The above operation is very simple. Now we open the A20 address line.
Call empty_8042! Wait for the input buffer to be empty
! You can write commands only when the input buffer is empty.
Mov al, #0xD1! 0xD1 command code-indicates to write data to port P2 of port 8042.
! Port P2 bit 1 is used to select the A20 line
! Data must be written to port 0x60
Call empty_8042! Wait for the input buffer to be empty and check whether the command is accepted
Mov al, # 0xDF! Select the A20 address line parameter
Out #0x60, al
Call empty_8042! If the input buffer is empty, the A20 line has been selected
! If everything above is normal, now we have to program the interrupt again
! We put them right behind intel-retained hardware interruptions, at int 0x20-0x2F.
! Where they do not cause conflicts. Unfortunately, IBM screwed up in the original PC and won't be corrected in the future.
! The bios of the PC has placed the interrupt on 0x08-0x0f, which is also used for internal hardware interruption.
! So we have to re-program the 8259 interrupt controller, which is useless!
MoV Al, #0x11! 0x11 indicates that the initialization command starts. It is an icw1 command.
! Indicates edge triggering, multi-chip cascade of 8259, And the last icw4 command word to be sent
Out #0x20, Al! Sent to the 8259a main chip
! The two words defined below are two relative jump commands directly represented by machine code, which play a delayed role
! 0xeb is the operation code of the direct near jump command, with a relative shift value of 1 byte. Therefore, the jump range is-127-127.
! The CPU adds the relative displacement value to the EIP register to form a new valid address. In this case, the EIP points
! The next command to be executed. The number of CPU clock cycles consumed during execution is 7-10.
! 0x00eb indicates an instruction with a jump value of 0, so the next instruction is executed directly. These two commands can provide a total
! Latency of 14-20 CPU clock cycles. In as86, the mnemonic of the corresponding command is not displayed. Therefore, Linus
! In some assembler programs, machine code is directly used to represent such commands.
! In addition, the number of clock cycles for each null Operation Command NOP is three. Therefore, to achieve the same latency, 6-7 NOP commands are required.
. Word 0x00eb, 0x00eb
Out #0xA0, al! Then sent to the 8259A slave Chip
. Word 0x00eb, 0x00eb
Mov al, #0x20
Out #0x21, al! The ICW2 command for the master chip. The starting interrupt number must be an odd address.
. Word 0x00eb, 0x00eb
Mov al, #0x28
Out #0xA1, al! The ICW2 command word sent from the chip, starting from the interrupt Number of the chip
Word 0x00eb, 0x00eb
Mov al, #0x04! 8259-1 is master
Out #0x21, Al! Sends the icw3 command to the master chip. The ir2 of the master chip is connected to the slave chip Int.
. Word 0x00eb, 0x00eb
MoV Al, #0x02! 8259-2 is slave
Out #0xa1, Al! Sends the icw3 command from the chip, indicating that the int from the chip is connected to the ir2 pin of the main chip.
. Word 0x00eb, 0x00eb
MoV Al, #0x01! 8086 mode for both
Out #0x21, Al! Send the icw4 command to the master chip. 8086 mode: normal EOI Mode
! You need to send a command to reset it. Initialization is complete and the chip is ready
. Word 0x00eb, 0x00eb
Out #0xa1, Al! The icw4 command word sent from the chip. The content is the same as above.
. Word 0x00eb, 0x00eb
MoV Al, # 0xff
Out #0x21, Al! Shield all interrupt requests from the master chip
. Word 0x00eb, 0x00eb
Out #0xa1, Al! Shield all interrupt requests from the chip
! Set to run in 32-bit protection mode. First load the machine status word, also known as the control register Cr0, its bit 0
! Setting 1 causes the CPU to work in the protection mode.
MoV ax, #0x0001
Lmsw ax! Load the machine status word in this way.
Jmpi 0, 8! Jump to CS Segment 8, offset 0
! We have moved the system module to the starting point of 0x00000, so the offset here is 0. Here 8 of the segment Value
! It is already a protection mode segment selector, used to select the descriptor table and Descriptor Table items as well as the required privileged level.
! Segment selection character length is 16 bits (2 bytes); bit 0-1 indicates the request's privileged level 0-3, the Linux operating system only uses:
! Level 0 (system level) and level 3 (user level); bit 2 is used to select Global Descriptor Table (0) or Local Descriptor Table (1 );
! 3-15 is the index of the Descriptor Table item, indicating the number of descriptors selected.
! So the segment selection character 8 (0b0000, 0000,000 0, 1000) indicates that the request privilege level is 0, and 1st items in the Global Descriptor Table are used.
! This indicates that the base address of the Code is 0, so the jump command here will directly execute the system code.
! The following subroutine checks whether the keyboard command queue is empty.
! Only when the input buffer is empty (Status Register 2 = 0) Can you write commands on it
Empty_8042:
. Word 0x00eb, 0x00eb! The machine code of the two jump commands is equivalent to the delayed null operation.
In Al, #0x64! Read at keyboard controller Status Register
Test Al, #2! Test bit 2. Is the input buffer full?
Jnz empty_8042! Yes -- loop
RET
! Start of the Global Descriptor Table. A descriptor table consists of multiple 8-byte descriptor items.
! Three descriptor items are provided here.
! 1st items are useless and must exist. The first item is the system code segment descriptor. The first item is the system data segment descriptor.
Gdt:
. Word 0, 0, 0! Dummy: 1st descriptors, no need
! Here, the offset in the gdt table is 0x08. This offset is used when the code segment register (segment selection operator) is loaded.
. Word 0x07FF! 8Mb-limit = 2047 (2048*4096 = 8 Mb)
. Word 0x0000! Base address = 0
. Word 0x9A00! Code read/exec
. Word 0x00C0! Granularity = 4096,386
! Here, the offset in the gdt table is 0x10. When loading data segment registers (such as ds), this offset is used.
. Word 0x07FF! 8Mb-limit = 2047 (2048*4096 = 8 Mb)
. Word 0x0000! Base address = 0
. 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 0x800! Gdt limit = 2048,256 GDT entries
! The length of the global table is 2 K bytes, because every 8 bytes constitutes a segment descriptor
! Therefore, there are 256 items in the table.
. Word 512 + gdt, 0x9! Gdt base = 0X9xxxx
! Memory linear address composed of four bytes: 0x0009 <16 + 0x0200 + gdt
! That is, 0x90200 + gdt (that is, the offset address in this section)
. Text
Endtext:
. Data
Enddata:
. Bss
Endbss: