!************************************************************************** !* !* Boot-ROM-Code to load an operating system across a TCP/IP network. !* !* Module: floppy.S !* Purpose: Floppy boot sector to load the rest of the boot rom code !* Entries: called by BIOS at offset 0 !* !************************************************************************** !* !* Copyright (C) 1995-2003 Gero Kuhlmann !* !* This program is free software; you can redistribute it and/or modify !* it under the terms of the GNU General Public License as published by !* the Free Software Foundation; either version 2 of the License, or !* any later version. !* !* This program is distributed in the hope that it will be useful, !* but WITHOUT ANY WARRANTY; without even the implied warranty of !* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !* GNU General Public License for more details. !* !* You should have received a copy of the GNU General Public License !* along with this program; if not, write to the Free Software !* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. !* !* $Id: floppy.S,v 1.6 2003/01/25 23:29:41 gkminix Exp $ !* ! !************************************************************************** ! ! Load assembler macros: ! #include #include #include #include "ldram.inc" ! !************************************************************************** ! ! Equates local for this module: ! HIGHADR equ MEMEND ! end of physical base memory LOADADR equ FLOPMEM ! segment where to load the boot code LOADSIZE equ FLOPEND-FLOPMEM ! required load memory size SECTSIZE equ 512 ! size of one floppy sector ROMOFS equ $0003 ! offset to ROM startup code DPTVECTOR equ $0078 ! address of pointer to DPT DPTSIZE equ 13 ! size of DPT DPTSPTOFS equ $04 ! offset to sectors per track in DPT PARTSTARTSECT equ $0008 ! offset to start sector in part table PNP_HDROFS equ $001A ! offset to PnP header offset PNP_BEVOFS equ $001A ! offset to BEV within PnP structure ! !************************************************************************** ! ! Here the loader program starts. Some ideas are taken from the Linux ! boot sector. Note that this program has to be completely seperate ! from all other segments of the boot rom code. It can contain data ! since its getting loaded into the ram. ! This program gets either loaded by the BIOS directly off a floppy ! boot sector, or from a a hard disk boot sector. These cases can ! be distinguished by the value of DL, which contains the BIOS boot ! drive ID. If it got loaded from a hard disk, DS:SI points to the ! current partition table entry. ! The loader works by first patching the floppy disk parameter table, ! and then trying to read the last sector of the floppy in order to ! find out about the disk size. Then it will load 128 kB from the ! diskette into the OS loading area (see memory.h for explanation). .text ! begin loader text segment entry start org 0 start: call dobot1 dobot1: pop ax ! determine the offset sub ax,#dobot1 push dx mov cl,#4 mov bx,cs mov dx,bx ! convert the base address from and dx,#$F000 ! seg:ofs format into sort of a shl bx,cl ! linear address. this conversion add bx,ax ! assumes that no overflow can occur. ! compute a code segment address shr bx,cl ! which lets this code start at add bx,dx ! offset 0. this is necessary so pop dx ! all offsets computed by as86 work. push bx mov ax,#dobot2 push ax retf ! this will jump to dobot2 with new ! code segment register ! Check if we were loaded off a hard disk. dobot2: mov cx,word ptr [si + PARTSTARTSECT + 2] mov bx,word ptr [si + PARTSTARTSECT + 0] mov ax,cs mov ds,ax mov es,ax cmp dl,#$80 jb dobot5 ! For hard disks, we can use the BIOS to determine the number of sectors ! per track and the number of heads. For floppies, this doesnt work. mov word ptr [startsect + 0],bx mov word ptr [startsect + 2],cx mov [drivenum],dl mov ah,#$08 int INT_DISK ! ask the BIOS about the disk jc lderr1 ! parameters dobot4: mov al,dh xor ah,ah inc ax ! save number of heads mov [heads],ax and cx,#$003F ! save number of sectors per track mov bx,#LOADADR mov es,bx ! set load address jmp gotit lderr1: mov si,#errask ! print error message jmp near lderr5 ! Next determine the number of sectors per track on the floppy. To do ! this the disk parameter table has first to be changed, so that the ! BIOS is able to access at least 18 sectors in a track. ! Note that we dont check if the system really has enough base memory ! for loading everything. Is there any system on the market which still ! doesnt have 640kB in the lower memory area? dobot5: cld mov dx,ds xor ax,ax mov ds,ax ! first save the old disk parameter lds si,[DPTVECTOR] ! table seg es mov word ptr [oldvect + 0],si seg es ! save old vector mov word ptr [oldvect + 2],ds mov di,#newdpt mov cx,#DPTSIZE rep movsb seg es mov byte ptr [newdpt + DPTSPTOFS],#18 mov word ptr [DPTVECTOR + 0],#newdpt mov word ptr [DPTVECTOR + 2],dx mov ds,dx ! Next try to find the number of sectors per track. To do this, the ! last sector for each disk format is read. This idea has been taken ! from the Linux boot sector. xor dx,dx ! reset drive 0 (AX is already zero int INT_DISK ! from the code above) mov ax,#LOADADR mov es,ax ! set load address xor bx,bx mov cx,#1 ! load one sector to initialize drive mov ax,#$0201 int INT_DISK mov cx,#18 ! sector 18, track 0 mov ax,#$0201 ! read one sector int INT_DISK jnc gotit mov cx,#15 ! sector 15 mov ax,#$0201 ! read one sector int INT_DISK jnc gotit mov cx,#9 gotit: mov [sects],cx ! save number of sectors per track ! Now load the next 128 kB starting at the second sector of track 0. In ! this loop, CX contains the number of sectors to load, and AX contains ! the logical number of the next sector to load. push es ! save load segment for later if SECTSIZE = 512 mov cx,#LOADSIZE / 32 ! number of sectors to load endif if SECTSIZE = 256 mov cx,#LOADSIZE / 16 ! number of sectors to load endif mov ax,#1 ! starting sector rdloop: push ax push cx mov bx,cx call cvtsec ! compute sector information jc lderr2 xor ax,ax mov al,byte ptr [sects] sub al,cl ! compute the number of sectors and al,#$3F ! to load with one BIOS call inc al cmp bx,ax jae rdlop1 mov ax,bx rdlop1: push ax ! save number of sectors to load xor bx,bx ! starting offset of load memory mov ah,#$02 ! load sectors int INT_DISK pop bx ! get number of sectors loaded jnc rdlop2 ! there was no error cmp ah,#9 jne lderr2 mov bx,es ! that error we can handle mov ax,es or ax,#$0FFF inc ax sub ax,bx ! we can load ax * 16 more bytes if SECTSIZE = 512 mov cl,#5 else mov cl,#4 endif shr ax,cl xor ah,ah jmp rdlop1 lderr2: stc rdlop2: pop cx pop ax jc lderr4 ! check for error inside inner loop sub cx,bx ! decrement sector count jbe loaded ! check if we loaded all sectors push cx add ax,bx ! increment sector number if SECTSIZE = 512 mov cl,#5 else mov cl,#4 endif shl bx,cl mov dx,es add dx,bx ! increment load pointer mov es,dx pop cx jmp rdloop lderr4: pop es mov si,#dskmsg ! print error message lderr5: call prnstr reboot: mov si,#botmsg call prnstr xor ah,ah ! wait for a key press int INT_KBD int INT_BOOTSTRAP jmp reboot ! Restore the floppy DPT. loaded: xor ax,ax cmp byte ptr [drivenum],#$80 jae load1 les si,[oldvect] ! restore old vector mov ds,ax mov word ptr [DPTVECTOR + 0],si mov word ptr [DPTVECTOR + 2],es xor dx,dx ! reset disk system int INT_DISK ! The boot code is in the memory now. We cant use the bootrom initialization ! routine because it gets confused when we start this floppy boot on a system ! which has a netboot bootrom already installed. Instead, we just setup the ! bootrom RAM area and then call it directly via the PnP header structure. ! For a standard bootrom the ram data area is all empty, and we just have ! to set the signature and checksum. load1: pop bx ! restore load segment mov ds,ax mov es,ax mov di,#RAM_START ! clear bootrom data area mov cx,#RAM_SIZE / 2 rep stosw mov word ptr [RAM_START + RAM_ID],#RAM_SIGNATURE mov word ptr [RAM_START + RAM_PCIPFA],#$FFFF mov word ptr [RAM_START + RAM_PNPCSN],#$FFFF mov byte ptr [RAM_START + RAM_CHKSUM],#(-((RAM_SIGNATURE & $FF) + (RAM_SIGNATURE >> 8) + (4 * $FF))) & $FF mov ds,bx mov si,[PNP_HDROFS] push cs ! push return address mov ax,#reboot push ax push bx ! push call address push word ptr [si + PNP_BEVOFS] retf ! call the bootrom ! !************************************************************************** ! ! Routine to convert an absolute sector into a sector/track/head information. ! Input: AX - relative sector number ! Output: CL - sector number ! CH - track number ! DH - head number ! DL - drive number ! Carry flag set if error ! Registers changed: AX, CX, DX ! cvtsec: xor dx,dx add ax,word ptr [startsect + 0] adc dx,word ptr [startsect + 2] cmp [sects],dx ! in case of error this will set the jb cvts9 ! carry flag push bx div word ptr [sects] ! compute sector number in a track inc dl ! sector numbers always start with 1 mov bl,dl xor dx,dx div word ptr [heads] ! divide by number of heads mov dh,dl ! set head number mov bh,al ! set track number mov cl,#6 shl ah,cl ! isolate highest 2 bits of 10-bit mov cx,bx ! track number and move them into or cl,ah ! CL mov dl,[drivenum] ! set drive number pop bx clc cvts9: ret ! !************************************************************************** ! ! Routine to print a string onto the console. ! Input: CS:SI - address of NULL terminated string ! Output: None ! Registers changed: AX, SI ! prnstr: push bx prnst1: seg cs mov al,[si] ! get next character or al,al jz prnst2 mov ah,#$0E ! BIOS command for character output mov bx,#$0007 ! screen page and colors int INT_VIDEO ! send character to screen inc si jmp prnst1 prnst2: pop bx ret ! !************************************************************************** ! ! Strings and messages: ! dskmsg: .ascii 'Read error' .byte $0D,$0A,0 errask: .ascii 'BIOS error' .byte $0D,$0A,0 botmsg: .ascii 'Press a key...' .byte 0 ! !************************************************************************** ! ! Data space: ! sects: .word 0 ! number of sectors per track heads: .word 2 ! number of heads startsect: .long 0 ! logical start sector drivenum: .byte 0 ! BIOS drive number oldvect: .long 0 ! old dpt vector .org SECTSIZE - 2 .word $AA55 newdpt: ! changed disk parameter table comes ! right at the end of everything ! !************************************************************************** ! end