root/boot/bootsect.S

/* [previous][next][first][last][top][bottom][index][help] */
   1 !
   2 ! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
   3 ! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current
   4 ! versions of linux which compress the kernel
   5 !
   6 #include <linux/config.h>
   7 SYSSIZE = DEF_SYSSIZE
   8 !
   9 !       bootsect.s              Copyright (C) 1991, 1992 Linus Torvalds
  10 !       modified by Drew Eckhardt
  11 !       modified by Bruce Evans (bde)
  12 !
  13 ! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
  14 ! itself out of the way to address 0x90000, and jumps there.
  15 !
  16 ! bde - should not jump blindly, there may be systems with only 512K low
  17 ! memory.  Use int 0x12 to get the top of memory, etc.
  18 !
  19 ! It then loads 'setup' directly after itself (0x90200), and the system
  20 ! at 0x10000, using BIOS interrupts. 
  21 !
  22 ! NOTE! currently system is at most (8*65536-4096) bytes long. This should 
  23 ! be no problem, even in the future. I want to keep it simple. This 508 kB
  24 ! kernel size should be enough, especially as this doesn't contain the
  25 ! buffer cache as in minix (and especially now that the kernel is 
  26 ! compressed :-)
  27 !
  28 ! The loader has been made as simple as possible, and continuos
  29 ! read errors will result in a unbreakable loop. Reboot by hand. It
  30 ! loads pretty fast by getting whole tracks at a time whenever possible.
  31 
  32 .text
  33 
  34 SETUPSECS = 4                           ! nr of setup-sectors
  35 BOOTSEG   = 0x07C0                      ! original address of boot-sector
  36 INITSEG   = DEF_INITSEG                 ! we move boot here - out of the way
  37 SETUPSEG  = DEF_SETUPSEG                ! setup starts here
  38 SYSSEG    = DEF_SYSSEG                  ! system loaded at 0x10000 (65536).
  39 
  40 ! ROOT_DEV & SWAP_DEV are now written by "build".
  41 ROOT_DEV = 0
  42 SWAP_DEV = 0
  43 #ifndef SVGA_MODE
  44 #define SVGA_MODE ASK_VGA
  45 #endif
  46 #ifndef RAMDISK
  47 #define RAMDISK 0
  48 #endif 
  49 #ifndef CONFIG_ROOT_RDONLY
  50 #define CONFIG_ROOT_RDONLY 0
  51 #endif
  52 
  53 ! ld86 requires an entry symbol. This may as well be the usual one.
  54 .globl  _main
  55 _main:
  56 #if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
  57         int     3
  58 #endif
  59         mov     ax,#BOOTSEG
  60         mov     ds,ax
  61         mov     ax,#INITSEG
  62         mov     es,ax
  63         mov     cx,#256
  64         sub     si,si
  65         sub     di,di
  66         cld
  67         rep
  68         movsw
  69         jmpi    go,INITSEG
  70 
  71 go:     mov     ax,cs           
  72         mov     dx,#0x4000-12   ! 0x4000 is arbitrary value >= length of
  73                                 ! bootsect + length of setup + room for stack
  74                                 ! 12 is disk parm size
  75 
  76 ! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
  77 ! wouldn't have to worry about this if we checked the top of memory.  Also
  78 ! my BIOS can be configured to put the wini drive tables in high memory
  79 ! instead of in the vector table.  The old stack might have clobbered the
  80 ! drive table.
  81 
  82         mov     ds,ax
  83         mov     es,ax
  84         mov     ss,ax           ! put stack at INITSEG:0x4000-12.
  85         mov     sp,dx
  86 /*
  87  *      Many BIOS's default disk parameter tables will not 
  88  *      recognize multi-sector reads beyond the maximum sector number
  89  *      specified in the default diskette parameter tables - this may
  90  *      mean 7 sectors in some cases.
  91  *
  92  *      Since single sector reads are slow and out of the question,
  93  *      we must take care of this by creating new parameter tables
  94  *      (for the first disk) in RAM.  We will set the maximum sector
  95  *      count to 18 - the most we will encounter on an HD 1.44.  
  96  *
  97  *      High doesn't hurt.  Low does.
  98  *
  99  *      Segments are as follows: ds=es=ss=cs - INITSEG,
 100  *              fs = 0, gs = parameter table segment
 101  */
 102 
 103         push    #0
 104         pop     fs
 105         mov     bx,#0x78                ! fs:bx is parameter table address
 106         seg fs
 107         lgs     si,(bx)                 ! gs:si is source
 108 
 109         mov     di,dx                   ! es:di is destination
 110         mov     cx,#6                   ! copy 12 bytes
 111         cld
 112 
 113         rep
 114         seg gs
 115         movsw
 116 
 117         mov     di,dx
 118         movb    4(di),*18               ! patch sector count
 119 
 120         seg fs
 121         mov     (bx),di
 122         seg fs
 123         mov     2(bx),es
 124 
 125         mov     ax,cs
 126         mov     fs,ax
 127         mov     gs,ax
 128         
 129         xor     ah,ah                   ! reset FDC 
 130         xor     dl,dl
 131         int     0x13    
 132 
 133 ! load the setup-sectors directly after the bootblock.
 134 ! Note that 'es' is already set up.
 135 
 136 load_setup:
 137         xor     dx, dx                  ! drive 0, head 0
 138         mov     cx,#0x0002              ! sector 2, track 0
 139         mov     bx,#0x0200              ! address = 512, in INITSEG
 140         mov     ax,#0x0200+SETUPSECS    ! service 2, nr of sectors
 141                                         ! (assume all on head 0, track 0)
 142         int     0x13                    ! read it
 143         jnc     ok_load_setup           ! ok - continue
 144 
 145         push    ax                      ! dump error code
 146         call    print_nl
 147         mov     bp, sp
 148         call    print_hex
 149         pop     ax      
 150         
 151         xor     dl, dl                  ! reset FDC
 152         xor     ah, ah
 153         int     0x13
 154         jmp     load_setup
 155 
 156 ok_load_setup:
 157 
 158 ! Get disk drive parameters, specifically nr of sectors/track
 159 
 160 #if 0
 161 
 162 ! bde - the Phoenix BIOS manual says function 0x08 only works for fixed
 163 ! disks.  It doesn't work for one of my BIOS's (1987 Award).  It was
 164 ! fatal not to check the error code.
 165 
 166         xor     dl,dl
 167         mov     ah,#0x08                ! AH=8 is get drive parameters
 168         int     0x13
 169         xor     ch,ch
 170 #else
 171 
 172 ! It seems that there is no BIOS call to get the number of sectors.  Guess
 173 ! 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
 174 ! Otherwise guess 9.
 175 
 176         xor     dx, dx                  ! drive 0, head 0
 177         mov     cx,#0x0012              ! sector 18, track 0
 178         mov     bx,#0x0200+SETUPSECS*0x200  ! address after setup (es = cs)
 179         mov     ax,#0x0201              ! service 2, 1 sector
 180         int     0x13
 181         jnc     got_sectors
 182         mov     cl,#0x0f                ! sector 15
 183         mov     ax,#0x0201              ! service 2, 1 sector
 184         int     0x13
 185         jnc     got_sectors
 186         mov     cl,#0x09
 187 
 188 #endif
 189 
 190 got_sectors:
 191         seg cs
 192         mov     sectors,cx
 193         mov     ax,#INITSEG
 194         mov     es,ax
 195 
 196 ! Print some inane message
 197 
 198         mov     ah,#0x03                ! read cursor pos
 199         xor     bh,bh
 200         int     0x10
 201         
 202         mov     cx,#9
 203         mov     bx,#0x0007              ! page 0, attribute 7 (normal)
 204         mov     bp,#msg1
 205         mov     ax,#0x1301              ! write string, move cursor
 206         int     0x10
 207 
 208 ! ok, we've written the message, now
 209 ! we want to load the system (at 0x10000)
 210 
 211         mov     ax,#SYSSEG
 212         mov     es,ax           ! segment of 0x010000
 213         call    read_it
 214         call    kill_motor
 215         call    print_nl
 216 
 217 ! After that we check which root-device to use. If the device is
 218 ! defined (!= 0), nothing is done and the given device is used.
 219 ! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
 220 ! on the number of sectors that the BIOS reports currently.
 221 
 222         seg cs
 223         mov     ax,root_dev
 224         or      ax,ax
 225         jne     root_defined
 226         seg cs
 227         mov     bx,sectors
 228         mov     ax,#0x0208              ! /dev/ps0 - 1.2Mb
 229         cmp     bx,#15
 230         je      root_defined
 231         mov     ax,#0x021c              ! /dev/PS0 - 1.44Mb
 232         cmp     bx,#18
 233         je      root_defined
 234         mov     ax,#0x0200              ! /dev/fd0 - autodetect
 235 root_defined:
 236         seg cs
 237         mov     root_dev,ax
 238 
 239 ! after that (everyting loaded), we jump to
 240 ! the setup-routine loaded directly after
 241 ! the bootblock:
 242 
 243         jmpi    0,SETUPSEG
 244 
 245 ! This routine loads the system at address 0x10000, making sure
 246 ! no 64kB boundaries are crossed. We try to load it as fast as
 247 ! possible, loading whole tracks whenever we can.
 248 !
 249 ! in:   es - starting address segment (normally 0x1000)
 250 !
 251 sread:  .word 1+SETUPSECS       ! sectors read of current track
 252 head:   .word 0                 ! current head
 253 track:  .word 0                 ! current track
 254 
 255 read_it:
 256         mov ax,es
 257         test ax,#0x0fff
 258 die:    jne die                 ! es must be at 64kB boundary
 259         xor bx,bx               ! bx is starting address within segment
 260 rp_read:
 261         mov ax,es
 262         sub ax,#SYSSEG
 263         cmp ax,syssize          ! have we loaded all yet?
 264         jbe ok1_read
 265         ret
 266 ok1_read:
 267         seg cs
 268         mov ax,sectors
 269         sub ax,sread
 270         mov cx,ax
 271         shl cx,#9
 272         add cx,bx
 273         jnc ok2_read
 274         je ok2_read
 275         xor ax,ax
 276         sub ax,bx
 277         shr ax,#9
 278 ok2_read:
 279         call read_track
 280         mov cx,ax
 281         add ax,sread
 282         seg cs
 283         cmp ax,sectors
 284         jne ok3_read
 285         mov ax,#1
 286         sub ax,head
 287         jne ok4_read
 288         inc track
 289 ok4_read:
 290         mov head,ax
 291         xor ax,ax
 292 ok3_read:
 293         mov sread,ax
 294         shl cx,#9
 295         add bx,cx
 296         jnc rp_read
 297         mov ax,es
 298         add ah,#0x10
 299         mov es,ax
 300         xor bx,bx
 301         jmp rp_read
 302 
 303 read_track:
 304         pusha
 305         pusha   
 306         mov     ax, #0xe2e      ! loading... message 2e = .
 307         mov     bx, #7
 308         int     0x10
 309         popa            
 310 
 311         mov     dx,track
 312         mov     cx,sread
 313         inc     cx
 314         mov     ch,dl
 315         mov     dx,head
 316         mov     dh,dl
 317         and     dx,#0x0100
 318         mov     ah,#2
 319         
 320         push    dx                              ! save for error dump
 321         push    cx
 322         push    bx
 323         push    ax
 324 
 325         int     0x13
 326         jc      bad_rt
 327         add     sp, #8
 328         popa
 329         ret
 330 
 331 bad_rt: push    ax                              ! save error code
 332         call    print_all                       ! ah = error, al = read
 333         
 334         
 335         xor ah,ah
 336         xor dl,dl
 337         int 0x13
 338         
 339 
 340         add     sp, #10
 341         popa    
 342         jmp read_track
 343 
 344 /*
 345  *      print_all is for debugging purposes.  
 346  *      It will print out all of the registers.  The assumption is that this is
 347  *      called from a routine, with a stack frame like
 348  *      dx 
 349  *      cx
 350  *      bx
 351  *      ax
 352  *      error
 353  *      ret <- sp
 354  *
 355 */
 356  
 357 print_all:
 358         mov     cx, #5          ! error code + 4 registers
 359         mov     bp, sp  
 360 
 361 print_loop:
 362         push    cx              ! save count left
 363         call    print_nl        ! nl for readability
 364 
 365         cmp     cl, 5
 366         jae     no_reg          ! see if register name is needed
 367         
 368         mov     ax, #0xe05 + 'A - 1
 369         sub     al, cl
 370         int     0x10
 371 
 372         mov     al, #'X
 373         int     0x10
 374 
 375         mov     al, #':
 376         int     0x10
 377 
 378 no_reg:
 379         add     bp, #2          ! next register
 380         call    print_hex       ! print it
 381         pop     cx
 382         loop    print_loop
 383         ret
 384 
 385 print_nl:
 386         mov     ax, #0xe0d      ! CR
 387         int     0x10
 388         mov     al, #0xa        ! LF
 389         int     0x10
 390         ret
 391 
 392 /*
 393  *      print_hex is for debugging purposes, and prints the word
 394  *      pointed to by ss:bp in hexadecmial.
 395 */
 396 
 397 print_hex:
 398         mov     cx, #4          ! 4 hex digits
 399         mov     dx, (bp)        ! load word into dx
 400 print_digit:
 401         rol     dx, #4          ! rotate so that lowest 4 bits are used
 402         mov     ah, #0xe        
 403         mov     al, dl          ! mask off so we have only next nibble
 404         and     al, #0xf
 405         add     al, #'0         ! convert to 0-based digit
 406         cmp     al, #'9         ! check for overflow
 407         jbe     good_digit
 408         add     al, #'A - '0 - 10
 409 
 410 good_digit:
 411         int     0x10
 412         loop    print_digit
 413         ret
 414 
 415 
 416 /*
 417  * This procedure turns off the floppy drive motor, so
 418  * that we enter the kernel in a known state, and
 419  * don't have to worry about it later.
 420  */
 421 kill_motor:
 422         push dx
 423         mov dx,#0x3f2
 424         xor al, al
 425         outb
 426         pop dx
 427         ret
 428 
 429 sectors:
 430         .word 0
 431 
 432 msg1:
 433         .byte 13,10
 434         .ascii "Loading"
 435 
 436 .org 498
 437 root_flags:
 438         .word CONFIG_ROOT_RDONLY
 439 syssize:
 440         .word SYSSIZE
 441 swap_dev:
 442         .word SWAP_DEV
 443 ram_size:
 444         .word RAMDISK
 445 vid_mode:
 446         .word SVGA_MODE
 447 root_dev:
 448         .word ROOT_DEV
 449 boot_flag:
 450         .word 0xAA55

/* [previous][next][first][last][top][bottom][index][help] */