;
; normal DOS boot sector
;
segment _DATA class=DATA align=2
; Global identifier of the start of the boot sector, which will be used in
; fdisk to read in the boot sector.
global _bootnormal_code
_bootnormal_code:
;-----------------------------------------------------------------------
; ENTRY (copied from freedos bootsector)
;
; IN: DL = boot drive
;OUT: DL = boot drive
;
;-----------------------------------------------------------------------
; Put 0 to IF to disable maskable interrupts.
real_start: cli
; Put 0 to DF. When DF is 0, string operations increment SI and DI.
cld
; Put 0 to AX.
xor ax, ax
; Put 0 to SS.
mov ss, ax ; initialize stack
; Put 0 to DS.
mov ds, ax
; Put 0x7C00 to BP.
mov bp, 0x7c00
; Put 0x7BE0 to SP.
lea sp, [bp-0x20]
; Put 1 to IF to enable maskable interrupts.
sti
; Put 0x1FE0 to AX.
mov ax, 0x1FE0
; Put 0x1FE0 to ES.
mov es, ax
; Put 0x7C00 to SI.
mov si, bp
; Put 0x7C00 to DI.
mov di, bp
; Put 256 to CX, to be used as repeat count below.
mov cx, 0x0100
; Copy boot sector from [0x0000:0x07C0] to [0x1FE0:0x07C0].
; Each time copy 2 bytes. Repeat 256 times.
rep movsw
; Jump to `cont` in segment 0x1FE0.
jmp word 0x1FE0:0x7c00+ cont-real_start
; Put 0x1FE0 to DS.
cont: mov ds, ax
; Put 0x1FE0 to SS.
mov ss, ax
; Put 0 to AX.
xor ax,ax
; Put 0 to ES.
mov es,ax
; call print
; db "FreeDOS MBR...",0
; search for active partition
; Put [bp+446] to DI.
; It is the address of the first entry of partition table.
lea di, [bp+0x1be] ; start of partition table
test_next_for_active:
; Test whether byte 446 is 0x80, which means it is an active partition.
test byte [di],0x80
; If byte 446 is 0x80, jump to `active_partition_found`.
jne active_partition_found
; If byte 446 is not 0x80, increment DI by 16 to point to the next entry.
add di,0x10 ; next table
; Test whether DI points to byte 510, which means end of partition table
; entries.
cmp di, 07c00h+0x1fe; scanned beyond end of table ??
; If DI not points to byte 510, jump to `test_next_for_active`.
jb test_next_for_active
;*****************************************************************
; If DI points to byte 510, print error message.
call print
db 'no active partition found',0
WAIT_FOR_REBOOT:
; Jump to self.
jmp $
;*****************************************************************
trouble_reading_drive:
call print
db 'read error while reading drive',0
jmp WAIT_FOR_REBOOT
;*****************************************************************
invalid_partition_code:
call print
db 'partition signature != 55AA',0
jmp WAIT_FOR_REBOOT
;*****************************************************************
active_partition_found:
; call print
; db 'loading active partition',0
; Read the active partition's boot sector.
call read_boot_sector
; If CF is 1 which means have error, jump to `trouble_reading_drive`.
jc trouble_reading_drive
; Test whether the last 2 bytes of the active partition's boot sector is
; 0xAA55.
cmp word [es:0x7c00+0x1fe],0xaa55
; If the last 2 bytes of the active partition's boot sector is not 0xAA55,
; jump to `invalid_partition_code`.
jne invalid_partition_code
; call print
; db '.jump DOS..',0
; Jump to the active partition's boot sector.
jmp word 0x0:0x7c00 ; and jump to boot sector code
;*****************************
; read_boot_sector
;
; IN: DI--> partition info
;OUT:CARRY
;*****************************
read_boot_sector:
; /* check for LBA support */
; Put 0x55AA to BX, as required by interrupt 0x13 AH=0x41 service.
mov bx,0x55aa
; Specify to use interrupt 0x13 AH=0x41 service to check extensions present.
mov ah,0x41
; Invoke interrupt 0x13 AH=0x41 service to check extensions present.
; If extensions are not present, CF is 1. If present, CX stores the bitmask:
; 1 – Device access using the LBA packet.
; 2 – Drive locking and ejecting.
; 4 – Enhanced disk drive support.
int 0x13
; If CF is 1 which means extensions are not present, jump to `StandardBios`.
jc StandardBios ; if (regs.b.x != 0xaa55 || (regs.flags & 0x01))
; Test whether BX is 0xAA55.
cmp bx,0xaa55 ; goto StandardBios;
; If BX is not 0xAA55, jump to `StandardBios`.
jne StandardBios
; /* if DAP cannot be used, don't use LBA */
; if ((regs.c.x & 1) == 0)
; goto StandardBios;
; Test whether the LBA bit is 1 in CL.
test cl,1
; If the LBA bit is not 1 in CL, jump to `StandardBios`.
jz StandardBios
; If the LBA bit is 1 in CL, jump to `LBABios`.
jmp short LBABios
;struct _bios_LBA_address_packet /* Used to access a hard disk via LBA */
;{
; unsigned char packet_size; /* size of this packet...set to 16 */
; unsigned char reserved_1; /* set to 0...unused */
; unsigned char number_of_blocks; /* 0 < number_of_blocks < 128 */
; unsigned char reserved_2; /* set to 0...unused */
; UBYTE far *buffer_address; /* addr of transfer buffer */
; unsigned long block_address; /* LBA address */
; unsigned long block_address_high; /* high bytes of LBA addr...unused */
;};
_bios_LBA_address_packet:
; The LBA packet size.
db 0x10
db 0
; The number of sectors to read.
db 4 ; read four sectors - why not
;
db 0
; The read buffer offset.
dw 0x7c00 ; fixed boot address for DOS sector
; The read buffer segment.
dw 0x0000
; The the 48-bit LBA sector number.
_bios_LBA_low dw 0
_bios_LBA_high dw 0
dw 0,0
LBABios:
; copy start address of partition to DAP
; Put lower 2 bytes of address of the active partition to AX.
mov ax,[di+8]
; Put lower 2 bytes of address of the active partition to the LBA packet.
mov [0x7c00+ (_bios_LBA_low-real_start)],ax
; Put higher 2 bytes of address of the active partition to AX.
mov ax,[di+8+2]
; Put higher 2 bytes of address of the active partition to the LBA packet.
mov [0x7c00+ (_bios_LBA_high-real_start)],ax
; Specify to use interrupt 0x13 AH=0x42 service to read disk sectors using LBA
; addressing.
mov ax,0x4200 ; regs.a.x = LBA_READ;
; Put address of the LBA packet to SI, as required by interrupt 0x13
mov si,0x7c00+ (_bios_LBA_address_packet-real_start); regs.si = FP_OFF(&dap);
; Invoke interrupt 0x13 AH=0x42 service.
; If have error, CF is 1.
int 0x13
; Return.
ret
;*****************************************************************
; read disk, using standard BIOS
;
StandardBios:
; call print
; db 'standard BIOS',0
; Specify to use interrupt 0x13 AH=0x02 service.
; AH 0x02 service means read sectors from drive using CHS addressing.
; AL 0x04 means read 4 sectors.
mov ax,0x0204 ; regs.a.x = 0x0201;
; Point ES:BX to [0x0000:0x7c00].
; Interrupt 0x13 AH=0x02 service will read sectors into this buffer.
mov bx,0x7c00 ; regs.b.x = FP_OFF(buffer);
; Put the cylinder number and the sector number to CX.
; CX stores both the cylinder number (10 bits, possible values are 0 to 1023)
; and the sector number (6 bits, possible values are 1 to 63).
; Layout:
; CX: ---CH--- ---CL---
; Cylinder : 76543210 98
; Sector : 543210
mov cx,[di+2] ; regs.c.x =
; ((chs.Cylinder & 0xff) << 8) + ((chs.Cylinder & 0x300) >> 2) +
; chs.Sector;
; that was easy ;-)
; Put the head number to DH.
mov dh,[di+1] ; regs.d.b.h = chs.Head;
; regs.es = FP_SEG(buffer);
; Invoke interrupt 0x13 AH=0x02 service.
int 0x13
; Return.
ret
;****** PRINT
; prints text after call to this function.
print_1char:
; BH is page number.
; BL is color.
xor bx, bx ; video page 0
; 0x0E means TTY mode of interrupt 0x10 video service.
mov ah, 0x0E ; else print it
; Invoke interrupt 0x10 video service to print the character in AL.
int 0x10 ; via TTY mode
; Pop the offset of the next character to SI.
print: pop si ; this is the first character
; Put the character in [DS:SI] to AL.
; SI is incremented.
; Now SI points to one byte after the character.
; If the character in AL is value 0, it means end of characters, in which case
; SI points to the next instruction to return to.
print1: lodsb ; get token
; Push SI as the potential return address.
push si ; stack up potential return address
; Test whether the character in AL is value 0.
cmp al, 0 ; end of string?
; If the character in AL is not value 0, jump to `print_1char`.
jne print_1char ; until done
; If the character in AL is value 0, return.
ret ; and jump to it
; Fill zeros up to byte 510, exclusive.
times 0x1fe-$+$$ db 0
; Put 0x55AA in the end of the boot sector.
db 0x55,0xaa
Comments: