;-------------------------------------------------------------------------------+ ; + ; Bootsect.asm + ; + ; Boot sector for an IBM-PC compatible. + ; Written to fit into the first sector of a floppy disk. + ; + ; build with : NASM bootsect.asm -fbin -obootsect.bin + ; Write to disk using : partcopy bootsect.bin 0 200 -f0 + ; + ; + ; This bootsector is written for a FAT12 disk, although as much code as + ; possible has been written to be portable to FAT16 and other FS + ; + ; This code has only been tested with nasm 0.98 + ; + ; The assembled code has been tested and works on the following machines: + ; Packard Bell Pentium 66 with 104M RAM + ; Compaq Prolinea 4/33S (486SX 33) with 24M RAM + ; Compaq Presario (AMD K6-2/350) with 64M RAM + ; Home built system (AMD K6-2/400 on a Jetway motherboard) with 320M RAM + ; Toshiba S2800-200 laptop (Pentium III 700) with 64M RAM + ; + ; This code is released into the public domain. All I ask is that if it is used + ; for any commercial purpose my name be included in the credits. + ; + ; Debbie Wiles + ; debs@ukcpu.net + ;-------------------------------------------------------------------------------+ ;-------------------------------------------------------------------------------+ ; Documentation: + ; + ; Memory usage: + ; This boot sector copies itself to memory starting at 9000:0000h and + ; jumps to the next instruction in the copy. + ; All segment values are set as constant defined values + ; Segment values for rootseg, fatseg, kernseg and loadseg need to be + ; multiples of 20h (32) so that disk reads can be easily split + ; at 64k boundaries. + ; Values used for all segments and for the stack size are all set as + ; %defines, so that when changes are needed, they only need + ; changing once + ; Loading of files: + ; the loader and kernel files can be placed anywhere on disk, and they do + ; not need to be contiguous on the disk + ; Reads are split on track boundaries. Not all BIOS's can do multi-track + ; reads + ; Boot record: + ; The boot parameter block in this incarmation of the code are hard-coded + ; with values suitable for a 3.5" Hig Density (ie 1.4M) floppy + ; disk. These values can be changed when using on any other type + ; of disk. This can be done from a formatting program or a system + ; disk utility like DOS's sys.com, as well as re-assembling to + ; produce an appropriate boot sector for another disk. + ; Hardware Requirements + ; This code uses only commands that are valid for a 386 or later CPU + ;-------------------------------------------------------------------------------+ ;-----------------------------------------------------------------------+ ; First, define some constants + ;-----------------------------------------------------------------------+ %include "boot.inc" ;-----------------------------------------------------------------------+ ; The figures defined in boot.inc allow the following space for + ; various data areas in memory: + ; + ; moveseg 4k = 8 sectors reserved for boot code. + ; Allows for future growth + ; rootseg 16k = 32 sectors. Maximum of 512 root + ; directory entries + ; stackseg 12k This should be more than enough space + ; for a stack. + ; fatseg 128k This allows for the maximum of 256 + ; sectors (65535 entries) for the + ; FAT. + ; The first 64k (128 sectors) would have + ; to be loaded first, then the + ; segment increased by 1000h to + ; load the remainder + ; loadseg 512k This is the amount of memory allocated + ; for the loader. + ; This is currently set up to load the + ; loader beyond at 0x65536, + ; leaving the memory below 64k + ; available for global data. + ; Memory beyond the byte at 0050:0000h + ; is free for use in a standard + ; real-mode system with no OS + ; + ; All load segments for data read from disk should be at addresses + ; which are multiples of 020h (this is because the GetSecs + ; routine needs to be able to check for reads which cross 64k + ; boundaries, because the DMA in many machines cannot read + ; across a 64k boundary) + ;-----------------------------------------------------------------------+ ;-----------------------------------------------------------------------+ ; Next, the directives + ;-----------------------------------------------------------------------+ bits 16 ; BIOS starts us in 16-bit mode org 0 ; BIOS loads bootsector at this address section .text ;-----------------------------------------------------------------------+ ; First actual code in the bootsector is a jump over the BPB + ;-----------------------------------------------------------------------+ BootSector: jmp short start ; There should always be 3 bytes of code nop ; followed by the start of the data. ;-----------------------------------------------------------------------+ ; Next, the BPB (BIOS Parameter Block) + ; (This provides compatibility with DOS) + ; The following figures are specific to a 1.44M 3 1/2 inch floppy disk + ;-----------------------------------------------------------------------+ OEM_Name db 'Deb 0.1' ; 8 bytes for OEM Name and Version nBytesPerSec dw 0200h ; 512 bytes per Sector nSecPerClust db 01h ; Sectors per Cluster nSecRes dw 01h ; Sectors reserved for Boot Record nFATs db 02h ; Number of FATs nRootEnts dw 0E0h ; 0E0h (224) Root Directory Entries nSecs dw 0B40h ; Number of Logical Sectors 0B40h = 2880 ; 00h when > 65,535 sectors mDesc db 0F0h ; Medium Descriptor Byte nSecPerFat dw 09h ; Sectors per FAT nSecPerTrack dw 012h ; Sectors per Track nHeads dw 02h ; Number of Heads nLogSec0 nSecHidden dd 00h ; Number of Hidden Sectors ;-----------------------------------------------------------------------+ ; These figures provide compatibility with DOS v4.00+ + ; + ; Figures in this section aren't needed for a floppy disc, but we will + ; keep them in to make porting easier. ;-----------------------------------------------------------------------+ nSecsExt dd 00h ; This value used when there are more ; than 65,535 sectors on a disc ; (ie disc size >= 32M) DriveNum db 00h ; Physical drive number nResByte db 00h ; Reserved - can be used in code db 29h ; Signature for Extended Boot Record ; Informs DOS that this Boot ; Record is DOS 4.0 compatible SerNum dd 019B574D0h ; Volume Serial Number ; Would normally be calculated ; during format process VolName db 'NO NAME ' ; Volume Label FSType db 'FAT12 ' ; Reserved, gives file system type ;-----------------------------------------------------------------------+ ; End of DOS Boot Sector compatibility + ; Following variables provide temporary storage for internal use + ;-----------------------------------------------------------------------+ ;-------------------------------------------------------------------------------+ ; Start of initialisation code + ;-------------------------------------------------------------------------------+ start cli ;-----------------------------------------------------------------------+ ; Let's move our boot code to 9000:0000h, and set segregs. + ;-----------------------------------------------------------------------+ push WORD stackseg ; this works, but leave in the mov's pop SS ; in case not all machines allow a push ; while setting up the stack ; mov AX,stackseg ; ; mov SS,AX ; Setup stack mov SP,stacksize ; push WORD bootseg pop DS push WORD moveseg pop ES xor SI,SI ; Starting from bottom of each segment xor DI,DI mov CX,100h ; 256 words to be transferred cld rep movsw ; Move the code ;-----------------------------------------------------------------------+ ; Now we jump to the next instruction in the copy of the code + ;-----------------------------------------------------------------------+ jmp moveseg:next ; CS is now set to moveseg ;-------------------------------------------------------------------------------+ ; Now we can get on with the real work :) + ;-------------------------------------------------------------------------------+ ;-----------------------------------------------------------------------+ ; Store boot drive, and initialize the video system + ;-----------------------------------------------------------------------+ next: push CS pop DS ; All segments are now set up as we want mov [DriveNum],DL ; Store drive number ; (supplied by BIOS startup code) sti mov AX,03h ; Set video mode 3 int 10h ; This code is here to clear the screen ; before writing to it ;-------------------------------------------------------------------------------+ ; The first main jobs are to load the root directory and FAT into memory + ; + ; The segment addresses to load at are defined at the top of this code + ;-------------------------------------------------------------------------------+ ;-----------------------------------------------------------------------+ ; Find the logical sector address of root directory start, and store, + ; then load root directory to the end of the boot sector code + ;-----------------------------------------------------------------------+ loadroot: push WORD rootseg pop ES movzx AX, BYTE [nFATs] mul WORD [nSecPerFat] add AX,[nSecRes] ; DX:AX = logical sector of start of cwd ; root directory (0 based) ; Good for FAT12 or FAT16, not FAT32 mov SI,[nRootEnts] dec SI shr SI,4 inc SI ; SI = Sectors to read push ES call GetSecs pop ES ;-----------------------------------------------------------------------+ ; Search for kernel and loader files in root directory + ;-----------------------------------------------------------------------+ readroot: movzx CX, byte [nSecPerClust] shl CX,1 sub AX,CX ; AX = cluster 0 start sector mov SI,loadname ; SI = position of filename in bootsec ;-----------------------------------------------------------------------+ ; Read the root directory, searching for each file in turn + ; Searches first for kernel file then for loader + ;-----------------------------------------------------------------------+ ReadRoot: xor DI,DI mov BX,[nRootEnts] .1 push SI push DI mov CX,0Bh ; 11 bytes for file name repe cmpsb ; check if root entry is our file pop DI pop SI je .3 ; jump if file entry found add DI,020h ; else set up for next root entry dec BX ; if no more root entries to search js BootErr ; jump to error routine jmp short .1 ; and search again .3 ;-----------------------------------------------------------------------+ ; push sector number of first cluster and start cluster of current + ; file, then set up for searching for second file + ;-----------------------------------------------------------------------+ push AX ; cluster 0 sector push WORD [ES:DI+01Ah] ; cluster number of start of kernel ;-----------------------------------------------------------------------+ ; Now calculate figures for FAT, and load that + ;-----------------------------------------------------------------------+ loadfat: push WORD fatseg pop ES push ES pop GS mov AX,[nSecRes] mov SI,[nSecPerFat] call GetSecs ;-----------------------------------------------------------------------+ ; load kernel at the specified memory address + ;-----------------------------------------------------------------------+ loadkern: mov BP,msgLoad mov CX,loadname-msgLoad call PrintText push WORD loadseg pop ES .1 pop BX ; AX = first cluster of file pop CX push CX push BX ; first pop = first cluster of file ; second pop = cluster 0 sector ;-----------------------------------------------------------------------+ ; Get cluster chain from the FAT + ;-----------------------------------------------------------------------+ xor SI,SI ; count of clusters in chain mov AX,BX ; current cluster .2 inc AX ; pre-increment mov DI,BX shr DI,1 ; calculate offset of cluster in FAT mov BX, WORD [GS:BX+DI] jnc .3 ; if even numbered cluster, jump shr BX,4 ; for odd clusters only .3 and BH,0Fh ; now BX = next cluster to read inc SI ; count of clusters in sequence cmp AX,BX ; is this cluster contiguous? je .2 ;-----------------------------------------------------------------------+ ; Load the current chain of clusters + ;-----------------------------------------------------------------------+ pop AX ; first cluster in this chain push BX ; first cluster in next chain movzx BX, BYTE [nSecPerClust] mul BX add AX,CX call GetSecs pop BX push BX cmp BX,0FFFh jne .1 pop BX pop BX ; discard two stack entries jmp loadseg:0 ;-------------------------------------------------------------------------------+ ; Routines called elsewhere in the bootsector + ;-------------------------------------------------------------------------------+ ;-------------------------------------------------------------------------------+ ; BootErr + ; + ; Input: none + ; + ; Output: none + ; + ; Decription: Beeps, waits for keypress, and reboots + ;-------------------------------------------------------------------------------+ BootErr: mov AX,0E07h ; on error, beep int 10h mov BP,NotSysDisk ; Error message to display mov CX,TextEnd-NotSysDisk call PrintText ; message printed only if no kernel reboot: xor AX,AX ; Wait for keypress int 016h int 19h ;-------------------------------------------------------------------------------+ ; GetSecs + ; + ; Input: DX:AX sector within partition (0 based) + ; SI Number of Sectors to read + ; ES Segment to transfer data to + ; + ; Output: DX:AX next sector on partition beyond those copied + ; + ; Decription: Gets sectors from the drive + ; Splits reads into track reads where possible + ; Changes ES + ;-------------------------------------------------------------------------------+ GetSecs: .1 push SI push AX push DX add AX,[nSecHidden] adc DX,[nSecHidden+2] ; Convert to LBN mov BX,[nSecPerTrack] div BX ; AX = track, DX = sector - 1 sub BX,DX ; sectors remaining on this track cmp BX,SI ; is it more than we need? jbe .2 ; if not, jump mov BX,SI ; BX = number of sectors to transfer .2 mov CX,DX inc CX ; CL = sector number, CX = 0 xor DX,DX div WORD [nHeads] ; AX = cylinder, DX = head mov DH,DL ; DH = head mov DL,[DriveNum] ; DL = Drive number mov CH,AL ; CH = low 8 bits of cylinder ror AH,2 add CL,AH ; CL = high two bits of cylinder and ; sector number (bits 0-5) mov AL,BL ; AL = sectors to transfer ;-----------------------------------------------------------------------+ ; this section splits reads on DMA 64k boundaries + ;-----------------------------------------------------------------------+ xor AH,AH ; AX = sectors to transfer mov SI,ES shl SI,4 ; ESI = position in current 64k block neg SI ; bytes remaining in current block jz .3 mov BX,AX shl BX,9 ; BX = bytes to transfer cmp BX,SI jb .3 mov AX,SI ; bytes to transfer shr AX,9 ; sectors to transfer ;-----------------------------------------------------------------------+ ; end of DMA boundary check + ;-----------------------------------------------------------------------+ .3 mov AH,02h ; read sectors xor BX,BX int 13h ; read sectors jc BootErr push AX pop BX ; now BX = sectors just read pop DX ; pop AX ; DX:AX = original start sector add AX,BX adc DX,0 ; DX:AX = new start sector push BX mov CX,ES shl BX,5 add CX,BX mov ES,CX ; updated segment to write to pop BX pop SI ; sectors to read before this read sub SI,BX jnz .1 ret ;-------------------------------------------------------------------------------+ ; PrintText + ; + ; Input: BP Offset within bootsector of text to print + ; CX Number of bytes to write + ; + ; Output: None + ; + ; Decription: Writes a string to the screen at row 0 column 0 + ;-------------------------------------------------------------------------------+ PrintText: push CS pop ES ; ES points to current segment PrintKern: ; print string pointed to by ES:BP ; PrintKern used only for debugging mov AX,1301h ; Print string, update cursor mov BX,0Fh ; Page 0, Bright White text xor DX,DX int 10h ret ;-------------------------------------------------------------------------------+ ; Variables used within code + ;-------------------------------------------------------------------------------+ msgLoad db 'Loading OS',0dh,0ah loadname db 'LOADER BIN' NotSysDisk db 'Non-System Disk',0dh,0ah RebootText db 'Press any key to reboot' TextEnd: ;-------------------------------------------------------------------------------+ ; End of code. Pad out to fill 512 bytes, including final word 0xAA55 + ;-------------------------------------------------------------------------------+ times 1FEh+$$-$ db 0 dw 0AA55h ;-------------------------------------------------------------------------------+