1 ; -*- fundamental -*- (asm-mode sucks) 2 ; $Id: ldlinux.asm,v 1.42 1998/12/05 08:48:05 hpa Exp $ 3 ; **************************************************************************** 4 ; 5 ; ldlinux.asm 6 ; 7 ; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This 8 ; functionality is good to have for installation floppies, where it may 9 ; be hard to find a functional Linux system to run LILO off. 10 ; 11 ; This program allows manipulation of the disk to take place entirely 12 ; from MS-LOSS, and can be especially useful in conjunction with the 13 ; umsdos filesystem. 14 ; 15 ; This file is loaded in stages; first the boot sector at offset 7C00h, 16 ; then the first sector (cluster, really, but we can only assume 1 sector) 17 ; of LDLINUX.SYS at 7E00h and finally the remainder of LDLINUX.SYS at 8000h. 18 ; 19 ; Copyright (C) 1994-1998 H. Peter Anvin 20 ; 21 ; This program is free software; you can redistribute it and/or modify 22 ; it under the terms of the GNU General Public License as published by 23 ; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, 24 ; USA; either version 2 of the License, or (at your option) any later 25 ; version; incorporated herein by reference. 26 ; 27 ; **************************************************************************** 28 29 ; 30 ; Some semi-configurable constants... change on your own risk. Most are imposed 31 ; by the kernel. 32 ; 33 max_cmd_len equ 255 ; Must be odd; 255 is the kernel limit 34 retry_count equ 6 ; How patient are we with the disk? 35 HIGHMEM_MAX equ 03f000000h ; Highest address for an initrd 36 37 ; 38 ; Should be updated with every release to avoid bootsector/SYS file mismatch 39 ; 40 %define version_str VERSION ; Must be 4 characters long! 41 %define date DATE_STR ; Defined from the Makefile 42 %define year '1998' 43 ; 44 ; Debgging stuff 45 ; 46 ; %define debug 1 ; Uncomment to enable debugging 47 ; 48 ; ID for SYSLINUX (reported to kernel) 49 ; 50 syslinux_id equ 031h ; SYSLINUX (3) version 1.x (1) 51 ; 52 ; Segments used by Linux 53 ; 54 real_mode_seg equ 9000h 55 struc real_mode_seg_t 56 00000000 resb 20h-($-$$) ; org 20h 57 00000020 kern_cmd_magic resw 1 ; Magic # for command line 58 00000022 kern_cmd_offset resw 1 ; Offset for kernel command line 59 00000024 resb 497-($-$$) ; org 497d 60 000001F1 bs_setupsecs resb 1 ; Sectors for setup code (0 -> 4) 61 000001F2 bs_rootflags resw 1 ; Root readonly flag 62 000001F4 bs_syssize resw 1 63 000001F6 bs_swapdev resw 1 ; Swap device (obsolete) 64 000001F8 bs_ramsize resw 1 ; Ramdisk flags, formerly ramdisk size 65 000001FA bs_vidmode resw 1 ; Video mode 66 000001FC bs_rootdev resw 1 ; Root device 67 000001FE bs_bootsign resw 1 ; Boot sector signature (0AA55h) 68 00000200 su_jump resb 1 ; 0EBh 69 00000201 su_jump2 resb 1 70 00000202 su_header resd 1 ; New setup code: header 71 00000206 su_version resw 1 ; See linux/arch/i386/boot/setup.S 72 00000208 su_switch resw 1 73 0000020A su_setupseg resw 1 74 0000020C su_startsys resw 1 75 0000020E su_kver resw 1 ; Kernel version pointer 76 00000210 su_loader resb 1 ; Loader ID 77 00000211 su_loadflags resb 1 ; Load high flag 78 00000212 su_movesize resw 1 79 00000214 su_code32start resd 1 ; Start of code loaded high 80 00000218 su_ramdiskat resd 1 ; Start of initial ramdisk 81 su_ramdisklen equ $ ; Length of initial ramdisk 82 0000021C su_ramdisklen1 resw 1 83 0000021E su_ramdisklen2 resw 1 84 00000220 su_bsklugeoffs resw 1 85 00000222 su_bsklugeseg resw 1 86 00000224 su_heapend resw 1 87 00000226 resb (8000h-12)-($-$$) ; Were bootsect.S puts it... 88 linux_stack equ $ 89 linux_fdctab equ $ 90 00007FF4 resb 8000h-($-$$) 91 cmd_line_here equ $ ; Should be out of the way 92 endstruc 93 94 setup_seg equ 9020h 95 struc setup_seg_t 96 org 0h ; as 9020:0000, not 9000:0200 97 setup_entry equ $ 98 endstruc 99 100 ; 101 ; Magic number of su_header field 102 ; 103 HEADER_ID equ 'HdrS' ; HdrS (in littleendian hex) 104 ; 105 ; Flags for the su_loadflags field 106 ; 107 LOAD_HIGH equ 01h ; Large kernel, load high 108 CAN_USE_HEAP equ 80h ; Boot loader reports heap size 109 ; 110 ; The following structure is used for "virtual kernels"; i.e. LILO-style 111 ; option labels. The options we permit here are `kernel' and `append 112 ; Since there is no room in the bottom 64K for all of these, we 113 ; stick them at 8000:0000 and copy them down before we need them. 114 ; 115 ; Note: this structure can be added to, but it must 116 ; 117 %define vk_power 7 ; log2(max number of vkernels) 118 %define max_vk (1 << vk_power) ; Maximum number of vkernels 119 %define vk_shift (16-vk_power) ; Number of bits to shift 120 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer 121 122 struc vkernel 123 00000000 vk_vname: resb 11 ; Virtual name **MUST BE FIRST!** 124 0000000B vk_rname: resb 11 ; Real name 125 00000016 vk_appendlen: resw 1 126 alignb 4 127 00000018 vk_append: resb max_cmd_len+1 ; Command line 128 alignb 4 129 vk_end: equ $ ; Should be <= vk_size 130 endstruc 131 132 %if (vk_end > vk_size) || (vk_size*max_vk > 65536) 133 %error "Too many vkernels defined, reduce vk_power" 134 %endif 135 136 ; 137 ; Segment assignments in the bottom 640K 138 ; 0000h - main code/data segment (and BIOS segment) 139 ; 9000h - real_mode_seg 140 ; 141 vk_seg equ 8000h ; This is where we stick'em 142 xfer_buf_seg equ 7000h ; Bounce buffer for I/O to high mem 143 fat_seg equ 5000h ; 128K area for FAT (2x64K) 144 comboot_seg equ 1000h ; COMBOOT image loading zone 145 146 ; 147 ; For our convenience: define macros for jump-over-unconditinal jumps 148 ; 149 %macro jmpz 1 150 jnz %%skip 151 jmp %1 152 %%skip: 153 %endmacro 154 155 %macro jmpnz 1 156 jz %%skip 157 jmp %1 158 %%skip: 159 %endmacro 160 161 %macro jmpe 1 162 jne %%skip 163 jmp %1 164 %%skip: 165 %endmacro 166 167 %macro jmpne 1 168 je %%skip 169 jmp %1 170 %%skip: 171 %endmacro 172 173 %macro jmpc 1 174 jnc %%skip 175 jmp %1 176 %%skip: 177 %endmacro 178 179 %macro jmpnc 1 180 jc %%skip 181 jmp %1 182 %%skip: 183 %endmacro 184 185 %macro jmpb 1 186 jnb %%skip 187 jmp %1 188 %%skip: 189 %endmacro 190 191 %macro jmpnb 1 192 jb %%skip 193 jmp %1 194 %%skip: 195 %endmacro 196 197 ; 198 ; Macros similar to res[bwd], but which works in the code segment (after 199 ; section .text) 200 ; 201 %macro zb 1 202 times %1 db 0 203 %endmacro 204 205 %macro zw 1 206 times %1 dw 0 207 %endmacro 208 209 %macro zd 1 210 times %1 dd 0 211 %endmacro 212 213 ; --------------------------------------------------------------------------- 214 ; BEGIN THE BIOS/CODE/DATA SEGMENT 215 ; --------------------------------------------------------------------------- 216 absolute 4*1Eh ; In the interrupt table 217 fdctab equ $ 218 00000078 fdctab1 resw 1 219 0000007A fdctab2 resw 1 220 221 %ifdef debug 222 org 0100h 223 ..start: 224 ; 225 ; Hook for debugger stuff. This gets automatically removed when 226 ; generating the real thing. 227 ; 228 ; Initialize the registers for debugger operation 229 ; 230 cli 231 mov ax,cs 232 mov ds,ax 233 mov es,ax 234 mov ss,ax 235 mov sp,StackBuf 236 sti 237 cld 238 ; 239 ; Load the actual boot sector so we can copy the data block 240 ; 241 xor ax,ax ; Reset floppy 242 xor dx,dx 243 int 13h 244 mov cx,6 ; Retry count... 245 debug_tryloop: push cx 246 mov bx,trackbuf 247 mov cx,0001h 248 xor dx,dx 249 mov ax,0201h 250 int 13h 251 pop cx 252 jnc debug_okay 253 loop debug_tryloop 254 int 3 ; Halt! (Breakpoint) 255 debug_okay: mov si,trackbuf+0bh 256 mov di,bsBytesPerSec 257 mov cx,33h 258 rep movsb 259 ; 260 ; Save bogus "BIOS floppy block" info to the stack in case we hit kaboom 261 ; 262 push si 263 push si 264 push si ; Writing to the trackbuf is harmless 265 ; 266 ; Copy the BIOS data area 267 ; 268 push ds 269 xor ax,ax 270 mov ds,ax 271 mov si,0400h 272 mov di,si 273 mov cx,0100h 274 rep movsw 275 pop ds 276 ; 277 ; 278 ; A NOP where we can breakpoint, then jump into the code *after* 279 ; the segment register initialization section 280 ; 281 nop 282 jmp debugentrypt 283 %endif 284 285 absolute 0484h 286 00000484 BIOS_vidrows resb 1 ; Number of screen rows 287 288 ; 289 ; Memory below this point is reserved for the BIOS and the MBR 290 ; 291 absolute 1000h 292 trackbuf equ $ ; Track buffer goes here 293 trackbufsize equ 16384 ; Safe size of track buffer 294 ; trackbuf ends at 5000h 295 296 absolute 6000h ; Here we keep our BSS stuff 297 StackBuf equ $ ; Start the stack here (grow down - 4K) 298 00006000 VKernelBuf: resb vk_size ; "Current" vkernel 299 alignb 4 300 00006200 AppendBuf resb max_cmd_len+1 ; append= 301 00006300 KbdMap resb 256 ; Keyboard map 302 00006400 FKeyName resb 10*16 ; File names for F-key help 303 000064A0 NumBuf resb 16 ; Buffer to load number 304 NumBufEnd equ NumBuf+15 ; Pointer to last byte in NumBuf 305 alignb 4 306 000064B0 PartInfo resb 16 ; Partition table entry 307 000064C0 InitRDat resd 1 ; Load address (linear) for initrd 308 000064C4 HiLoadAddr resd 1 ; Address pointer for high load loop 309 000064C8 HighMemSize resd 1 ; End of memory pointer (bytes) 310 000064CC KernelSize resd 1 ; Size of kernel (bytes) 311 000064D0 KernelName resb 12 ; Mangled name for kernel 312 ; (note the spare byte after!) 313 RootDir equ $ ; Location of root directory 314 000064DC RootDir1 resw 1 315 000064DE RootDir2 resw 1 316 DataArea equ $ ; Location of data area 317 000064E0 DataArea1 resw 1 318 000064E2 DataArea2 resw 1 319 FBytes equ $ ; Used by open/getc 320 000064E4 FBytes1 resw 1 321 000064E6 FBytes2 resw 1 322 000064E8 RootDirSize resw 1 ; Root dir size in sectors 323 000064EA DirScanCtr resw 1 ; Used while searching directory 324 000064EC DirBlocksLeft resw 1 ; Ditto 325 000064EE EndofDirSec resw 1 ; = trackbuf+bsBytesPerSec-31 326 000064F0 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS 327 000064F2 ClustSize resw 1 ; Bytes/cluster 328 000064F4 SecPerClust resw 1 ; Same as bsSecPerClust, but a word 329 000064F6 NextCluster resw 1 ; Pointer to "nextcluster" routine 330 000064F8 BufSafe resw 1 ; Clusters we can load into trackbuf 331 000064FA BufSafeSec resw 1 ; = how many sectors? 332 000064FC BufSafeBytes resw 1 ; = how many bytes? 333 000064FE EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes 334 00006500 KernelClust resw 1 ; Kernel size in clusters 335 00006502 InitRDClust resw 1 ; Ramdisk size in clusters 336 00006504 ClustPerMoby resw 1 ; Clusters per 64K 337 00006506 FClust resw 1 ; Number of clusters in open/getc file 338 00006508 FNextClust resw 1 ; Pointer to next cluster in d:o 339 0000650A FPtr resw 1 ; Pointer to next char in buffer 340 0000650C CmdOptPtr resw 1 ; Pointer to first option on cmd line 341 0000650E KernelCNameLen resw 1 ; Length of unmangled kernel name 342 00006510 InitRDCNameLen resw 1 ; Length of unmangled initrd name 343 00006512 NextCharJump resw 1 ; Routine to interpret next print char 344 00006514 SetupSecs resw 1 ; Number of setup sectors 345 00006516 SavedSP resw 1 ; Our SP while running a COMBOOT image 346 00006518 A20Test resw 1 ; Counter for testing status of A20 347 TextAttrBX equ $ 348 0000651A TextAttribute resb 1 ; Text attribute for message file 349 0000651B TextPage resb 1 ; Active display page 350 CursorDX equ $ 351 0000651C CursorCol resb 1 ; Cursor column for message file 352 0000651D CursorRow resb 1 ; Cursor row for message file 353 ScreenSize equ $ 354 0000651E VidCols resb 1 ; Columns on screen-1 355 0000651F VidRows resb 1 ; Rows on screen-1 356 00006520 RetryCount resb 1 ; Used for disk access retries 357 00006521 KbdFlags resb 1 ; Check for keyboard escapes 358 00006522 LoadFlags resb 1 ; Loadflags from kernel 359 00006523 A20Tries resb 1 ; Times until giving up on A20 360 00006524 MNameBuf resb 11 ; Generic mangled file name buffer 361 0000652F InitRD resb 11 ; initrd= mangled name 362 0000653A KernelCName resb 13 ; Unmangled kernel name 363 00006547 InitRDCName resb 13 ; Unmangled initrd name 364 365 section .text 366 org 7C00h 367 ; 368 ; Primary entry point. Tempting as though it may be, we can't put the 369 ; initial "cli" here; the jmp opcode in the first byte is part of the 370 ; "magic number" (using the term very loosely) for the DOS superblock. 371 ; 372 bootsec equ $ 373 00000000 EB3C jmp short start ; 2 bytes 374 00000002 90 nop ; 1 byte 375 ; 376 ; "Superblock" follows -- it's in the boot sector, so it's already 377 ; loaded and ready for us 378 ; 379 00000003 5359534C494E5558 bsOemName db 'SYSLINUX' ; The SYS command sets this, so... 380 superblock equ $ 381 bsBytesPerSec zw 1 382 <1> bsBytesPerSec : 383 0000000B 0000 <1> times %1 dw 0 384 bsSecPerClust zb 1 385 <1> bsSecPerClust : 386 0000000D 00 <1> times %1 db 0 387 bsResSectors zw 1 388 <1> bsResSectors : 389 0000000E 0000 <1> times %1 dw 0 390 bsFATs zb 1 391 <1> bsFATs : 392 00000010 00 <1> times %1 db 0 393 bsRootDirEnts zw 1 394 <1> bsRootDirEnts : 395 00000011 0000 <1> times %1 dw 0 396 bsSectors zw 1 397 <1> bsSectors : 398 00000013 0000 <1> times %1 dw 0 399 bsMedia zb 1 400 <1> bsMedia : 401 00000015 00 <1> times %1 db 0 402 bsFATsecs zw 1 403 <1> bsFATsecs : 404 00000016 0000 <1> times %1 dw 0 405 bsSecPerTrack zw 1 406 <1> bsSecPerTrack : 407 00000018 0000 <1> times %1 dw 0 408 bsHeads zw 1 409 <1> bsHeads : 410 0000001A 0000 <1> times %1 dw 0 411 bsHiddenSecs equ $ 412 bsHidden1 zw 1 413 <1> bsHidden1 : 414 0000001C 0000 <1> times %1 dw 0 415 bsHidden2 zw 1 416 <1> bsHidden2 : 417 0000001E 0000 <1> times %1 dw 0 418 bsHugeSectors equ $ 419 bsHugeSec1 zw 1 420 <1> bsHugeSec1 : 421 00000020 0000 <1> times %1 dw 0 422 bsHugeSec2 zw 1 423 <1> bsHugeSec2 : 424 00000022 0000 <1> times %1 dw 0 425 bsDriveNumber zb 1 426 <1> bsDriveNumber : 427 00000024 00 <1> times %1 db 0 428 bsReserved1 zb 1 429 <1> bsReserved1 : 430 00000025 00 <1> times %1 db 0 431 bsBootSignature zb 1 ; 29h if the following fields exist 432 <1> bsBootSignature : 433 00000026 00 <1> times %1 db 0 434 bsVolumeID zd 1 435 <1> bsVolumeID : 436 00000027 00000000 <1> times %1 dd 0 437 bsVolumeLabel zb 11 438 <1> bsVolumeLabel : 439 0000002B 00 <1> times %1 db 0 440 bsFileSysType zb 8 ; Must be FAT12 for this version 441 <1> bsFileSysType : 442 00000036 00 <1> times %1 db 0 443 superblock_len equ $-superblock 444 ; 445 ; Note we don't check the constraints above now; we did that at install 446 ; time (we hope!) 447 ; 448 449 ;floppy_table equ $ ; No sense in wasting memory, overwrite start 450 451 start: 452 0000003E FA cli ; No interrupts yet, please 453 0000003F FC cld ; Copy upwards 454 ; 455 ; Set up the stack 456 ; 457 00000040 31C9 xor cx,cx 458 00000042 8ED1 mov ss,cx 459 00000044 BC0060 mov sp,StackBuf ; Just below BSS 460 00000047 8EC1 mov es,cx 461 ; 462 ; DS:SI may contain a partition table entry. Preserve it for us. 463 ; 464 00000049 B108 mov cl,8 ; Save partition info (CH == 0) 465 0000004B BFB064 mov di,PartInfo 466 0000004E F3A5 rep movsw 467 ; 468 ; Now sautee the BIOS floppy info block to that it will support decent- 469 ; size transfers; the floppy block is 11 bytes and is stored in the 470 ; INT 1Eh vector (brilliant waste of resources, eh?) 471 ; 472 ; Of course, if BIOSes had been properly programmed, we wouldn't have 473 ; had to waste precious boot sector space with this code. 474 ; 475 ; This code no longer fits. Hope that noone really needs it anymore. 476 ; (If so, it needs serious updating.) In fact, some indications is that 477 ; this code does more harm than good with all the new kinds of drives and 478 ; media. 479 ; 480 %ifdef SUPPORT_REALLY_BROKEN_BIOSES 481 lds si,[ss:fdctab] ; DS:SI -> original 482 push ds ; Save on stack in case 483 push si ; we have to bail 484 push bx 485 mov cx,6 ; 12 bytes 486 mov di,floppy_table 487 push di 488 cld 489 rep movsw ; Faster to move words 490 pop di 491 mov ds,ax ; Now we can point DS to here, too 492 mov cl,[bsSecPerTrack] ; Patch the sector count 493 mov [di+4],cl 494 mov [fdctab+2],ax ; Segment 0 495 mov [fdctab],di ; offset floppy_block 496 %else 497 00000050 8ED9 mov ds,cx ; CX == 0 498 %endif 499 ; 500 ; Ready to enable interrupts, captain 501 ; 502 00000052 FB sti 503 ; 504 ; The drive number and possibly partition information was passed to us 505 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to 506 ; trust that rather than what the superblock contains. 507 ; 508 ; Would it be better to zero out bsHidden if we don't have a partition table? 509 ; 510 ; Note: di points to beyond the end of PartInfo 511 ; 512 00000053 8816[2400] mov [bsDriveNumber],dl 513 00000057 F6C280 test dl,80h ; If floppy disk (00-7F), assume no 514 0000005A 7428 jz not_harddisk ; partition table 515 0000005C F645F07F test byte [di-16],7Fh ; Sanity check: "active flag" should 516 00000060 750A jnz no_partition ; be 00 or 80 517 00000062 8D75F8 lea si,[di-8] ; Partition offset (dword) 518 00000065 BF[1C00] mov di,bsHidden1 519 00000068 B102 mov cl,2 ; CH == 0 520 0000006A F3A5 rep movsw 521 no_partition: 522 ; 523 ; Get disk drive parameters (don't trust the superblock.) Don't do this for 524 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about 525 ; what the *drive* supports, not about the *media*. Fortunately floppy disks 526 ; tend to have a fixed, well-defined geometry which is stored in the superblock. 527 ; 528 ; DL == drive # still 529 0000006C B408 mov ah,08h 530 0000006E CD13 int 13h 531 00000070 7212 jc no_driveparm 532 00000072 20E4 and ah,ah 533 00000074 750E jnz no_driveparm 534 00000076 FEC6 inc dh ; Contains # of heads - 1 535 00000078 8836[1A00] mov [bsHeads],dh 536 0000007C 81E13F00 and cx,3fh 537 00000080 890E[1800] mov [bsSecPerTrack],cx 538 no_driveparm: 539 not_harddisk: 540 ; 541 ; Now we have to do some arithmetric to figure out where things are located. 542 ; If Micro$oft had had brains they would already have done this for us, 543 ; and stored it in the superblock at format time, but here we go, 544 ; wasting precious boot sector space again... 545 ; 546 debugentrypt: 547 00000084 31C0 xor ax,ax ; INT 13:08 destroys ES 548 00000086 8EC0 mov es,ax 549 00000088 A0[1000] mov al,[bsFATs] ; Number of FATs (AH == 0) 550 0000008B F726[1600] mul word [bsFATsecs] ; Get the size of the FAT area 551 0000008F 0306[1C00] add ax,[bsHidden1] ; Add hidden sectors 552 00000093 1316[1E00] adc dx,[bsHidden2] 553 00000097 0306[0E00] add ax,[bsResSectors] ; And reserved sectors 554 0000009B 83D200 adc dx,byte 0 555 556 0000009E A3DC64 mov [RootDir1],ax ; Location of root directory 557 000000A1 8916DE64 mov [RootDir2],dx 558 000000A5 A3E064 mov [DataArea1],ax 559 000000A8 8916E264 mov [DataArea2],dx 560 000000AC 50 push ax 561 000000AD 52 push dx 562 563 000000AE B82000 mov ax,32 ; Size of a directory entry 564 000000B1 F726[1100] mul word [bsRootDirEnts] 565 000000B5 8B1E[0B00] mov bx,[bsBytesPerSec] 566 000000B9 01D8 add ax,bx ; Round up, not down 567 000000BB 48 dec ax 568 000000BC F7F3 div bx ; Now we have the size of the root dir 569 000000BE A3E864 mov [RootDirSize],ax 570 000000C1 A3EA64 mov [DirScanCtr],ax 571 000000C4 81C3E10F add bx,trackbuf-31 572 000000C8 891EEE64 mov [EndofDirSec],bx ; End of a single directory sector 573 574 000000CC 0106E064 add [DataArea1],ax 575 000000D0 8316E26400 adc word [DataArea2],byte 0 576 577 000000D5 5A pop dx ; Reload root directory starting point 578 000000D6 58 pop ax 579 ; 580 ; Now the fun begins. We have to search the root directory for 581 ; LDLINUX.SYS and load the first sector, so we have a little more 582 ; space to have fun with. Then we can go chasing through the FAT. 583 ; Joy!! 584 ; 585 000000D7 50 sd_nextsec: push ax 586 000000D8 52 push dx 587 000000D9 BB0010 mov bx,trackbuf 588 000000DC 53 push bx 589 000000DD E88D00 call getonesec 590 000000E0 5E pop si 591 000000E1 803C00 sd_nextentry: cmp byte [si],0 ; Directory high water mark 592 000000E4 7429 je kaboom 593 000000E6 F6440B18 test byte [si+11],18h ; Must be a file 594 000000EA 750C jnz sd_not_file 595 000000EC BF[EF01] mov di,ldlinux_name 596 000000EF B90B00 mov cx,11 597 000000F2 56 push si 598 000000F3 F3A6 repe cmpsb 599 000000F5 5E pop si 600 000000F6 742D je found_it 601 000000F8 83C620 sd_not_file: add si,byte 32 ; Distance to next 602 000000FB 3B36EE64 cmp si,[EndofDirSec] 603 000000FF 72E0 jb sd_nextentry 604 00000101 5A pop dx 605 00000102 58 pop ax 606 00000103 83C001 add ax,byte 1 607 00000106 83D200 adc dx,byte 0 608 00000109 FF0EEA64 dec word [DirScanCtr] 609 0000010D 75C8 jnz sd_nextsec 610 ; 611 ; kaboom: write a message and bail out. 612 ; 613 kaboom: 614 0000010F 31F6 xor si,si 615 00000111 8ED6 mov ss,si 616 00000113 BC0060 mov sp,StackBuf ; Reset stack 617 00000116 8EDE mov ds,si ; Reset data segment 618 00000118 BE[DC01] mov si,bailmsg 619 0000011B E83700 call writestr ; Returns with AL = 0 620 0000011E 98 cbw ; AH <- 0 621 0000011F CD16 int 16h ; Wait for keypress 622 00000121 CD19 int 19h ; And try once more to boot... 623 00000123 EBFE norge: jmp short norge ; If int 19h returned; this is the end 624 625 ; 626 ; found_it: now we compute the location of the first sector, then 627 ; load it and JUMP (since we're almost out of space) 628 ; 629 found_it: ; Note: we actually leave two words on the stack here 630 ; (who cares?) 631 00000125 31C0 xor ax,ax 632 00000127 A0[0D00] mov al,[bsSecPerClust] 633 0000012A 89C5 mov bp,ax ; Load an entire cluster 634 0000012C 8B5C1A mov bx,[si+26] ; First cluster 635 0000012F 891EF064 mov [RunLinClust],bx ; Save for later use 636 00000133 4B dec bx ; First cluster is "cluster 2" 637 00000134 4B dec bx 638 00000135 F7E3 mul bx 639 00000137 0306E064 add ax,[DataArea1] 640 0000013B 1316E264 adc dx,[DataArea2] 641 0000013F BB[0002] mov bx,ldlinux_sys 642 00000142 E82B00 call getlinsec 643 00000145 BE[EF01] mov si,bs_magic 644 00000148 BF[1F02] mov di,ldlinux_magic 645 0000014B B91100 mov cx,magic_len 646 0000014E F3A6 repe cmpsb ; Make sure that the bootsector 647 00000150 75BD jne kaboom ; matches LDLINUX.SYS 648 ; 649 ; Done! Jump to the entry point! 650 ; 651 00000152 E9DB00 jmp ldlinux_ent 652 653 ; 654 ; 655 ; writestr: write a null-terminated string to the console 656 ; 657 writestr: 658 00000155 AC wstr_1: lodsb 659 00000156 20C0 and al,al 660 00000158 7412 jz return 661 0000015A B40E mov ah,0Eh ; Write to screen as TTY 662 0000015C BB0700 mov bx,0007h ; White on black, current page 663 0000015F CD10 int 10h 664 00000161 EBF2 jmp short wstr_1 665 ; 666 ; disk_error: decrement the retry count and bail if zero 667 ; 668 00000163 4E disk_error: dec si ; SI holds the disk retry counter 669 00000164 74A9 jz kaboom 670 00000166 93 xchg ax,bx ; Shorter than MOV 671 00000167 5B pop bx ; 672 00000168 59 pop cx ; 673 00000169 5A pop dx ; 674 0000016A EB3C jmp short disk_try_again 675 676 0000016C C3 return: ret 677 678 ; 679 ; getonesec: like getlinsec, but pre-sets the count to 1 680 ; 681 getonesec: 682 0000016D BD0100 mov bp,1 683 ; Fall through to getlinsec 684 685 ; 686 ; getlinsec: load a sequence of BP floppy sector given by the linear sector 687 ; number in DX:AX into the buffer at ES:BX. We try to optimize 688 ; by loading up to a whole track at a time, but the user 689 ; is responsible for not crossing a 64K boundary. 690 ; (Yes, BP is weird for a count, but it was available...) 691 ; 692 ; On return, BX points to the first byte after the transferred 693 ; block. 694 ; 695 ; The "stupid patch area" gets replaced by the code 696 ; mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with 697 ; the -s option. 698 ; 699 ; Stylistic note: use "xchg" instead of "mov" when the source is a register 700 ; that is dead from that point; this saves space. However, please keep 701 ; the order to dst,src to keep things sane. 702 ; 703 getlinsec: 704 00000170 8B36[1800] mov si,[bsSecPerTrack] 705 ; 706 ; Dividing by sectors to get (track,sector): we may have 707 ; up to 2^18 tracks, so we need to do this in two steps 708 ; to produce a 32-bit quotient. 709 ; 710 00000174 91 xchg cx,ax ; CX <- LSW of LBA 711 00000175 92 xchg ax,dx 712 00000176 31D2 xor dx,dx ; DX:AX now == MSW of LBA 713 00000178 F7F6 div si ; Obtain MSW of track # 714 0000017A 91 xchg ax,cx ; Remainder -> MSW of new dividend 715 ; LSW of LBA -> LSW of new dividend 716 ; Quotient -> MSW of track # 717 0000017B F7F6 div si ; Obtain LSW of track #, remainder 718 0000017D 87CA xchg cx,dx ; CX <- Sector index (0-based) 719 ; DX <- MSW of track # 720 0000017F F736[1A00] div word [bsHeads] ; Convert track to head/cyl 721 ; 722 ; Now we have AX = cyl, DX = head, CX = sector (0-based), 723 ; BP = sectors to transfer, SI = bsSecPerTrack, 724 ; ES:BX = data target 725 ; 726 00000183 56 gls_nextchunk: push si ; bsSecPerTrack 727 00000184 55 push bp ; Sectors to transfer 728 729 __BEGIN_STUPID_PATCH_AREA: 730 00000185 29CE sub si,cx ; Sectors left on track 731 00000187 39F5 cmp bp,si 732 00000189 7602 jna gls_lastchunk 733 0000018B 89F5 mov bp,si ; No more than a trackful, please! 734 __END_STUPID_PATCH_AREA: 735 gls_lastchunk: 736 0000018D 50 push ax ; Cylinder # 737 0000018E 52 push dx ; Head # 738 0000018F 55 push bp ; Number of sectors we're transferring 739 740 00000190 51 push cx ; Sector # 741 00000191 B106 mov cl,6 ; Because IBM was STOOPID 742 00000193 D2E4 shl ah,cl ; and thought 8 bits were enough 743 ; then thought 10 bits were enough... 744 00000195 59 pop cx ; Sector # 745 00000196 51 push cx 746 00000197 41 inc cx ; Sector numbers are 1-based 747 00000198 08E1 or cl,ah 748 0000019A 88C5 mov ch,al 749 0000019C 88D6 mov dh,dl 750 0000019E 8A16[2400] mov dl,[bsDriveNumber] 751 000001A2 95 xchg ax,bp ; Sector to transfer count 752 ; (xchg shorter than mov) 753 000001A3 B402 mov ah,02h ; Read it! 754 ; 755 ; Do the disk transfer... save the registers in case we fail :( 756 ; 757 000001A5 BE0600 mov si,retry_count ; # of times to retry a disk access 758 000001A8 52 disk_try_again: push dx ; 759 000001A9 51 push cx ; 760 000001AA 53 push bx ; 761 000001AB 50 push ax ; 762 000001AC 56 push si ; 763 000001AD CD13 int 13h 764 000001AF 5E pop si ; 765 000001B0 5B pop bx ; 766 000001B1 72B0 jc disk_error 767 ; 768 ; Disk access successful 769 ; 770 000001B3 5B pop bx ; Buffer location 771 000001B4 58 pop ax ; No longer needed 772 000001B5 58 pop ax ; No longer needed 773 000001B6 59 pop cx ; Sector # 774 000001B7 5F pop di ; Sector transferred count 775 000001B8 89F8 mov ax,di ; Reduce sector left count 776 000001BA F726[0B00] mul word [bsBytesPerSec] ; Figure out how much to advance ptr 777 000001BE 01C3 add bx,ax ; Update buffer location 778 000001C0 5A pop dx ; Head # 779 000001C1 58 pop ax ; Cyl # 780 000001C2 5D pop bp ; Sectors left to transfer 781 000001C3 5E pop si ; Number of sectors/track 782 000001C4 29FD sub bp,di ; Reduce with # of sectors just read 783 000001C6 74A4 jz return ; Done! 784 000001C8 01F9 add cx,di 785 000001CA 39F1 cmp cx,si 786 000001CC 72B5 jb gls_nextchunk 787 000001CE 42 inc dx ; Next track on cyl 788 000001CF 3B16[1A00] cmp dx,[bsHeads] ; Was this the last one? 789 000001D3 7203 jb gls_nonewcyl 790 000001D5 40 inc ax ; If so, new cylinder 791 000001D6 31D2 xor dx,dx ; First head on new cylinder 792 000001D8 29F1 gls_nonewcyl: sub cx,si ; First sector on new track 793 000001DA EBA7 jmp short gls_nextchunk 794 795 000001DC 426F6F74206661696C- bailmsg: db 'Boot failed', 0Dh, 0Ah, 0 796 000001E5 65640D0A00 797 798 bs_checkpt equ $ ; Must be <= 1EFh 799 800 zb 1EFh-($-$$) 801 000001EA 00 <1> times %1 db 0 802 bs_magic equ $ ; From here to the magic_len equ 803 ; must match ldlinux_magic 804 000001EF 4C444C494E55582053- ldlinux_name: db 'LDLINUX SYS' ; Looks like this in the root dir 805 000001F8 5953 806 000001FA FDF56836 dd HEXDATE ; Hopefully unique between compiles 807 808 000001FE 55AA bootsignature dw 0AA55h 809 magic_len equ $-bs_magic 810 811 ; 812 ; =========================================================================== 813 ; End of boot sector 814 ; =========================================================================== 815 ; Start of LDLINUX.SYS 816 ; =========================================================================== 817 818 ldlinux_sys: 819 820 00000200 0D0A5359534C494E55- syslinux_banner db 0Dh, 0Ah, 'SYSLINUX ', version_str, ' ', date, ' ', 0 821 00000209 5820312E3432203139- 822 00000212 39382D31322D303520- 823 0000021B 00 824 0000021C 0D0A1A db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS 825 826 0000021F 4C444C494E55582053- ldlinux_magic db 'LDLINUX SYS' 827 00000228 5953 828 0000022A FDF56836 dd HEXDATE 829 0000022E 55AA dw 0AA55h 830 831 align 4 832 833 ; 834 ; Entry point. Note that some BIOSes are buggy and put the boot sector 835 ; at 07C0:0000 instead of 0000:7C00 and the like. We don't want to add 836 ; anything more to the boot sector, so it is written to not assume a fixed 837 ; value in CS, but we don't want to deal with that anymore from now on. 838 ; 839 ldlinux_ent: 840 00000230 EA[3502]0000 jmp 0:ldlinux_ent2 841 ldlinux_ent2: 842 ; 843 ; Tell the user we got this far 844 ; 845 00000235 BE[0002] mov si,syslinux_banner 846 00000238 E81AFF call writestr 847 ; 848 ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS. 849 ; We can really only rely on a single sector having been loaded. Hence 850 ; we should load the FAT into RAM and start chasing pointers... 851 ; 852 0000023B BA0100 mov dx,1 ; 64K 853 0000023E 31C0 xor ax,ax 854 00000240 F736[0B00] div word [bsBytesPerSec] ; sectors/64K 855 00000244 89C6 mov si,ax 856 857 00000246 06 push es 858 00000247 BB0050 mov bx,fat_seg ; Load into fat_seg:0000 859 0000024A 8EC3 mov es,bx 860 861 0000024C A1[1C00] mov ax,[bsHidden1] ; Hidden sectors 862 0000024F 8B16[1E00] mov dx,[bsHidden2] 863 00000253 0306[0E00] add ax,[bsResSectors] ; plus reserved sectors = FAT 864 00000257 83D200 adc dx,byte 0 865 0000025A 8B0E[1600] mov cx,[bsFATsecs] ; Sectors/FAT 866 fat_load_loop: 867 0000025E 89CD mov bp,cx 868 00000260 39F5 cmp bp,si 869 00000262 7602 jna fat_load 870 00000264 89F5 mov bp,si ; A full 64K moby 871 fat_load: 872 00000266 31DB xor bx,bx ; Offset 0 in the current ES 873 00000268 E82201 call getlinsecsr 874 0000026B 29E9 sub cx,bp 875 0000026D 740F jz fat_load_done ; Last moby? 876 0000026F 01E8 add ax,bp ; Advance sector count 877 00000271 83D200 adc dx,byte 0 878 00000274 8CC3 mov bx,es ; Next 64K moby 879 00000276 81C30010 add bx,1000h 880 0000027A 8EC3 mov es,bx 881 0000027C EBE0 jmp short fat_load_loop 882 fat_load_done: 883 0000027E 07 pop es 884 ; 885 ; Fine, now we have the FAT in memory. How big is a cluster, really? 886 ; Also figure out how many clusters will fit in an 8K buffer, and how 887 ; many sectors and bytes that is 888 ; 889 0000027F 8B3E[0B00] mov di,[bsBytesPerSec] ; Used a lot below 890 891 00000283 A0[0D00] mov al,[bsSecPerClust] ; We do this in the boot 892 00000286 30E4 xor ah,ah ; sector, too, but there 893 00000288 A3F464 mov [SecPerClust],ax ; wasn't space to save it 894 0000028B 89C6 mov si,ax ; Also used a lot... 895 0000028D F7E7 mul di 896 0000028F A3F264 mov [ClustSize],ax ; Bytes/cluster 897 00000292 89C3 mov bx,ax 898 00000294 B80040 mov ax,trackbufsize 899 00000297 31D2 xor dx,dx 900 00000299 F7F3 div bx 901 0000029B A3F864 mov [BufSafe],ax ; # of cluster in trackbuf 902 0000029E F726F464 mul word [SecPerClust] 903 000002A2 A3FA64 mov [BufSafeSec],ax 904 000002A5 F7E7 mul di 905 000002A7 A3FC64 mov [BufSafeBytes],ax 906 000002AA 050096 add ax,getcbuf ; Size of getcbuf is the same 907 000002AD A3FE64 mov [EndOfGetCBuf],ax ; as for trackbuf 908 ; 909 ; FAT12 or FAT16? This computation is fscking ridiculous... 910 ; 911 000002B0 31D2 xor dx,dx 912 000002B2 31C9 xor cx,cx 913 000002B4 A1[1300] mov ax,[bsSectors] 914 000002B7 21C0 and ax,ax 915 000002B9 7507 jnz have_secs 916 000002BB A1[2000] mov ax,[bsHugeSectors] 917 000002BE 8B16[2200] mov dx,[bsHugeSectors+2] 918 000002C2 2B06[0E00] have_secs: sub ax,[bsResSectors] 919 000002C6 83DA00 sbb dx,byte 0 920 000002C9 8A0E[1000] mov cl,[bsFATs] 921 000002CD 2B06[1600] sec_fat_loop: sub ax,[bsFATsecs] 922 000002D1 83DA00 sbb dx,byte 0 923 000002D4 E2F7 loop sec_fat_loop 924 000002D6 50 push ax 925 000002D7 52 push dx 926 000002D8 A1[1100] mov ax,[bsRootDirEnts] 927 000002DB BB2000 mov bx,32 ; Smaller than shift since we 928 000002DE F7E3 mul bx ; need the doubleword product 929 000002E0 01F8 add ax,di 930 000002E2 83D200 adc dx,byte 0 931 000002E5 83E801 sub ax,byte 1 932 000002E8 83DA00 sbb dx,byte 0 933 000002EB F7F7 div di 934 000002ED 89C3 mov bx,ax 935 000002EF 5A pop dx 936 000002F0 58 pop ax 937 000002F1 29D8 sub ax,bx 938 000002F3 83DA00 sbb dx,byte 0 939 000002F6 F7F6 div si 940 000002F8 3DF60F cmp ax,4086 ; Right value? 941 000002FB B8[C403] mov ax,nextcluster_fat16 942 000002FE 7703 ja have_fat_type 943 00000300 B8[9D03] have_fat12: mov ax,nextcluster_fat12 944 00000303 A3F664 have_fat_type: mov word [NextCluster],ax 945 946 ; 947 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first 948 ; cluster again, though. 949 ; 950 load_rest: 951 00000306 8B0EF264 mov cx,[ClustSize] 952 0000030A BB[0002] mov bx,ldlinux_sys 953 0000030D 01CB add bx,cx 954 0000030F 8B36F064 mov si,[RunLinClust] 955 00000313 FF16F664 call [NextCluster] 956 00000317 31D2 xor dx,dx 957 00000319 B85117 mov ax,ldlinux_len-1 ; To be on the safe side 958 0000031C 01C8 add ax,cx 959 0000031E F7F1 div cx ; the number of clusters 960 00000320 48 dec ax ; We've already read one 961 00000321 7405 jz all_read_jmp 962 00000323 89C1 mov cx,ax 963 00000325 E80300 call getfssec 964 ; 965 ; All loaded up 966 ; 967 all_read_jmp: 968 00000328 E9B100 jmp all_read 969 ; 970 ; ----------------------------------------------------------------------------- 971 ; Subroutines that have to be in the first sector 972 ; ----------------------------------------------------------------------------- 973 ; 974 ; getfssec: Get multiple clusters from a file, given the starting cluster. 975 ; 976 ; This routine makes sure the subtransfers do not cross a 64K boundary, 977 ; and will correct the situation if it does, UNLESS *sectors* cross 978 ; 64K boundaries. 979 ; 980 ; ES:BX -> Buffer 981 ; SI -> Starting cluster number (2-based) 982 ; CX -> Cluster count (0FFFFh = until end of file) 983 ; 984 ; 386 check 985 getfssec: 986 0000032B 31ED getfragment: xor bp,bp ; Fragment sector count 987 0000032D 89F0 mov ax,si ; Get sector address 988 0000032F 48 dec ax ; Convert to 0-based 989 00000330 48 dec ax 990 00000331 F726F464 mul word [SecPerClust] 991 00000335 0306E064 add ax,[DataArea1] 992 00000339 1316E264 adc dx,[DataArea2] 993 getseccnt: ; See if we can read > 1 clust 994 0000033D 032EF464 add bp,[SecPerClust] 995 00000341 49 dec cx ; Reduce clusters left to find 996 00000342 89F7 mov di,si ; Predict next cluster 997 00000344 47 inc di 998 00000345 FF16F664 call [NextCluster] 999 00000349 7207 jc gfs_eof ; At EOF? 1000 0000034B E304 jcxz endfragment ; Or was it the last we wanted? 1001 0000034D 39FE cmp si,di ; Is file continuous? 1002 0000034F 74EC jz getseccnt ; Yes, we can get 1003 00000351 F8 endfragment: clc ; Not at EOF 1004 00000352 9C gfs_eof: pushf ; Remember EOF or not 1005 00000353 56 push si 1006 00000354 51 push cx 1007 gfs_getchunk: 1008 00000355 50 push ax 1009 00000356 52 push dx 1010 00000357 8CC0 mov ax,es ; Check for 64K boundaries. 1011 00000359 B104 mov cl,4 1012 0000035B D3E0 shl ax,cl 1013 0000035D 01D8 add ax,bx 1014 0000035F 31D2 xor dx,dx 1015 00000361 F7D8 neg ax 1016 00000363 7501 jnz gfs_partseg 1017 00000365 42 inc dx ; Full 64K segment 1018 gfs_partseg: 1019 00000366 F736[0B00] div word [bsBytesPerSec] ; How many sectors fit? 1020 0000036A 89EE mov si,bp 1021 0000036C 29C6 sub si,ax ; Compute remaining sectors 1022 0000036E 7610 jbe gfs_lastchunk 1023 00000370 89C5 mov bp,ax 1024 00000372 5A pop dx 1025 00000373 58 pop ax 1026 00000374 E81600 call getlinsecsr 1027 00000377 01E8 add ax,bp 1028 00000379 83D200 adc dx,byte 0 1029 0000037C 89F5 mov bp,si ; Remaining sector count 1030 0000037E EBD5 jmp short gfs_getchunk 1031 00000380 5A gfs_lastchunk: pop dx 1032 00000381 58 pop ax 1033 00000382 E8EBFD call getlinsec 1034 00000385 59 pop cx 1035 00000386 5E pop si 1036 00000387 9D popf 1037 00000388 E302 jcxz gfs_return ; If we hit the count limit 1038 0000038A 739F jnc getfragment ; If we didn't hit EOF 1039 0000038C C3 gfs_return: ret 1040 1041 ; 1042 ; getlinsecsr: save registers, call getlinsec, restore registers 1043 ; 1044 0000038D 50 getlinsecsr: push ax 1045 0000038E 52 push dx 1046 0000038F 51 push cx 1047 00000390 55 push bp 1048 00000391 56 push si 1049 00000392 57 push di 1050 00000393 E8DAFD call getlinsec 1051 00000396 5F pop di 1052 00000397 5E pop si 1053 00000398 5D pop bp 1054 00000399 59 pop cx 1055 0000039A 5A pop dx 1056 0000039B 58 pop ax 1057 0000039C C3 ret 1058 1059 ; 1060 ; nextcluster: Advance a cluster pointer in SI to the next cluster 1061 ; pointed at in the FAT tables (note: FAT12 assumed) 1062 ; Sets CF on return if end of file. 1063 ; 1064 ; The variable NextCluster gets set to the appropriate 1065 ; value here. 1066 ; 1067 nextcluster_fat12: 1068 0000039D 50 push ax 1069 0000039E 1E push ds 1070 0000039F B80050 mov ax,fat_seg 1071 000003A2 8ED8 mov ds,ax 1072 000003A4 89F0 mov ax,si ; Multiply by 3/2 1073 000003A6 D1E8 shr ax,1 1074 000003A8 9C pushf ; CF now set if odd 1075 000003A9 01C6 add si,ax 1076 000003AB 8B34 mov si,[si] 1077 000003AD 9D popf 1078 000003AE 7308 jnc nc_even 1079 000003B0 D1EE shr si,1 ; Needed for odd only 1080 000003B2 D1EE shr si,1 1081 000003B4 D1EE shr si,1 1082 000003B6 D1EE shr si,1 1083 nc_even: 1084 000003B8 81E6FF0F and si,0FFFh 1085 000003BC 81FEF00F cmp si,0FF0h ; Clears CF if at end of file 1086 000003C0 F5 cmc ; But we want it SET... 1087 000003C1 1F pop ds 1088 000003C2 58 pop ax 1089 000003C3 C3 nc_return: ret 1090 1091 ; 1092 ; FAT16 decoding routine. Note that a 16-bit FAT can be up to 128K, 1093 ; so we have to decide if we're in the "low" or the "high" 64K-segment... 1094 ; 1095 nextcluster_fat16: 1096 000003C4 50 push ax 1097 000003C5 1E push ds 1098 000003C6 B80050 mov ax,fat_seg 1099 000003C9 D1E6 shl si,1 1100 000003CB 7303 jnc .seg0 1101 000003CD B80060 mov ax,fat_seg+1000h 1102 000003D0 8ED8 .seg0: mov ds,ax 1103 000003D2 8B34 mov si,[si] 1104 000003D4 81FEF0FF cmp si,0FFF0h 1105 000003D8 F5 cmc 1106 000003D9 1F pop ds 1107 000003DA 58 pop ax 1108 000003DB C3 ret 1109 ; 1110 ; Debug routine 1111 ; 1112 %ifdef debug 1113 safedumpregs: 1114 cmp word [Debug_Magic],0D00Dh 1115 jnz nc_return 1116 jmp dumpregs 1117 %endif 1118 1119 rl_checkpt equ $ ; Must be <= 400h 1120 1121 ; ---------------------------------------------------------------------------- 1122 ; End of code and data that have to be in the first sector 1123 ; ---------------------------------------------------------------------------- 1124 1125 all_read: 1126 ; 1127 ; Let the user (and programmer!) know we got this far. This used to be 1128 ; in Sector 1, but makes a lot more sense here. 1129 ; 1130 000003DC BE[3E13] mov si,copyright_str 1131 000003DF E873FD call writestr 1132 ; 1133 ; Check that no moron is trying to boot Linux on a 286 or so. According 1134 ; to Intel, the way to check is to see if the high 4 bits of the FLAGS 1135 ; register are either all stuck at 1 (8086/8088) or all stuck at 0 1136 ; (286 in real mode), if not it is a 386 or higher. They didn't 1137 ; say how to check for a 186/188, so I *hope* it falls out as a 8086 1138 ; or 286 in this test. 1139 ; 1140 ; Also, provide an escape route in case it doesn't work. 1141 ; 1142 check_escapes: 1143 000003E2 B402 mov ah,02h ; Check keyboard flags 1144 000003E4 CD16 int 16h 1145 000003E6 A22165 mov [KbdFlags],al ; Save for boot prompt check 1146 000003E9 A804 test al,04h ; Ctrl->skip 386 check 1147 000003EB 7538 jnz skip_checks 1148 test_8086: 1149 000003ED 9C pushf ; Get flags 1150 000003EE 58 pop ax 1151 000003EF 25FF0F and ax,0FFFh ; Clear top 4 bits 1152 000003F2 50 push ax ; Load into FLAGS 1153 000003F3 9D popf 1154 000003F4 9C pushf ; And load back 1155 000003F5 58 pop ax 1156 000003F6 2500F0 and ax,0F000h ; Get top 4 bits 1157 000003F9 3D00F0 cmp ax,0F000h ; If set -> 8086/8088 1158 000003FC 740E je not_386 1159 test_286: 1160 000003FE 9C pushf ; Get flags 1161 000003FF 58 pop ax 1162 00000400 0D00F0 or ax,0F000h ; Set top 4 bits 1163 00000403 50 push ax 1164 00000404 9D popf 1165 00000405 9C pushf 1166 00000406 58 pop ax 1167 00000407 2500F0 and ax,0F000h ; Get top 4 bits 1168 0000040A 7509 jnz is_386 ; If not clear -> 386 1169 not_386: 1170 0000040C BE[B613] mov si,err_not386 1171 0000040F E843FD call writestr 1172 00000412 E9FAFC jmp kaboom 1173 is_386: 1174 ; Now we know it's a 386 or higher 1175 ; 1176 ; Now check that there is at least 608K of low (DOS) memory 1177 ; (608K = 9800h segments) 1178 ; 1179 00000415 CD12 int 12h 1180 00000417 3D6002 cmp ax,608 1181 0000041A 7309 jae enough_ram 1182 0000041C BE[A314] mov si,err_noram 1183 0000041F E833FD call writestr 1184 00000422 E9EAFC jmp kaboom 1185 enough_ram: 1186 skip_checks: 1187 ; 1188 ; Check if we're 386 (as opposed to 486+); if so we need to blank out 1189 ; the WBINVD instruction 1190 ; 1191 ; We check for 486 by setting EFLAGS.AC 1192 ; 1193 00000425 669C pushfd ; Save the good flags 1194 00000427 669C pushfd 1195 00000429 6658 pop eax 1196 0000042B 6689C3 mov ebx,eax 1197 0000042E 663500000400 xor eax,(1 << 18) ; AC bit 1198 00000434 6650 push eax 1199 00000436 669D popfd 1200 00000438 669C pushfd 1201 0000043A 6658 pop eax 1202 0000043C 669D popfd ; Restore the original flags 1203 0000043E 6631D8 xor eax,ebx 1204 00000441 7505 jnz is_486 1205 ; 1206 ; 386 - Looks like we better blot out the WBINVD instruction 1207 ; 1208 00000443 C606[D40D]C3 mov byte [try_wbinvd],0c3h ; Near RET 1209 is_486: 1210 1211 ; 1212 ; Initialization that does not need to go into the any of the pre-load 1213 ; areas 1214 ; 1215 00000448 E8C70A call adjust_screen 1216 ; 1217 ; Now we're all set to start with our *real* business. First load the 1218 ; configuration file (if any) and parse it. 1219 ; 1220 ; In previous versions I avoided using 32-bit registers because of a 1221 ; rumour some BIOSes clobbered the upper half of 32-bit registers at 1222 ; random. I figure, though, that if there are any of those still left 1223 ; they probably won't be trying to install Linux on them... 1224 ; 1225 ; The code is still ripe with 16-bitisms, though. Not worth the hassle 1226 ; to take'm out. In fact, we may want to put them back if we're going 1227 ; to boot ELKS at some point. 1228 ; 1229 0000044B BE[5A17] mov si,linuxauto_cmd ; Default command: "linux auto" 1230 0000044E BF[7118] mov di,default_cmd 1231 00000451 B90B00 mov cx,linuxauto_len 1232 00000454 F3A4 rep movsb 1233 1234 00000456 BF0063 mov di,KbdMap ; Default keymap 1:1 1235 00000459 30C0 xor al,al 1236 0000045B B90001 mov cx,256 1237 0000045E AA mkkeymap: stosb 1238 0000045F FEC0 inc al 1239 00000461 E2FB loop mkkeymap 1240 1241 ; 1242 ; Load configuration file 1243 ; 1244 00000463 BF[FA16] mov di,syslinux_cfg 1245 00000466 E81E0C call open 1246 00000469 0F849501 jz near no_config_file 1247 parse_config: 1248 0000046D E8B30C call getkeyword 1249 00000470 0F828B01 jc near end_config_file ; Config file loaded 1250 00000474 3D6465 cmp ax,'de' ; DEfault 1251 00000477 7442 je pc_default 1252 00000479 3D6170 cmp ax,'ap' ; APpend 1253 0000047C 744D je pc_append 1254 0000047E 3D7469 cmp ax,'ti' ; TImeout 1255 00000481 0F849500 je near pc_timeout 1256 00000485 3D7072 cmp ax,'pr' ; PRompt 1257 00000488 0F84AB00 je near pc_prompt 1258 0000048C 3D666F cmp ax,'fo' ; FOnt 1259 0000048F 0F841601 je near pc_font 1260 00000493 3D6B62 cmp ax,'kb' ; KBd 1261 00000496 0F841901 je near pc_kbd 1262 0000049A 3D6469 cmp ax,'di' ; DIsplay 1263 0000049D 0F848B00 je near pc_display 1264 000004A1 3D6C61 cmp ax,'la' ; LAbel 1265 000004A4 0F84CF00 je near pc_label 1266 000004A8 3D6B65 cmp ax,'ke' ; KErnel 1267 000004AB 7454 je pc_kernel 1268 000004AD 3D696D cmp ax,'im' ; IMplicit 1269 000004B0 0F848E00 je near pc_implicit 1270 000004B4 3C66 cmp al,'f' ; F-key 1271 000004B6 75B5 jne parse_config 1272 000004B8 E99200 jmp pc_fkey 1273 1274 000004BB BF[7118] pc_default: mov di,default_cmd ; "default" command 1275 000004BE E8730D call getline 1276 000004C1 BE[6017] mov si,auto_cmd ; add "auto"+null 1277 000004C4 B90500 mov cx,auto_len 1278 000004C7 F3A4 rep movsb 1279 000004C9 EBA2 jmp short parse_config 1280 1281 000004CB 833E[5417]00 pc_append: cmp word [VKernelCtr],byte 0 ; "append" command 1282 000004D0 7710 ja pc_append_vk 1283 000004D2 BF0062 mov di,AppendBuf 1284 000004D5 E85C0D call getline 1285 000004D8 81EF0062 sub di,AppendBuf 1286 000004DC 893E[4A17] pc_app1: mov [AppendLen],di 1287 000004E0 EB8B jmp short parse_config 1288 000004E2 BF1860 pc_append_vk: mov di,VKernelBuf+vk_append ; "append" command (vkernel) 1289 000004E5 E84C0D call getline 1290 000004E8 81EF1860 sub di,VKernelBuf+vk_append 1291 000004EC 83FF02 cmp di,byte 2 1292 000004EF 750A jne pc_app2 1293 000004F1 803E18602D cmp byte [VKernelBuf+vk_append],'-' 1294 000004F6 7503 jne pc_app2 1295 000004F8 BF0000 mov di,0 ; If "append -" -> null string 1296 000004FB 893E1660 pc_app2: mov [VKernelBuf+vk_appendlen],di 1297 000004FF EB33 jmp short parse_config_2 1298 1299 00000501 833E[5417]00 pc_kernel: cmp word [VKernelCtr],byte 0 ; "kernel" command 1300 00000506 0F8463FF je near parse_config ; ("label" section only) 1301 0000050A BF0010 mov di,trackbuf 1302 0000050D 57 push di 1303 0000050E E8230D call getline 1304 00000511 5E pop si 1305 00000512 BF0B60 mov di,VKernelBuf+vk_rname 1306 00000515 E8590D call mangle_name 1307 00000518 EB1A jmp short parse_config_2 1308 1309 0000051A E8620C pc_timeout: call getint ; "timeout" command 1310 0000051D 7215 jc parse_config_2 1311 0000051F B815D2 mov ax,0D215h ; There are approx 1.D215h 1312 00000522 F7E3 mul bx ; clock ticks per 1/10 s 1313 00000524 01D3 add bx,dx 1314 00000526 891E[4C17] mov [KbdTimeOut],bx 1315 0000052A EB08 jmp short parse_config_2 1316 1317 0000052C E88F00 pc_display: call pc_getfile ; "display" command 1318 0000052F 7403 jz parse_config_2 ; File not found? 1319 00000531 E8570A call get_msg_file ; Load and display file 1320 00000534 E936FF parse_config_2: jmp parse_config 1321 1322 00000537 E8450C pc_prompt: call getint ; "prompt" command 1323 0000053A 72F8 jc parse_config_2 1324 0000053C 891E[5617] mov [ForcePrompt],bx 1325 00000540 EBF2 jmp short parse_config_2 1326 1327 00000542 E83A0C pc_implicit: call getint ; "implicit" command 1328 00000545 72ED jc parse_config_2 1329 00000547 891E[5817] mov [AllowImplicit],bx 1330 0000054B EBE7 jmp short parse_config_2 1331 1332 0000054D 80EC31 pc_fkey: sub ah,'1' 1333 00000550 7302 jnb pc_fkey1 1334 00000552 B409 mov ah,9 ; F10 1335 00000554 31C9 pc_fkey1: xor cx,cx 1336 00000556 88E1 mov cl,ah 1337 00000558 51 push cx 1338 00000559 B80100 mov ax,1 1339 0000055C D3E0 shl ax,cl 1340 0000055E 0906[4E17] or [FKeyMap], ax ; Mark that we have this loaded 1341 00000562 BF0010 mov di,trackbuf 1342 00000565 57 push di 1343 00000566 E8CB0C call getline ; Get filename to display 1344 00000569 5E pop si 1345 0000056A 5F pop di 1346 0000056B C1E704 shl di,4 ; Multiply number by 16 1347 0000056E 81C70064 add di,FKeyName 1348 00000572 E8FC0C call mangle_name ; Mangle file name 1349 00000575 EBBD jmp short parse_config_2 1350 1351 00000577 E85700 pc_label: call commit_vk ; Commit any current vkernel 1352 0000057A BF0010 mov di,trackbuf ; Get virtual filename 1353 0000057D 57 push di 1354 0000057E E8B30C call getline 1355 00000581 5E pop si 1356 00000582 BF0060 mov di,VKernelBuf+vk_vname 1357 00000585 E8E90C call mangle_name ; Mangle virtual name 1358 00000588 FF06[5417] inc word [VKernelCtr] ; One more vkernel 1359 0000058C BE0060 mov si,VKernelBuf+vk_vname ; By default, rname == vname 1360 0000058F BF0B60 mov di,VKernelBuf+vk_rname 1361 00000592 B90B00 mov cx,11 1362 00000595 F3A4 rep movsb 1363 00000597 BE0062 mov si,AppendBuf ; Default append==global append 1364 0000059A BF1860 mov di,VKernelBuf+vk_append 1365 0000059D 8B0E[4A17] mov cx,[AppendLen] 1366 000005A1 890E1660 mov [VKernelBuf+vk_appendlen],cx 1367 000005A5 F3A4 rep movsb 1368 000005A7 EB8B jmp short parse_config_2 1369 1370 000005A9 E81200 pc_font: call pc_getfile ; "font" command 1371 000005AC 7486 jz parse_config_2 ; File not found? 1372 000005AE E87C09 call loadfont ; Load and install font 1373 000005B1 EB08 jmp short parse_config_3 1374 1375 000005B3 E80800 pc_kbd: call pc_getfile ; "kbd" command 1376 000005B6 7403 jz parse_config_3 1377 000005B8 E8B109 call loadkeys 1378 000005BB E9AFFE parse_config_3: jmp parse_config 1379 1380 ; 1381 ; pc_getfile: For command line options that take file argument, this 1382 ; routine decodes the file argument and runs it through searchdir 1383 ; 1384 000005BE BF0010 pc_getfile: mov di,trackbuf 1385 000005C1 57 push di 1386 000005C2 E86F0C call getline 1387 000005C5 5E pop si 1388 000005C6 BF2465 mov di,MNameBuf 1389 000005C9 57 push di 1390 000005CA E8A40C call mangle_name 1391 000005CD 5F pop di 1392 000005CE E9B408 jmp searchdir ; Tailcall 1393 1394 ; 1395 ; commit_vk: Store the current VKernelBuf into buffer segment 1396 ; 1397 commit_vk: 1398 000005D1 833E[5417]00 cmp word [VKernelCtr],byte 0 1399 000005D6 741F je cvk_ret ; No VKernel = return 1400 000005D8 813E[5417]8000 cmp word [VKernelCtr],max_vk ; Above limit? 1401 000005DE 7718 ja cvk_overflow 1402 000005E0 8B3E[5417] mov di,[VKernelCtr] 1403 000005E4 4F dec di 1404 000005E5 C1E709 shl di,vk_shift 1405 000005E8 BE0060 mov si,VKernelBuf 1406 000005EB B98000 mov cx,(vk_size >> 2) 1407 000005EE 06 push es 1408 000005EF 680080 push word vk_seg 1409 000005F2 07 pop es 1410 000005F3 F366A5 rep movsd ; Copy to buffer segment 1411 000005F6 07 pop es 1412 000005F7 C3 cvk_ret: ret 1413 000005F8 C706[5417]8000 cvk_overflow: mov word [VKernelCtr],max_vk ; No more than max_vk, please 1414 000005FE C3 ret 1415 1416 ; 1417 ; End of configuration file 1418 ; 1419 end_config_file: 1420 000005FF E8CFFF call commit_vk ; Commit any current vkernel 1421 no_config_file: 1422 ; 1423 ; Check whether or not we are supposed to display the boot prompt. 1424 ; 1425 check_for_key: 1426 00000602 833E[5617]00 cmp word [ForcePrompt],byte 0 ; Force prompt? 1427 00000607 7509 jnz enter_command 1428 00000609 F60621655B test byte [KbdFlags],5Bh ; Caps, Scroll, Shift, Alt 1429 0000060E 0F84B700 jz near auto_boot ; If neither, default boot 1430 1431 enter_command: 1432 00000612 BE[6813] mov si,boot_prompt 1433 00000615 E83DFB call writestr 1434 1435 00000618 BF[7017] mov di,command_line 1436 ; 1437 ; get the very first character -- we can either time 1438 ; out, or receive a character press at this time. Some dorky BIOSes stuff 1439 ; a return in the buffer on bootup, so wipe the keyboard buffer first. 1440 ; 1441 0000061B B401 clear_buffer: mov ah,1 ; Check for pending char 1442 0000061D CD16 int 16h 1443 0000061F 7406 jz get_char_time 1444 00000621 31C0 xor ax,ax ; Get char 1445 00000623 CD16 int 16h 1446 00000625 EBF4 jmp short clear_buffer 1447 00000627 8B0E[4C17] get_char_time: mov cx,[KbdTimeOut] 1448 0000062B 21C9 and cx,cx 1449 0000062D 741A jz get_char ; Timeout == 0 -> no timeout 1450 0000062F 41 inc cx ; The first loop will happen 1451 ; immediately as we don't 1452 ; know the appropriate DX value 1453 00000630 51 time_loop: push cx 1454 00000631 52 tick_loop: push dx 1455 00000632 B401 mov ah,1 ; Check for pending keystroke 1456 00000634 CD16 int 16h 1457 00000636 750F jnz get_char_pop 1458 00000638 31C0 xor ax,ax 1459 0000063A CD1A int 1Ah ; Get time "of day" 1460 0000063C 58 pop ax 1461 0000063D 39C2 cmp dx,ax ; Has the timer advanced? 1462 0000063F 74F0 je tick_loop 1463 00000641 59 pop cx 1464 00000642 E2EC loop time_loop ; If so, decrement counter 1465 00000644 E99000 jmp command_done ; Timeout! 1466 00000647 6658 get_char_pop: pop eax ; Clear the stack 1467 00000649 31C0 get_char: xor ax,ax ; Get char 1468 0000064B CD16 int 16h 1469 0000064D 20C0 and al,al 1470 0000064F 7433 jz func_key 1471 00000651 BB0063 mov bx,KbdMap ; Keyboard map translation 1472 00000654 D7 xlatb 1473 00000655 3C20 cmp al,' ' ; ASCII? 1474 00000657 7214 jb not_ascii 1475 00000659 7706 ja enter_char 1476 0000065B 81FF[7017] cmp di,command_line ; Space must not be first 1477 0000065F 74E8 je get_char 1478 00000661 81FF[6F18] enter_char: cmp di,max_cmd_len+command_line ; Check there's space 1479 00000665 73E2 jnb get_char 1480 00000667 AA stosb ; Save it 1481 00000668 E89D08 call writechr ; Echo to screen 1482 0000066B EBDC jmp short get_char 1483 0000066D 3C0D not_ascii: cmp al,0Dh ; Enter 1484 0000066F 7466 je command_done 1485 00000671 3C08 cmp al,08h ; Backspace 1486 00000673 75D4 jne get_char 1487 00000675 81FF[7017] cmp di,command_line ; Make sure there is anything 1488 00000679 74CE je get_char ; to erase 1489 0000067B 4F dec di ; Unstore one character 1490 0000067C BE[6F13] mov si,wipe_char ; and erase it from the screen 1491 0000067F E8D3FA call writestr 1492 00000682 EBC5 jmp short get_char 1493 func_key: 1494 00000684 57 push di 1495 00000685 80FC44 cmp ah,68 ; F10 1496 00000688 77BF ja get_char 1497 0000068A 80EC3B sub ah,59 ; F1 1498 0000068D 72BA jb get_char 1499 0000068F 88E1 mov cl,ah 1500 00000691 C1E804 shr ax,4 ; Convert to x16 1501 00000694 BB0100 mov bx,1 1502 00000697 D3E3 shl bx,cl 1503 00000699 231E[4E17] and bx,[FKeyMap] 1504 0000069D 74AA jz get_char ; Undefined F-key 1505 0000069F 89C7 mov di,ax 1506 000006A1 81C70064 add di,FKeyName 1507 000006A5 E8DD07 call searchdir 1508 000006A8 7405 jz fk_nofile 1509 000006AA E8DE08 call get_msg_file 1510 000006AD EB06 jmp short fk_wrcmd 1511 fk_nofile: 1512 000006AF BE[F716] mov si,crlf_msg 1513 000006B2 E8A0FA call writestr 1514 fk_wrcmd: 1515 000006B5 BE[6813] mov si,boot_prompt 1516 000006B8 E89AFA call writestr 1517 000006BB 5F pop di ; Command line write pointer 1518 000006BC 57 push di 1519 000006BD C60500 mov byte [di],0 ; Null-terminate command line 1520 000006C0 BE[7017] mov si,command_line 1521 000006C3 E88FFA call writestr ; Write command line so far 1522 000006C6 5F pop di 1523 000006C7 EB80 jmp short get_char 1524 auto_boot: 1525 000006C9 BE[7118] mov si,default_cmd 1526 000006CC BF[7017] mov di,command_line 1527 000006CF B94000 mov cx,(max_cmd_len+4) >> 2 1528 000006D2 F366A5 rep movsd 1529 000006D5 EB0F jmp short load_kernel 1530 command_done: 1531 000006D7 BE[F716] mov si,crlf_msg 1532 000006DA E878FA call writestr 1533 000006DD 81FF[7017] cmp di,command_line ; Did we just hit return? 1534 000006E1 74E6 je auto_boot 1535 000006E3 30C0 xor al,al ; Store a final null 1536 000006E5 AA stosb 1537 1538 load_kernel: ; Load the kernel now 1539 ; 1540 ; First we need to mangle the kernel name the way DOS would... 1541 ; 1542 000006E6 BE[7017] mov si,command_line 1543 000006E9 BFD064 mov di,KernelName 1544 000006EC 56 push si 1545 000006ED 57 push di 1546 000006EE E8800B call mangle_name 1547 000006F1 5F pop di 1548 000006F2 5E pop si 1549 ; 1550 ; Fast-forward to first option (we start over from the beginning, since 1551 ; mangle_name doesn't necessarily return a consistent ending state.) 1552 ; 1553 000006F3 AC clin_non_wsp: lodsb 1554 000006F4 3C20 cmp al,' ' 1555 000006F6 77FB ja clin_non_wsp 1556 000006F8 20C0 clin_is_wsp: and al,al 1557 000006FA 7405 jz clin_opt_ptr 1558 000006FC AC lodsb 1559 000006FD 3C20 cmp al,' ' 1560 000006FF 76F7 jbe clin_is_wsp 1561 00000701 4E clin_opt_ptr: dec si ; Point to first nonblank 1562 00000702 89360C65 mov [CmdOptPtr],si ; Save ptr to first option 1563 ; 1564 ; Now check if it is a "virtual kernel" 1565 ; 1566 00000706 8B0E[5417] mov cx,[VKernelCtr] 1567 0000070A 1E push ds 1568 0000070B 680080 push word vk_seg 1569 0000070E 1F pop ds 1570 0000070F 83F900 cmp cx,byte 0 1571 00000712 7413 je not_vk 1572 00000714 31F6 xor si,si ; Point to first vkernel 1573 00000716 60 vk_check: pusha 1574 00000717 B90B00 mov cx,11 1575 0000071A F3A6 repe cmpsb ; Is this it? 1576 0000071C 0F847700 je near vk_found 1577 00000720 61 popa 1578 00000721 81C60002 add si,vk_size 1579 00000725 E2EF loop vk_check 1580 00000727 1F not_vk: pop ds 1581 ; 1582 ; Not a "virtual kernel" - check that's OK and construct the command line 1583 ; 1584 00000728 833E[5817]00 cmp word [AllowImplicit],byte 0 1585 0000072D 745D je bad_implicit 1586 0000072F 06 push es 1587 00000730 56 push si 1588 00000731 57 push di 1589 00000732 BF0090 mov di,real_mode_seg 1590 00000735 8EC7 mov es,di 1591 00000737 BE0062 mov si,AppendBuf 1592 0000073A BF0080 mov di,cmd_line_here 1593 0000073D 8B0E[4A17] mov cx,[AppendLen] 1594 00000741 F3A4 rep movsb 1595 00000743 893E[5017] mov [CmdLinePtr],di 1596 00000747 5F pop di 1597 00000748 5E pop si 1598 00000749 07 pop es 1599 0000074A BB1000 mov bx,exten_count << 2 ; Alternates to try 1600 ; 1601 ; Find the kernel on disk 1602 ; 1603 0000074D C606DB6400 get_kernel: mov byte [KernelName+11],0 ; Zero-terminate filename/extension 1604 00000752 66A1D864 mov eax,[KernelName+8] ; Save initial extension 1605 00000756 66A3[3617] mov [OrigKernelExt],eax 1606 0000075A 53 .search_loop: push bx 1607 0000075B BFD064 mov di,KernelName ; Search on disk 1608 0000075E E82407 call searchdir 1609 00000761 5B pop bx 1610 00000762 756C jnz kernel_good 1611 00000764 668B87[3617] mov eax,[exten_table+bx] ; Try a different extension 1612 00000769 66A3D864 mov [KernelName+8],eax 1613 0000076D 83EB04 sub bx,byte 4 1614 00000770 73E8 jnb .search_loop 1615 bad_kernel: 1616 00000772 BED064 mov si,KernelName 1617 00000775 BF3A65 mov di,KernelCName 1618 00000778 57 push di 1619 00000779 E8510B call unmangle_name ; Get human form 1620 0000077C BE[7313] mov si,err_notfound ; Complain about missing kernel 1621 0000077F E8D3F9 call writestr 1622 00000782 5E pop si ; KernelCName 1623 00000783 E8CFF9 call writestr 1624 00000786 BE[F716] mov si,crlf_msg 1625 00000789 E9E406 jmp abort_load ; Ask user for clue 1626 ; 1627 ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0" 1628 ; 1629 0000078C BED064 bad_implicit: mov si,KernelName ; For the error message 1630 0000078F BF3A65 mov di,KernelCName 1631 00000792 E8380B call unmangle_name 1632 00000795 EBDB jmp short bad_kernel 1633 ; 1634 ; vk_found: We *are* using a "virtual kernel" 1635 ; 1636 00000797 61 vk_found: popa 1637 00000798 57 push di 1638 00000799 BF0060 mov di,VKernelBuf 1639 0000079C B98000 mov cx,vk_size >> 2 1640 0000079F F366A5 rep movsd 1641 000007A2 06 push es ; Restore old DS 1642 000007A3 1F pop ds 1643 000007A4 06 push es 1644 000007A5 680090 push word real_mode_seg 1645 000007A8 07 pop es 1646 000007A9 BF0080 mov di,cmd_line_here 1647 000007AC BE1860 mov si,VKernelBuf+vk_append 1648 000007AF 8B0E1660 mov cx,[VKernelBuf+vk_appendlen] 1649 000007B3 F3A4 rep movsb 1650 000007B5 893E[5017] mov [CmdLinePtr],di ; Where to add rest of cmd 1651 000007B9 07 pop es 1652 000007BA 5F pop di ; DI -> KernelName 1653 000007BB 57 push di 1654 000007BC BE0B60 mov si,VKernelBuf+vk_rname 1655 000007BF B90B00 mov cx,11 ; We need ECX == CX later 1656 000007C2 F3A4 rep movsb 1657 000007C4 5F pop di 1658 000007C5 31DB xor bx,bx ; Try only one version 1659 000007C7 E983FF jmp get_kernel 1660 ; 1661 ; kernel_corrupt: Called if the kernel file does not seem healthy 1662 ; 1663 000007CA BE[9113] kernel_corrupt: mov si,err_notkernel 1664 000007CD E9A006 jmp abort_load 1665 ; 1666 ; This is it! We have a name (and location on the disk)... let's load 1667 ; that sucker!! First we have to decide what kind of file this is; base 1668 ; that decision on the file extension. The following extensions are 1669 ; recognized: 1670 ; 1671 ; .COM - COMBOOT image 1672 ; .CBT - COMBOOT image 1673 ; .BS - Boot sector 1674 ; .BSS - Boot sector, but transfer over DOS superblock 1675 ; 1676 ; Anything else is assumed to be a Linux kernel. 1677 ; 1678 kernel_good: 1679 000007D0 60 pusha 1680 000007D1 BED064 mov si,KernelName 1681 000007D4 BF3A65 mov di,KernelCName 1682 000007D7 E8F30A call unmangle_name ; Get human form 1683 000007DA 81EF3A65 sub di,KernelCName 1684 000007DE 893E0E65 mov [KernelCNameLen],di 1685 000007E2 61 popa 1686 1687 000007E3 668B0ED864 mov ecx,[KernelName+8] ; Get (mangled) extension 1688 000007E8 6681F9434F4D00 cmp ecx,'COM' 1689 000007EF 0F84A703 je near is_comboot_image 1690 000007F3 6681F943425400 cmp ecx,'CBT' 1691 000007FA 0F849C03 je near is_comboot_image 1692 000007FE 6681F942532000 cmp ecx,'BS ' 1693 00000805 0F844304 je near is_bootsector 1694 00000809 6681F942535300 cmp ecx,'BSS' 1695 00000810 0F843D04 je near is_bss_sector 1696 ; Otherwise Linux kernel 1697 ; 1698 ; A Linux kernel consists of three parts: boot sector, setup code, and 1699 ; kernel code. The boot sector is never executed when using an external 1700 ; booting utility, but it contains some status bytes that are necessary. 1701 ; The boot sector and setup code together form exactly 5 sectors that 1702 ; should be loaded at 9000:0. The subsequent code should be loaded 1703 ; at 1000:0. For simplicity, we load the whole thing at 0F60:0, and 1704 ; copy the latter stuff afterwards. 1705 ; 1706 ; NOTE: In the previous code I have avoided making any assumptions regarding 1707 ; the size of a sector, in case this concept ever gets extended to other 1708 ; media like CD-ROM (not that a CD-ROM would be bloody likely to use a FAT 1709 ; filesystem, of course). However, a "sector" when it comes to Linux booting 1710 ; stuff means 512 bytes *no matter what*, so here I am using that piece 1711 ; of knowledge. 1712 ; 1713 ; First check that our kernel is at least 64K and less than 8M (if it is 1714 ; more than 8M, we need to change the logic for loading it anyway...) 1715 ; 1716 is_linux_kernel: 1717 00000814 81FA8000 cmp dx,80h ; 8 megs 1718 00000818 77B0 ja kernel_corrupt 1719 0000081A 21D2 and dx,dx 1720 0000081C 74AC jz kernel_corrupt 1721 0000081E 50 kernel_sane: push ax 1722 0000081F 52 push dx 1723 00000820 56 push si 1724 00000821 BE[E216] mov si,loading_msg 1725 00000824 E88604 call cwritestr 1726 ; 1727 ; Now start transferring the kernel 1728 ; 1729 00000827 680090 push word real_mode_seg 1730 0000082A 07 pop es 1731 1732 0000082B 50 push ax 1733 0000082C 52 push dx 1734 0000082D F736F264 div word [ClustSize] ; # of clusters total 1735 00000831 21D2 and dx,dx ; Round up 1736 00000833 0F95C2 setnz dl 1737 00000836 0FB6D2 movzx dx,dl 1738 00000839 01D0 add ax,dx 1739 0000083B A30065 mov [KernelClust],ax 1740 0000083E 5A pop dx 1741 0000083F 58 pop ax 1742 00000840 A3CC64 mov [KernelSize],ax 1743 00000843 8916CE64 mov [KernelSize+2],dx 1744 ; 1745 ; Now, if we transfer these straight, we'll hit 64K boundaries. Hence we 1746 ; have to see if we're loading more than 64K, and if so, load it step by 1747 ; step. 1748 ; 1749 00000847 BA0100 mov dx,1 ; 10000h 1750 0000084A 31C0 xor ax,ax 1751 0000084C F736F264 div word [ClustSize] 1752 00000850 A30465 mov [ClustPerMoby],ax ; Clusters/64K 1753 ; 1754 ; Start by loading the bootsector/setup code, to see if we need to 1755 ; do something funky. It should fit in the first 32K (loading 64K won't 1756 ; work since we might have funny stuff up near the end of memory). 1757 ; If we have larger than 32K clusters, yes, we're hosed. 1758 ; 1759 00000853 E80006 call abort_check ; Check for abort key 1760 00000856 8B0E0465 mov cx,[ClustPerMoby] 1761 0000085A D1E9 shr cx,1 ; Half a moby 1762 0000085C 290E0065 sub [KernelClust],cx 1763 00000860 31DB xor bx,bx 1764 00000862 5E pop si ; Cluster pointer on stack 1765 00000863 E8C5FA call getfssec 1766 00000866 0F8260FF jc near kernel_corrupt ; Failure in first 32K 1767 0000086A 26813EFE0155AA cmp word [es:bs_bootsign],0AA55h 1768 00000871 0F8555FF jne near kernel_corrupt ; Boot sec signature missing 1769 ; 1770 ; Get the BIOS' idea of what the size of high memory is 1771 ; 1772 00000875 56 push si ; Save our cluster pointer! 1773 1774 00000876 B801E8 mov ax,0e801h ; Query high memory (semi-recent) 1775 00000879 CD15 int 15h 1776 0000087B 7215 jc no_e801 1777 0000087D 3D003C cmp ax,3c00h 1778 00000880 7710 ja no_e801 ; > 3C00h something's wrong with this call 1779 00000882 721A jb e801_hole ; If memory hole we can only use low part 1780 1781 00000884 89D8 mov ax,bx 1782 00000886 66C1E010 shl eax,16 ; 64K chunks 1783 0000088A 660500000001 add eax,(16 << 20) ; Add first 16M 1784 00000890 EB1C jmp short got_highmem 1785 1786 no_e801: 1787 00000892 B488 mov ah,88h ; Query high memory (oldest) 1788 00000894 CD15 int 15h 1789 00000896 3D0038 cmp ax,14*1024 ; Don't trust memory >15M 1790 00000899 7603 jna e801_hole 1791 0000089B B80038 mov ax,14*1024 1792 e801_hole: 1793 0000089E 6625FFFF0000 and eax,0ffffh 1794 000008A4 66C1E00A shl eax,10 ; Convert from kilobytes 1795 000008A8 660500001000 add eax,(1 << 20) ; First megabyte 1796 got_highmem: 1797 000008AE 66A3C864 mov [HighMemSize],eax 1798 ; 1799 ; Construct the command line (append options have already been copied) 1800 ; 1801 000008B2 26C70620003FA3 mov word [es:kern_cmd_magic],0A33Fh ; Command line magic no 1802 000008B9 26C70622000080 mov word [es:kern_cmd_offset],cmd_line_here 1803 000008C0 8B3E[5017] mov di,[CmdLinePtr] 1804 000008C4 BE[6517] mov si,boot_image ; BOOT_IMAGE= 1805 000008C7 B90B00 mov cx,boot_image_len 1806 000008CA F3A4 rep movsb 1807 000008CC BE3A65 mov si,KernelCName ; Unmangled kernel name 1808 000008CF 8B0E0E65 mov cx,[KernelCNameLen] 1809 000008D3 F3A4 rep movsb 1810 000008D5 B020 mov al,' ' ; Space 1811 000008D7 AA stosb 1812 000008D8 8B360C65 mov si,[CmdOptPtr] ; Options from user input 1813 000008DC B98100 mov cx,(kern_cmd_len+3) >> 2 1814 000008DF F366A5 rep movsd 1815 ; 1816 %ifdef debug 1817 push ds ; DEBUG DEBUG DEBUG 1818 push es 1819 pop ds 1820 mov si,offset cmd_line_here 1821 call cwritestr 1822 pop ds 1823 mov si,offset crlf_msg 1824 call cwritestr 1825 %endif 1826 ; 1827 ; Scan through the command line for anything that looks like we might be 1828 ; interested in. The original version of this code automatically assumed 1829 ; the first option was BOOT_IMAGE=, but that is no longer certain. 1830 ; 1831 000008E2 BE0080 mov si,cmd_line_here 1832 000008E5 C606[5217]00 mov byte [initrd_flag],0 1833 000008EA 06 push es ; Set DS <- real_mode_seg 1834 000008EB 1F pop ds 1835 000008EC AC get_next_opt: lodsb 1836 000008ED 20C0 and al,al 1837 000008EF 0F848A00 jz near cmdline_end 1838 000008F3 3C20 cmp al,' ' 1839 000008F5 76F5 jbe get_next_opt 1840 000008F7 4E dec si 1841 000008F8 668B04 mov eax,[si] 1842 000008FB 663D7667613D cmp eax,'vga=' 1843 00000901 7432 je is_vga_cmd 1844 00000903 663D6D656D3D cmp eax,'mem=' 1845 00000909 7462 je is_mem_cmd 1846 0000090B 06 push es ; Save ES -> real_mode_seg 1847 0000090C 16 push ss 1848 0000090D 07 pop es ; Set ES <- normal DS 1849 0000090E BF[0517] mov di,initrd_cmd 1850 00000911 B90700 mov cx,initrd_cmd_len 1851 00000914 F3A6 repe cmpsb 1852 00000916 7514 jne not_initrd 1853 00000918 BF2F65 mov di,InitRD 1854 0000091B 56 push si ; mangle_dir mangles si 1855 0000091C E85209 call mangle_name ; Mangle ramdisk name 1856 0000091F 5E pop si 1857 00000920 26803E2F6520 cmp byte [es:InitRD],' ' ; Null filename? 1858 00000926 260F9706[5217] seta byte [es:initrd_flag] ; Set flag if not 1859 0000092C 07 not_initrd: pop es ; Restore ES -> real_mode_seg 1860 0000092D AC skip_this_opt: lodsb ; Load from command line 1861 0000092E 3C20 cmp al,' ' 1862 00000930 77FB ja skip_this_opt 1863 00000932 4E dec si 1864 00000933 EBB7 jmp short get_next_opt 1865 is_vga_cmd: 1866 00000935 83C604 add si,byte 4 1867 00000938 668B04 mov eax,[si] 1868 0000093B BBFFFF mov bx,-1 1869 0000093E 663D6E6F726D cmp eax, 'norm' ; vga=normal 1870 00000944 7421 je vc0 1871 00000946 6625FFFFFF00 and eax,0ffffffh ; 3 bytes 1872 0000094C BBFEFF mov bx,-2 1873 0000094F 663D65787400 cmp eax, 'ext' ; vga=ext 1874 00000955 7410 je vc0 1875 00000957 BBFDFF mov bx,-3 1876 0000095A 663D61736B00 cmp eax, 'ask' ; vga=ask 1877 00000960 7405 je vc0 1878 00000962 E83808 call parseint ; vga= 1879 00000965 72C6 jc skip_this_opt ; Not an integer 1880 00000967 891EFA01 vc0: mov [bs_vidmode],bx ; Set video mode 1881 0000096B EBC0 jmp short skip_this_opt 1882 is_mem_cmd: 1883 0000096D 83C604 add si,byte 4 1884 00000970 E82A08 call parseint 1885 00000973 72B8 jc skip_this_opt ; Not an integer 1886 00000975 3666891EC864 mov [ss:HighMemSize],ebx 1887 0000097B EBB0 jmp short skip_this_opt 1888 cmdline_end: 1889 0000097D 16 push ss ; Restore standard DS 1890 0000097E 1F pop ds 1891 ; 1892 ; Now check if we have a large kernel, which needs to be loaded high 1893 ; 1894 0000097F 2666813E0202486472- cmp dword [es:su_header],HEADER_ID ; New setup code ID 1895 00000988 53 1896 00000989 0F85F201 jne near old_kernel ; Old kernel, load low 1897 0000098D 26813E06020002 cmp word [es:su_version],0200h ; Setup code version 2.0 1898 00000994 0F82E701 jb near old_kernel ; Old kernel, load low 1899 00000998 26813E06020102 cmp word [es:su_version],0201h ; Version 2.01+? 1900 0000099F 720D jb new_kernel ; If 2.00, skip this step 1901 000009A1 26C7062402F47F mov word [es:su_heapend],linux_stack ; Set up the heap 1902 000009A8 26800E110280 or byte [es:su_loadflags],80h ; Let the kernel know we care 1903 ; 1904 ; We definitely have a new-style kernel. Let the kernel know who we are, 1905 ; and that we are clueful 1906 ; 1907 new_kernel: 1908 000009AE 26C606100231 mov byte [es:su_loader],syslinux_id ; Show some ID 1909 000009B4 260FB606F101 movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors 1910 000009BA A31465 mov [SetupSecs],ax 1911 ; 1912 ; Now see if we have an initial RAMdisk; if so, do requisite computation 1913 ; 1914 000009BD F606[5217]01 test byte [initrd_flag],1 1915 000009C2 7478 jz nk_noinitrd 1916 000009C4 06 push es ; ES->real_mode_seg 1917 000009C5 1E push ds 1918 000009C6 07 pop es ; We need ES==DS 1919 000009C7 BE2F65 mov si,InitRD 1920 000009CA BF4765 mov di,InitRDCName 1921 000009CD E8FD08 call unmangle_name ; Create human-readable name 1922 000009D0 81EF4765 sub di,InitRDCName 1923 000009D4 893E1065 mov [InitRDCNameLen],di 1924 000009D8 BF2F65 mov di,InitRD 1925 000009DB E8A704 call searchdir ; Look for it in directory 1926 000009DE 07 pop es 1927 000009DF 7443 jz initrd_notthere 1928 000009E1 8936[5217] mov [initrd_ptr],si ; Save cluster pointer 1929 000009E5 26A31C02 mov [es:su_ramdisklen1],ax ; Ram disk length 1930 000009E9 2689161E02 mov [es:su_ramdisklen2],dx 1931 000009EE F736F264 div word [ClustSize] 1932 000009F2 21D2 and dx,dx ; Round up 1933 000009F4 0F95C2 setnz dl 1934 000009F7 0FB6D2 movzx dx,dl 1935 000009FA 01D0 add ax,dx 1936 000009FC A30265 mov [InitRDClust],ax ; Ramdisk clusters 1937 000009FF 668B16C864 mov edx,[HighMemSize] ; End of memory 1938 00000A04 66B80000003F mov eax,HIGHMEM_MAX ; Limit imposed by kernel 1939 00000A0A 6639C2 cmp edx,eax 1940 00000A0D 7603 jna memsize_ok 1941 00000A0F 6689C2 mov edx,eax ; Adjust to fit inside limit 1942 memsize_ok: 1943 00000A12 26662B161C02 sub edx,[es:su_ramdisklen] ; Subtract size of ramdisk 1944 00000A18 31D2 xor dx,dx ; Round down to 64K boundary 1945 00000A1A 668916C064 mov [InitRDat],edx ; Load address 1946 00000A1F E8B503 call loadinitrd ; Load initial ramdisk 1947 00000A22 EB18 jmp short initrd_end 1948 1949 initrd_notthere: 1950 00000A24 BE[C615] mov si,err_noinitrd 1951 00000A27 E82BF7 call writestr 1952 00000A2A BE4765 mov si,InitRDCName 1953 00000A2D E825F7 call writestr 1954 00000A30 BE[F716] mov si,crlf_msg 1955 00000A33 E93A04 jmp abort_load 1956 1957 00000A36 BE[E715] no_high_mem: mov si,err_nohighmem ; Error routine 1958 00000A39 E93404 jmp abort_load 1959 ; 1960 ; About to load the kernel. This is a modern kernel, so use the boot flags 1961 ; we were provided. 1962 ; 1963 nk_noinitrd: 1964 initrd_end: 1965 00000A3C 26A01102 mov al,[es:su_loadflags] 1966 00000A40 A22265 mov [LoadFlags],al 1967 ; 1968 ; Load the kernel. We always load it at 100000h even if we're supposed to 1969 ; load it "low"; for a "low" load we copy it down to low memory right before 1970 ; jumping to it. 1971 ; 1972 read_kernel: 1973 00000A43 BE3A65 mov si,KernelCName ; Print kernel name part of 1974 00000A46 E86402 call cwritestr ; "Loading" message 1975 00000A49 BE[EB16] mov si,dotdot_msg ; Print dots 1976 00000A4C E85E02 call cwritestr 1977 1978 00000A4F 66A1C864 mov eax,[HighMemSize] 1979 00000A53 662D00001000 sub eax,100000h ; Load address 1980 00000A59 663B06CC64 cmp eax,[KernelSize] 1981 00000A5E 72D6 jb no_high_mem ; Not enough high memory 1982 ; 1983 ; Move the stuff beyond the setup code to high memory at 100000h 1984 ; 1985 00000A60 660FB7361465 movzx esi,word [SetupSecs] ; Setup sectors 1986 00000A66 6646 inc esi ; plus 1 boot sector 1987 00000A68 66C1E609 shl esi,9 ; Convert to bytes 1988 00000A6C 66B900801000 mov ecx,108000h ; 108000h = 1M + 32K 1989 00000A72 6629F1 sub ecx,esi ; Adjust pointer to 2nd block 1990 00000A75 66890EC464 mov [HiLoadAddr],ecx 1991 00000A7A 6681E900001000 sub ecx,100000h ; Turn into a counter 1992 00000A81 66C1E902 shr ecx,2 ; Convert to dwords 1993 00000A85 6681C600000900 add esi,90000h ; Pointer to source 1994 00000A8C 66BF00001000 mov edi,100000h ; Copy to address 100000h 1995 00000A92 E84B02 call bcopy ; Transfer to high memory 1996 ; 1997 00000A95 680070 push word xfer_buf_seg ; Segment 7000h is xfer buffer 1998 00000A98 07 pop es 1999 high_load_loop: 2000 00000A99 BE[EC16] mov si,dot_msg ; Progress report 2001 00000A9C E80E02 call cwritestr 2002 00000A9F E8B403 call abort_check 2003 00000AA2 8B0E0065 mov cx,[KernelClust] 2004 00000AA6 3B0E0465 cmp cx,[ClustPerMoby] 2005 00000AAA 7604 jna high_last_moby 2006 00000AAC 8B0E0465 mov cx,[ClustPerMoby] 2007 high_last_moby: 2008 00000AB0 290E0065 sub [KernelClust],cx 2009 00000AB4 31DB xor bx,bx ; Load at offset 0 2010 00000AB6 5E pop si ; Restore cluster pointer 2011 00000AB7 E871F8 call getfssec 2012 00000ABA 56 push si ; Save cluster pointer 2013 00000ABB 9C pushf ; Save EOF 2014 00000ABC 31DB xor bx,bx 2015 00000ABE 66BE00000700 mov esi,(xfer_buf_seg << 4) 2016 00000AC4 668B3EC464 mov edi,[HiLoadAddr] ; Destination address 2017 00000AC9 66B900400000 mov ecx,4000h ; Cheating - transfer 64K 2018 00000ACF E80E02 call bcopy ; Transfer to high memory 2019 00000AD2 66893EC464 mov [HiLoadAddr],edi ; Point to next target area 2020 00000AD7 9D popf ; Restore EOF 2021 00000AD8 7207 jc high_load_done ; If EOF we are done 2022 00000ADA 833E006500 cmp word [KernelClust],byte 0 ; Are we done? 2023 00000ADF 75B8 jne high_load_loop ; Apparently not 2024 high_load_done: 2025 00000AE1 5E pop si ; No longer needed 2026 00000AE2 B80090 mov ax,real_mode_seg ; Set to real mode seg 2027 00000AE5 8EC0 mov es,ax 2028 2029 00000AE7 BE[EC16] mov si,dot_msg 2030 00000AEA E8C001 call cwritestr 2031 ; 2032 ; Abandon hope, ye that enter here! We do no longer permit aborts. 2033 ; 2034 00000AED E86603 call abort_check ; Last chance!! 2035 2036 ; 2037 ; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4 2038 ; setup sectors, but the boot protocol had not yet been defined. They 2039 ; rely on a signature to figure out if they need to copy stuff from 2040 ; the "protected mode" kernel area. Unfortunately, we used that area 2041 ; as a transfer buffer, so it's going to find the signature there. 2042 ; Hence, zero the low 32K beyond the setup area. 2043 ; 2044 00000AF0 8B3E1465 mov di,[SetupSecs] 2045 00000AF4 47 inc di ; Setup + boot sector 2046 00000AF5 B94000 mov cx,32768/512 ; Sectors/32K 2047 00000AF8 29F9 sub cx,di ; Remaining sectors 2048 00000AFA C1E709 shl di,9 ; Sectors -> bytes 2049 00000AFD C1E107 shl cx,7 ; Sectors -> dwords 2050 00000B00 6631C0 xor eax,eax 2051 00000B03 F366AB rep stosd ; Clear region 2052 ; 2053 ; Now, if we were supposed to load "low", copy the kernel down to 10000h 2054 ; 2055 00000B06 F606226501 test byte [LoadFlags],LOAD_HIGH 2056 00000B0B 751F jnz in_proper_place ; If high load, we're done 2057 2058 00000B0D 668B0ECC64 mov ecx,[KernelSize] 2059 00000B12 6681C103000000 add ecx,3 ; Round upwards 2060 00000B19 66C1E902 shr ecx,2 ; Bytes -> dwords 2061 00000B1D 66BE00001000 mov esi,100000h 2062 00000B23 66BF00000100 mov edi,10000h 2063 00000B29 E8B401 call bcopy 2064 in_proper_place: 2065 ; 2066 ; If the default root device is set to FLOPPY (0000h), change to 2067 ; /dev/fd0 (0200h) 2068 ; 2069 00000B2C 26833EFC0100 cmp word [es:bs_rootdev],byte 0 2070 00000B32 7507 jne root_not_floppy 2071 00000B34 26C706FC010002 mov word [es:bs_rootdev],0200h 2072 root_not_floppy: 2073 ; 2074 ; Copy the disk table to high memory, then re-initialize the floppy 2075 ; controller 2076 ; 2077 00000B3B 1E push ds 2078 00000B3C C5367800 lds si,[fdctab] 2079 00000B40 BFF47F mov di,linux_fdctab 2080 00000B43 B90300 mov cx,3 ; 12 bytes 2081 00000B46 57 push di 2082 00000B47 F366A5 rep movsd 2083 00000B4A 5F pop di 2084 00000B4B FA cli 2085 00000B4C 893E7800 mov [fdctab1],di ; Save new floppy tab pos 2086 00000B50 8C067A00 mov [fdctab2],es 2087 00000B54 FB sti 2088 00000B55 31C0 xor ax,ax 2089 00000B57 31D2 xor dx,dx 2090 00000B59 CD13 int 13h 2091 00000B5B 1F pop ds 2092 ; 2093 ; Linux wants the floppy motor shut off before starting the kernel, 2094 ; at least bootsect.S seems to imply so 2095 ; 2096 kill_motor: 2097 00000B5C BAF203 mov dx,03F2h 2098 00000B5F 30C0 xor al,al 2099 00000B61 EE out dx,al 2100 ; 2101 ; Now we're as close to be done as we can be and still use our normal 2102 ; routines, print a CRLF to end the row of dots 2103 ; 2104 00000B62 BE[F716] mov si,crlf_msg 2105 00000B65 E8EDF5 call writestr 2106 ; 2107 ; If we're debugging, wait for a keypress so we can read any debug messages 2108 ; 2109 %ifdef debug 2110 xor ax,ax 2111 int 16h 2112 %endif 2113 ; 2114 ; Set up segment registers and the Linux real-mode stack 2115 ; 2116 00000B68 B80090 mov ax,real_mode_seg 2117 00000B6B 8ED8 mov ds,ax 2118 00000B6D 8EC0 mov es,ax 2119 00000B6F 8EE0 mov fs,ax 2120 00000B71 8EE8 mov gs,ax 2121 00000B73 FA cli 2122 00000B74 8ED0 mov ss,ax 2123 00000B76 BCF47F mov sp,linux_stack 2124 00000B79 FB sti 2125 ; 2126 ; We're done... now RUN THAT KERNEL!!!! 2127 ; 2128 00000B7A EA00002090 jmp setup_seg:setup_entry 2129 ; 2130 ; Load an older kernel. Older kernels always have 4 setup sectors, can't have 2131 ; initrd, and are always loaded low. 2132 ; 2133 old_kernel: 2134 00000B7F F606[5217]01 test byte [initrd_flag],1 ; Old kernel can't have initrd 2135 00000B84 7406 jz load_old_kernel 2136 00000B86 BE[3216] mov si,err_oldkernel 2137 00000B89 E9E402 jmp abort_load 2138 load_old_kernel: 2139 00000B8C C70614650400 mov word [SetupSecs],4 ; Always 4 setup sectors 2140 00000B92 C606226500 mov byte [LoadFlags],0 ; Always low 2141 00000B97 E9A9FE jmp read_kernel 2142 2143 ; 2144 ; Load a COMBOOT image. A COMBOOT image is basically a DOS .COM file, 2145 ; except that it may, of course, not contain any DOS system calls. We 2146 ; do, however, allow the execution of INT 20h to return to SYSLINUX. 2147 ; 2148 is_comboot_image: 2149 00000B9A 21D2 and dx,dx 2150 00000B9C 7575 jnz comboot_too_large 2151 00000B9E 3D00FF cmp ax,0ff00h ; Max size in bytes 2152 00000BA1 7370 jae comboot_too_large 2153 2154 ; 2155 ; Set up the DOS vectors in the IVT (INT 20h-3fh) 2156 ; 2157 00000BA3 66C7068000- mov dword [4*0x20],comboot_return ; INT 20h vector 2158 00000BA8 [1C0C0000] 2159 00000BAC 66B8[2E0C0000] mov eax,comboot_bogus 2160 00000BB2 BF8400 mov di,4*0x21 2161 00000BB5 B91F00 mov cx,31 ; All remaining DOS vectors 2162 00000BB8 F366AB rep stosd 2163 2164 00000BBB B90010 mov cx,comboot_seg 2165 00000BBE 8EC1 mov es,cx 2166 2167 00000BC0 BB0001 mov bx,100h ; Load at :0100h 2168 2169 00000BC3 8B0E0465 mov cx,[ClustPerMoby] ; Absolute maximum # of clusters 2170 00000BC7 E861F7 call getfssec 2171 2172 00000BCA 31FF xor di,di 2173 00000BCC B94000 mov cx,64 ; 256 bytes (size of PSP) 2174 00000BCF 6631C0 xor eax,eax ; Clear PSP 2175 00000BD2 F366AB rep stosd 2176 2177 00000BD5 26C7060000CD20 mov word [es:0], 020CDh ; INT 20h instruction 2178 ; First non-free paragraph 2179 00000BDC 26C70602000020 mov word [es:02h], comboot_seg+1000h 2180 2181 ; Copy the command line from high memory 2182 00000BE3 B97D00 mov cx,125 ; Max cmdline len (minus space and CR) 2183 00000BE6 8B360C65 mov si,[CmdOptPtr] 2184 00000BEA BF8100 mov di,081h ; Offset in PSP for command line 2185 00000BED B020 mov al,' ' ; DOS command lines begin with a space 2186 00000BEF AA stosb 2187 2188 00000BF0 AC comboot_cmd_cp: lodsb 2189 00000BF1 20C0 and al,al 2190 00000BF3 7403 jz comboot_end_cmd 2191 00000BF5 AA stosb 2192 00000BF6 E2F8 loop comboot_cmd_cp 2193 00000BF8 B00D comboot_end_cmd: mov al,0Dh ; CR after last character 2194 00000BFA AA stosb 2195 00000BFB B07E mov al,126 ; Include space but not CR 2196 00000BFD 28C8 sub al,cl 2197 00000BFF 26A28000 mov [es:80h], al ; Store command line length 2198 2199 00000C03 8CC0 mov ax,es 2200 00000C05 8ED8 mov ds,ax 2201 00000C07 8ED0 mov ss,ax 2202 00000C09 31E4 xor sp,sp 2203 00000C0B 680000 push word 0 ; Return to address 0 -> exit 2204 2205 00000C0E EA00010010 jmp comboot_seg:100h ; Run it 2206 2207 ; Looks like a COMBOOT image but too large 2208 comboot_too_large: 2209 00000C13 BE[8216] mov si,err_comlarge 2210 00000C16 E83CF5 call writestr 2211 00000C19 E9F6F9 cb_enter: jmp enter_command 2212 2213 ; Proper return vector 2214 00000C1C FA comboot_return: cli ; Don't trust anyone 2215 00000C1D 31C0 xor ax,ax 2216 00000C1F 8ED0 mov ss,ax 2217 00000C21 368B261665 mov sp,[ss:SavedSP] 2218 00000C26 8ED8 mov ds,ax 2219 00000C28 8EC0 mov es,ax 2220 00000C2A FB sti 2221 00000C2B FC cld 2222 00000C2C EBEB jmp short cb_enter 2223 2224 ; Attempted to execute DOS system call 2225 00000C2E FA comboot_bogus: cli ; Don't trust anyone 2226 00000C2F 31C0 xor ax,ax 2227 00000C31 8ED0 mov ss,ax 2228 00000C33 368B261665 mov sp,[ss:SavedSP] 2229 00000C38 8ED8 mov ds,ax 2230 00000C3A 8EC0 mov es,ax 2231 00000C3C FB sti 2232 00000C3D FC cld 2233 00000C3E BE3A65 mov si,KernelCName 2234 00000C41 E811F5 call writestr 2235 00000C44 BE[6416] mov si,err_notdos 2236 00000C47 E80BF5 call writestr 2237 00000C4A EBCD jmp short cb_enter 2238 2239 ; 2240 ; Load a boot sector 2241 ; 2242 is_bootsector: 2243 ; Transfer zero bytes 2244 00000C4C 680000 push word 0 2245 00000C4F EB03 jmp short load_bootsec 2246 is_bss_sector: 2247 ; Transfer the superblock 2248 00000C51 683300 push word superblock_len 2249 load_bootsec: 2250 00000C54 21D2 and dx,dx 2251 00000C56 754C jnz bad_bootsec 2252 00000C58 8B1E[0B00] mov bx,[bsBytesPerSec] 2253 00000C5C 39D8 cmp ax,bx 2254 00000C5E 7544 jne bad_bootsec 2255 2256 ; Make sure we don't test this uninitialized 2257 00000C60 8997FE0F mov [bx+trackbuf-2],dx ; Note DX == 0 2258 2259 00000C64 BB0010 mov bx,trackbuf 2260 00000C67 B90100 mov cx,1 ; 1 cluster >= 1 sector 2261 00000C6A E8BEF6 call getfssec 2262 2263 00000C6D 8B1E[0B00] mov bx,[bsBytesPerSec] 2264 00000C71 8B87FE0F mov ax,[bx+trackbuf-2] 2265 00000C75 3D55AA cmp ax,0AA55h ; Boot sector signature 2266 00000C78 752A jne bad_bootsec 2267 2268 00000C7A BE[0B00] mov si,superblock 2269 00000C7D BF0B10 mov di,trackbuf+(superblock-bootsec) 2270 00000C80 59 pop cx ; Transfer count 2271 00000C81 F3A4 rep movsb 2272 ; 2273 ; Okay, here we go... copy over our own boot sector and run the new one 2274 ; 2275 00000C83 FA cli ; Point of no return 2276 2277 00000C84 8A16[2400] mov dl,[bsDriveNumber] ; May not be in new bootsector! 2278 2279 00000C88 BE0010 mov si,trackbuf 2280 00000C8B BF[0000] mov di,bootsec 2281 00000C8E 8B0E[0B00] mov cx,[bsBytesPerSec] 2282 00000C92 F3A4 rep movsb ; Copy the boot sector! 2283 2284 00000C94 BEB064 mov si,PartInfo 2285 00000C97 BFEE07 mov di,800h-18 ; Put partition info here 2286 00000C9A 57 push di 2287 00000C9B B90800 mov cx,8 ; 16 bytes 2288 00000C9E F3A5 rep movsw 2289 00000CA0 5E pop si ; DS:SI points to partition info 2290 2291 00000CA1 E95CF3 jmp bootsec 2292 2293 bad_bootsec: 2294 00000CA4 BE[9D16] mov si,err_bootsec 2295 00000CA7 E8ABF4 call writestr 2296 00000CAA E965F9 jmp enter_command 2297 2298 ; 2299 ; cwritestr: write a null-terminated string to the console, saving 2300 ; registers on entry (we can't use this in the boot sector, 2301 ; since we haven't verified 386-ness yet) 2302 ; 2303 cwritestr: 2304 00000CAD 60 pusha 2305 00000CAE AC cwstr_1: lodsb 2306 00000CAF 20C0 and al,al 2307 00000CB1 7409 jz cwstr_2 2308 00000CB3 B40E mov ah,0Eh ; Write to screen as TTY 2309 00000CB5 BB0700 mov bx,0007h ; White on black, current page 2310 00000CB8 CD10 int 10h 2311 00000CBA EBF2 jmp short cwstr_1 2312 00000CBC 61 cwstr_2: popa 2313 00000CBD C3 ret 2314 2315 ; 2316 ; 32-bit bcopy routine for real mode 2317 ; 2318 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd 2319 ; and then exit. IMPORTANT: This code assumes cs == ss == 0. 2320 ; 2321 ; This code is probably excessively anal-retentive in its handling of 2322 ; segments, but this stuff is painful enough as it is without having to rely 2323 ; on everything happening "as it ought to." 2324 ; 2325 00000CBE 90 align 4 2326 00000CC0 1F00 bcopy_gdt: dw bcopy_gdt_size-1 ; Null descriptor - contains GDT 2327 00000CC2 [C00C0000] dd bcopy_gdt ; pointer for LGDT instruction 2328 00000CC6 0000 dw 0 2329 00000CC8 FFFF0000 dd 0000ffffh ; Code segment, use16, readable, 2330 00000CCC 009B0000 dd 00009b00h ; present, dpl 0, cover 64K 2331 00000CD0 FFFF0000 dd 0000ffffh ; Data segment, use16, read/write, 2332 00000CD4 00938F00 dd 008f9300h ; present, dpl 0, cover all 4G 2333 00000CD8 FFFF0000 dd 0000ffffh ; Data segment, use16, read/write, 2334 00000CDC 00930000 dd 00009300h ; present, dpl 0, cover 64K 2335 bcopy_gdt_size: equ $-bcopy_gdt 2336 2337 00000CE0 6650 bcopy: push eax 2338 00000CE2 9C pushf ; Saves, among others, the IF flag 2339 00000CE3 0FA8 push gs 2340 00000CE5 0FA0 push fs 2341 00000CE7 1E push ds 2342 00000CE8 06 push es 2343 2344 00000CE9 FA cli 2345 00000CEA E84800 call enable_a20 2346 2347 00000CED 660F0116[C00C] o32 lgdt [bcopy_gdt] 2348 00000CF3 0F20C0 mov eax,cr0 2349 00000CF6 0C01 or al,1 2350 00000CF8 0F22C0 mov cr0,eax ; Enter protected mode 2351 00000CFB EA[000D]0800 jmp 08h:.in_pm 2352 2353 00000D00 B81000 .in_pm: mov ax,10h ; Data segment selector 2354 00000D03 8EC0 mov es,ax 2355 00000D05 8ED8 mov ds,ax 2356 2357 00000D07 B018 mov al,18h ; "Real-mode-like" data segment 2358 00000D09 8ED0 mov ss,ax 2359 00000D0B 8EE0 mov fs,ax 2360 00000D0D 8EE8 mov gs,ax 2361 2362 00000D0F 67F366A5 a32 rep movsd ; Do our business 2363 2364 00000D13 8EC0 mov es,ax ; Set to "real-mode-like" 2365 00000D15 8ED8 mov ds,ax 2366 2367 00000D17 0F20C0 mov eax,cr0 2368 00000D1A 24FE and al,~1 2369 00000D1C 0F22C0 mov cr0,eax ; Disable protected mode 2370 00000D1F EA[240D]0000 jmp 0:.in_rm 2371 2372 00000D24 31C0 .in_rm: xor ax,ax ; Back in real mode 2373 00000D26 8ED0 mov ss,ax 2374 00000D28 07 pop es 2375 00000D29 1F pop ds 2376 00000D2A 0FA1 pop fs 2377 00000D2C 0FA9 pop gs 2378 00000D2E E85200 call disable_a20 2379 2380 00000D31 9D popf ; Re-enables interrupts 2381 00000D32 6658 pop eax 2382 00000D34 C3 ret 2383 2384 ; 2385 ; Routines to enable and disable (yuck) A20. These routines are gathered 2386 ; from tips from a couple of sources, including the Linux kernel and 2387 ; http://www.x86.org/. The need for the delay to be as large as given here 2388 ; is indicated by Donnie Barnes of RedHat, the problematic system being an 2389 ; IBM ThinkPad 760EL. 2390 ; 2391 ; We typically toggle A20 twice for every 64K transferred. 2392 ; 2393 %define io_delay times 4 out 0EDh,al ; Invalid port (we hope) 2394 %define delaytime 1024 ; 4 x ISA bus cycles (@ 1.5 µs) 2395 2396 2397 enable_a20: 2398 00000D35 36C6062365FF mov byte [ss:A20Tries],255 ; Times to try to make this work 2399 2400 try_enable_a20: 2401 ; 2402 ; Flush the caches 2403 ; 2404 00000D3B E89600 call try_wbinvd 2405 2406 ; 2407 ; Enable the "fast A20 gate" 2408 ; 2409 00000D3E E492 in al, 092h 2410 00000D40 0C02 or al,02h 2411 00000D42 E692 out 092h, al 2412 ; 2413 ; Enable the keyboard controller A20 gate 2414 ; 2415 00000D44 E86600 call empty_8042 2416 00000D47 B0D1 mov al,0D1h ; Command write 2417 00000D49 E664 out 064h, al 2418 00000D4B E85F00 call empty_8042 2419 00000D4E B0DF mov al,0DFh ; A20 on 2420 00000D50 E660 out 060h, al 2421 00000D52 E84500 call kbc_delay 2422 ; Verify that A20 actually is enabled. Do that by 2423 ; observing a word in low memory and the same word in 2424 ; the HMA until they are no longer coherent. Note that 2425 ; we don't do the same check in the disable case, because 2426 ; we don't want to *require* A20 masking (SYSLINUX should 2427 ; work fine without it, if the BIOS does.) 2428 00000D55 06 push es 2429 00000D56 51 push cx 2430 00000D57 B9FFFF mov cx,0FFFFh ; Times to try, also HMA 2431 00000D5A 8EC1 mov es,cx ; HMA = segment 0FFFFh 2432 00000D5C 36FF061865 .a20_wait: inc word [ss:A20Test] 2433 00000D61 E87000 call try_wbinvd 2434 00000D64 26A12865 mov ax,[es:A20Test+10h] 2435 00000D68 363B061865 cmp ax,[ss:A20Test] 2436 00000D6D 7511 jne .a20_done 2437 00000D6F E2EB loop .a20_wait 2438 ; 2439 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up 2440 ; and report failure to the user. 2441 ; 2442 00000D71 59 pop cx 2443 00000D72 07 pop es 2444 2445 00000D73 36FE0E2365 dec byte [ss:A20Tries] 2446 00000D78 75C1 jnz try_enable_a20 2447 2448 00000D7A BE[C516] mov si, err_a20 2449 00000D7D E9F000 jmp abort_load 2450 ; 2451 ; A20 unmasked, proceed... 2452 ; 2453 00000D80 59 .a20_done: pop cx 2454 00000D81 07 pop es 2455 00000D82 C3 ret 2456 2457 disable_a20: 2458 ; 2459 ; Flush the caches 2460 ; 2461 00000D83 E84E00 call try_wbinvd 2462 ; 2463 ; Disable the "fast A20 gate" 2464 ; 2465 00000D86 E492 in al, 092h 2466 00000D88 24FD and al,~02h 2467 00000D8A E692 out 092h, al 2468 ; 2469 ; Disable the keyboard controller A20 gate 2470 ; 2471 00000D8C E81E00 call empty_8042 2472 00000D8F B0D1 mov al,0D1h 2473 00000D91 E664 out 064h, al ; Command write 2474 00000D93 E81700 call empty_8042 2475 00000D96 B0DD mov al,0DDh ; A20 off 2476 00000D98 E660 out 060h, al 2477 ; Fall through/tailcall to kbc_delay 2478 2479 00000D9A E81000 kbc_delay: call empty_8042 2480 00000D9D 51 push cx 2481 00000D9E B90004 mov cx, delaytime 2482 00000DA1 E6ED .delayloop: io_delay 2483 00000DA9 E2F6 loop .delayloop 2484 00000DAB 59 pop cx 2485 00000DAC C3 ret 2486 2487 empty_8042: 2488 00000DAD E6ED io_delay 2489 00000DB5 E464 in al, 064h ; Status port 2490 00000DB7 A801 test al,1 2491 00000DB9 740C jz .no_output 2492 00000DBB E6ED io_delay 2493 00000DC3 E460 in al, 060h ; Read input 2494 00000DC5 EBE6 jmp short empty_8042 2495 .no_output: 2496 00000DC7 A802 test al,2 2497 00000DC9 75E2 jnz empty_8042 2498 00000DCB E6ED io_delay 2499 00000DD3 C3 ret 2500 2501 ; 2502 ; WBINVD instruction; gets auto-eliminated on 386 CPUs 2503 ; 2504 try_wbinvd: 2505 00000DD4 0F09 wbinvd 2506 00000DD6 C3 ret 2507 2508 ; 2509 ; Load RAM disk into high memory 2510 ; 2511 loadinitrd: 2512 00000DD7 06 push es ; Save ES on entry 2513 00000DD8 B80090 mov ax,real_mode_seg 2514 00000DDB 8EC0 mov es,ax 2515 00000DDD 8B36[5217] mov si,[initrd_ptr] 2516 00000DE1 668B3EC064 mov edi,[InitRDat] ; initrd load address 2517 00000DE6 2666893E1802 mov [es:su_ramdiskat],edi ; Offset for ram disk 2518 00000DEC 56 push si 2519 00000DED BE4765 mov si,InitRDCName ; Write ramdisk name 2520 00000DF0 E8BAFE call cwritestr 2521 00000DF3 BE[EB16] mov si,dotdot_msg ; Write dots 2522 00000DF6 E8B4FE call cwritestr 2523 rd_load_loop: 2524 00000DF9 BE[EC16] mov si,dot_msg ; Progress report 2525 00000DFC E8AEFE call cwritestr 2526 00000DFF 5E pop si ; Restore cluster pointer 2527 00000E00 E85300 call abort_check 2528 00000E03 8B0E0265 mov cx,[InitRDClust] 2529 00000E07 3B0E0465 cmp cx,[ClustPerMoby] 2530 00000E0B 7604 jna rd_last_moby 2531 00000E0D 8B0E0465 mov cx,[ClustPerMoby] 2532 rd_last_moby: 2533 00000E11 290E0265 sub [InitRDClust],cx 2534 00000E15 31DB xor bx,bx ; Load at offset 0 2535 00000E17 680070 push word xfer_buf_seg ; Bounce buffer segment 2536 00000E1A 07 pop es 2537 00000E1B 51 push cx 2538 00000E1C E80CF5 call getfssec 2539 00000E1F 59 pop cx 2540 00000E20 56 push si ; Save cluster pointer 2541 00000E21 66BE00000700 mov esi,(xfer_buf_seg << 4) 2542 00000E27 668B3EC064 mov edi,[InitRDat] 2543 00000E2C 66B900400000 mov ecx,4000h ; Copy 64K 2544 00000E32 E8ABFE call bcopy ; Does not change flags!! 2545 00000E35 7210 jc rd_load_done ; EOF? 2546 00000E37 668106C06400000100 add dword [InitRDat],10000h ; Point to next 64K 2547 00000E40 833E026500 cmp word [InitRDClust],byte 0 ; Are we done? 2548 00000E45 75B2 jne rd_load_loop ; Apparently not 2549 rd_load_done: 2550 00000E47 5E pop si ; Clean up the stack 2551 00000E48 BE[F716] mov si,crlf_msg 2552 00000E4B E807F3 call writestr 2553 00000E4E BE[E216] mov si,loading_msg ; Write new "Loading " for 2554 00000E51 E801F3 call writestr ; the benefit of the kernel 2555 00000E54 07 pop es ; Restore original ES 2556 00000E55 C3 ret 2557 2558 ; 2559 ; abort_check: let the user abort with or 2560 ; 2561 abort_check: 2562 00000E56 60 pusha 2563 ac1: 2564 00000E57 B401 mov ah,1 ; Check for pending keystroke 2565 00000E59 CD16 int 16h 2566 00000E5B 7426 jz ac_ret ; If no pending keystroke 2567 00000E5D 31C0 xor ax,ax ; Load pending keystroke 2568 00000E5F CD16 int 16h 2569 00000E61 BB0063 mov bx,KbdMap 2570 00000E64 D7 xlatb 2571 00000E65 3C1B cmp al,27 ; aborts (DOS geeks) 2572 00000E67 7404 je ac2 2573 00000E69 3C03 cmp al,3 ; So does Ctrl-C (UNIX geeks) 2574 00000E6B 75EA jne ac1 ; Unknown key... try again 2575 ac2: ; If we get here, ABORT! 2576 00000E6D BE[EE16] mov si,aborted_msg 2577 ; Fall through to abort_load 2578 ; 2579 ; abort_load: Called by various routines which wants to print a fatal 2580 ; error message and return to the command prompt. Since this 2581 ; may happen at just about any stage of the boot process, assume 2582 ; our state is messed up, and just reset the segment registers 2583 ; and the stack forcibly. 2584 ; 2585 ; SI = offset (in _text) of error message to print 2586 ; 2587 abort_load: 2588 00000E70 8CC8 mov ax,cs ; Restore CS = DS = ES 2589 00000E72 8ED8 mov ds,ax 2590 00000E74 8EC0 mov es,ax 2591 00000E76 FA cli 2592 00000E77 BCFA5F mov sp,StackBuf-2*3 ; Reset stack 2593 00000E7A 8ED0 mov ss,ax ; Just in case... 2594 00000E7C FB sti 2595 00000E7D E8D5F2 call writestr ; Expects SI -> error msg 2596 00000E80 E98FF7 al_ok: jmp enter_command ; Return to command prompt 2597 ; 2598 ; End of abort_check 2599 ; 2600 00000E83 61 ac_ret: popa 2601 00000E84 C3 ret 2602 2603 ; 2604 ; searchdir: Search the root directory for a pre-mangled filename in 2605 ; DS:DI. This routine is similar to the one in the boot 2606 ; sector, but is a little less Draconian when it comes to 2607 ; error handling, plus it reads the root directory in 2608 ; larger chunks than a sector at a time (which is probably 2609 ; a waste of coding effort, but I like to do things right). 2610 ; 2611 ; FIXME: usually we can load the entire root dir in memory, 2612 ; and files are usually at the beginning anyway. It probably 2613 ; would be worthwhile to remember if we have the first chunk 2614 ; in memory and skip the load if that (it would speed up online 2615 ; help, mainly.) 2616 ; 2617 ; NOTE: This file considers finding a zero-length file an 2618 ; error. This is so we don't have to deal with that special 2619 ; case elsewhere in the program (most loops have the test 2620 ; at the end). 2621 ; 2622 ; If successful: 2623 ; ZF clear 2624 ; SI = cluster # for the first cluster 2625 ; DX:AX = file length in bytes 2626 ; If unsuccessful 2627 ; ZF set 2628 ; 2629 2630 searchdir: 2631 00000E85 A1[1100] mov ax,[bsRootDirEnts] 2632 00000E88 A3EA64 mov [DirScanCtr],ax 2633 00000E8B A1E864 mov ax,[RootDirSize] 2634 00000E8E A3EC64 mov [DirBlocksLeft],ax 2635 00000E91 A1DC64 mov ax,[RootDir1] 2636 00000E94 8B16DE64 mov dx,[RootDir2] 2637 scan_group: 2638 00000E98 8B2EEC64 mov bp,[DirBlocksLeft] 2639 00000E9C 21ED and bp,bp 2640 00000E9E 7467 jz dir_return 2641 00000EA0 3B2EFA64 cmp bp,[BufSafeSec] 2642 00000EA4 7604 jna load_last 2643 00000EA6 8B2EFA64 mov bp,[BufSafeSec] 2644 load_last: 2645 00000EAA 292EEC64 sub [DirBlocksLeft],bp 2646 00000EAE 50 push ax 2647 00000EAF 52 push dx 2648 00000EB0 A1[0B00] mov ax,[bsBytesPerSec] 2649 00000EB3 F7E5 mul bp 2650 00000EB5 05E10F add ax,trackbuf-31 2651 00000EB8 A3EE64 mov [EndofDirSec],ax ; End of loaded 2652 00000EBB 5A pop dx 2653 00000EBC 58 pop ax 2654 00000EBD 55 push bp ; Save number of sectors 2655 00000EBE 50 push ax ; Save present location 2656 00000EBF 52 push dx 2657 00000EC0 57 push di ; Save name 2658 00000EC1 BB0010 mov bx,trackbuf 2659 00000EC4 E8A9F2 call getlinsec 2660 00000EC7 5F pop di 2661 00000EC8 5A pop dx 2662 00000EC9 58 pop ax 2663 00000ECA 5D pop bp 2664 00000ECB BE0010 mov si,trackbuf 2665 00000ECE 803C00 dir_test_name: cmp byte [si],0 ; Directory high water mark 2666 00000ED1 7434 je dir_return ; Failed 2667 00000ED3 F6440B18 test byte [si+11],18h ; Check it really is a file 2668 00000ED7 750B jnz dir_not_this 2669 00000ED9 57 push di 2670 00000EDA 56 push si 2671 00000EDB B90B00 mov cx,11 ; Filename = 11 bytes 2672 00000EDE F3A6 repe cmpsb 2673 00000EE0 5E pop si 2674 00000EE1 5F pop di 2675 00000EE2 7416 je dir_success 2676 00000EE4 83C620 dir_not_this: add si,byte 32 2677 00000EE7 FF0EEA64 dec word [DirScanCtr] 2678 00000EEB 741A jz dir_return ; Out of it... 2679 00000EED 3B36EE64 cmp si,[EndofDirSec] 2680 00000EF1 72DB jb dir_test_name 2681 00000EF3 01E8 add ax,bp ; Increment linear sector number 2682 00000EF5 83D200 adc dx,byte 0 2683 00000EF8 EB9E jmp short scan_group 2684 dir_success: 2685 00000EFA 8B441C mov ax,[si+28] ; Length of file 2686 00000EFD 8B541E mov dx,[si+30] 2687 00000F00 8B741A mov si,[si+26] ; Cluster pointer 2688 00000F03 89C3 mov bx,ax 2689 00000F05 09D3 or bx,dx ; Sets ZF iff DX:AX is zero 2690 dir_return: 2691 00000F07 C3 ret 2692 2693 ; 2694 ; writechr: Write a single character in AL to the screen without 2695 ; mangling any registers 2696 ; 2697 writechr: 2698 00000F08 60 pusha 2699 00000F09 B40E mov ah,0Eh 2700 00000F0B BB0700 mov bx,0007h ; white text on this page 2701 00000F0E CD10 int 10h 2702 00000F10 61 popa 2703 00000F11 C3 ret 2704 2705 ; 2706 ; adjust_screen: Set the internal variables associated with the screen size. 2707 ; This is a subroutine in case we're loading a custom font. 2708 ; 2709 adjust_screen: 2710 00000F12 A08404 mov al,[BIOS_vidrows] 2711 00000F15 20C0 and al,al 2712 00000F17 7502 jnz vidrows_is_ok 2713 00000F19 B018 mov al,24 ; No vidrows in BIOS, assume 25 2714 ; (Remember: vidrows == rows-1) 2715 00000F1B A21F65 vidrows_is_ok: mov [VidRows],al 2716 00000F1E B40F mov ah,0fh 2717 00000F20 CD10 int 10h ; Read video state 2718 00000F22 883E1B65 mov [TextPage],bh 2719 00000F26 FECC dec ah ; Store count-1 (same as rows) 2720 00000F28 88261E65 mov [VidCols],ah 2721 00000F2C C3 bf_ret: ret 2722 2723 ; 2724 ; loadfont: Load a .psf font file and install it onto the VGA console 2725 ; (if we're not on a VGA screen then ignore.) It is called with 2726 ; SI and DX:AX set by routine searchdir 2727 ; 2728 loadfont: 2729 00000F2D BB0010 mov bx,trackbuf ; The trackbuf is >= 16K; the part 2730 00000F30 8B0EF864 mov cx,[BufSafe] ; of a PSF file we care about is no 2731 00000F34 E8F4F3 call getfssec ; more than 8K+4 bytes 2732 2733 00000F37 A10010 mov ax,[trackbuf] ; Magic number 2734 00000F3A 3D3604 cmp ax,0436h 2735 00000F3D 75ED jne bf_ret 2736 2737 00000F3F A00210 mov al,[trackbuf+2] ; File mode 2738 00000F42 3C03 cmp al,3 ; Font modes 0-3 supported 2739 00000F44 77E6 ja bf_ret 2740 2741 00000F46 8A3E0310 mov bh,byte [trackbuf+3] ; Height of font 2742 00000F4A 80FF02 cmp bh,2 ; VGA minimum 2743 00000F4D 72DD jb bf_ret 2744 00000F4F 80FF20 cmp bh,32 ; VGA maximum 2745 00000F52 77D8 ja bf_ret 2746 2747 00000F54 BD0410 mov bp,trackbuf+4 ; Address of font data 2748 00000F57 30DB xor bl,bl 2749 00000F59 B90001 mov cx,256 2750 00000F5C 31D2 xor dx,dx 2751 00000F5E B81011 mov ax,1110h 2752 00000F61 CD10 int 10h ; Load into VGA RAM 2753 2754 00000F63 30DB xor bl,bl 2755 00000F65 B80311 mov ax,1103h ; Select page 0 2756 00000F68 CD10 int 10h 2757 2758 00000F6A EBA6 jmp short adjust_screen 2759 2760 ; 2761 ; loadkeys: Load a LILO-style keymap; SI and DX:AX set by searchdir 2762 ; 2763 loadkeys: 2764 00000F6C 21D2 and dx,dx ; Should be 256 bytes exactly 2765 00000F6E 751A jne loadkeys_ret 2766 00000F70 3D0001 cmp ax,256 2767 00000F73 7515 jne loadkeys_ret 2768 2769 00000F75 BB0010 mov bx,trackbuf 2770 00000F78 B90100 mov cx,1 ; 1 cluster should be >= 256 bytes 2771 00000F7B E8ADF3 call getfssec 2772 2773 00000F7E BE0010 mov si,trackbuf 2774 00000F81 BF0063 mov di,KbdMap 2775 00000F84 B94000 mov cx,256 >> 2 2776 00000F87 F366A5 rep movsd 2777 2778 00000F8A C3 loadkeys_ret: ret 2779 2780 ; 2781 ; get_msg_file: Load a text file and write its contents to the screen, 2782 ; interpreting color codes. Is called with SI and DX:AX 2783 ; set by routine searchdir 2784 ; 2785 get_msg_file: 2786 00000F8B C7061265[DF0F] mov word [NextCharJump],msg_putchar ; State machine for color 2787 00000F91 C6061A6507 mov byte [TextAttribute],07h ; Default grey on white 2788 00000F96 60 pusha 2789 00000F97 8A3E1B65 mov bh,[TextPage] 2790 00000F9B B403 mov ah,03h ; Read cursor position 2791 00000F9D CD10 int 10h 2792 00000F9F 89161C65 mov [CursorDX],dx 2793 00000FA3 61 popa 2794 00000FA4 50 get_msg_chunk: push ax ; DX:AX = length of file 2795 00000FA5 52 push dx 2796 00000FA6 BB0010 mov bx,trackbuf 2797 00000FA9 8B0EF864 mov cx,[BufSafe] 2798 00000FAD E87BF3 call getfssec 2799 00000FB0 5A pop dx 2800 00000FB1 58 pop ax 2801 00000FB2 56 push si ; Save current cluster 2802 00000FB3 BE0010 mov si,trackbuf 2803 00000FB6 8B0EFC64 mov cx,[BufSafeBytes] ; No more than many bytes 2804 00000FBA 51 print_msg_file: push cx 2805 00000FBB 50 push ax 2806 00000FBC 52 push dx 2807 00000FBD AC lodsb 2808 00000FBE 3C1A cmp al,1Ah ; ASCII EOF? 2809 00000FC0 7418 je msg_done_pop 2810 00000FC2 FF161265 call [NextCharJump] ; Do what shall be done 2811 00000FC6 5A pop dx 2812 00000FC7 58 pop ax 2813 00000FC8 59 pop cx 2814 00000FC9 83E801 sub ax,byte 1 2815 00000FCC 83DA00 sbb dx,byte 0 2816 00000FCF 89C3 mov bx,ax 2817 00000FD1 09D3 or bx,dx 2818 00000FD3 7408 jz msg_done 2819 00000FD5 E2E3 loop print_msg_file 2820 00000FD7 5E pop si 2821 00000FD8 EBCA jmp short get_msg_chunk 2822 msg_done_pop: 2823 00000FDA 83C406 add sp,byte 6 ; Lose 3 words on the stack 2824 msg_done: 2825 00000FDD 5E pop si 2826 00000FDE C3 ret 2827 msg_putchar: ; Normal character 2828 00000FDF 3C0F cmp al,0Fh ; ^O = color code follows 2829 00000FE1 7431 je msg_ctrl_o 2830 00000FE3 3C0D cmp al,0Dh ; Ignore 2831 00000FE5 742C je msg_ignore 2832 00000FE7 3C0A cmp al,0Ah ; = newline 2833 00000FE9 7430 je msg_newline 2834 00000FEB 3C0C cmp al,0Ch ; = clear screen 2835 00000FED 7455 je msg_formfeed 2836 00000FEF 8B1E1A65 mov bx,[TextAttrBX] 2837 00000FF3 B409 mov ah,09h ; Write character/attribute 2838 00000FF5 B90100 mov cx,1 ; One character only 2839 00000FF8 CD10 int 10h ; Write to screen 2840 00000FFA A01C65 mov al,[CursorCol] 2841 00000FFD 40 inc ax 2842 00000FFE 3A061E65 cmp al,[VidCols] 2843 00001002 7717 ja msg_newline 2844 00001004 A21C65 mov [CursorCol],al 2845 00001007 8A3E1B65 msg_gotoxy: mov bh,[TextPage] 2846 0000100B 8B161C65 mov dx,[CursorDX] 2847 0000100F B402 mov ah,02h ; Set cursor position 2848 00001011 CD10 int 10h 2849 00001013 C3 msg_ignore: ret 2850 msg_ctrl_o: ; ^O = color code follows 2851 00001014 C7061265[5910] mov word [NextCharJump],msg_setbg 2852 0000101A C3 ret 2853 msg_newline: ; Newline char or end of line 2854 0000101B C6061C6500 mov byte [CursorCol],0 2855 00001020 A01D65 mov al,[CursorRow] 2856 00001023 40 inc ax 2857 00001024 3A061F65 cmp al,[VidRows] 2858 00001028 7705 ja msg_scroll 2859 0000102A A21D65 mov [CursorRow],al 2860 0000102D EBD8 jmp short msg_gotoxy 2861 0000102F 31C9 msg_scroll: xor cx,cx ; Upper left hand corner 2862 00001031 8B161E65 mov dx,[ScreenSize] 2863 00001035 88361D65 mov [CursorRow],dh ; New cursor at the bottom 2864 00001039 8A3E1A65 mov bh,[TextAttribute] 2865 0000103D B80106 mov ax,0601h ; Scroll up one line 2866 00001040 CD10 int 10h 2867 00001042 EBC3 jmp short msg_gotoxy 2868 msg_formfeed: ; Form feed character 2869 00001044 31C9 xor cx,cx 2870 00001046 890E1C65 mov [CursorDX],cx ; Upper lefthand corner 2871 0000104A 8B161E65 mov dx,[ScreenSize] 2872 0000104E 8A3E1A65 mov bh,[TextAttribute] 2873 00001052 B80006 mov ax,0600h ; Clear screen region 2874 00001055 CD10 int 10h 2875 00001057 EBAE jmp short msg_gotoxy 2876 msg_setbg: ; Color background character 2877 00001059 E8BE01 call unhexchar 2878 0000105C 721D jc msg_color_bad 2879 0000105E C0E004 shl al,4 2880 00001061 A21A65 mov [TextAttribute],al 2881 00001064 C7061265[6B10] mov word [NextCharJump],msg_setfg 2882 0000106A C3 ret 2883 msg_setfg: ; Color foreground character 2884 0000106B E8AC01 call unhexchar 2885 0000106E 720B jc msg_color_bad 2886 00001070 08061A65 or [TextAttribute],al ; setbg set foreground to 0 2887 00001074 C7061265[DF0F] mov word [NextCharJump],msg_putchar 2888 0000107A C3 ret 2889 msg_color_bad: 2890 0000107B C6061A6507 mov byte [TextAttribute],07h ; Default attribute 2891 00001080 C7061265[DF0F] mov word [NextCharJump],msg_putchar 2892 00001086 C3 ret 2893 2894 ; 2895 ; open,getc: Load a file a character at a time for parsing in a manner 2896 ; similar to the C library getc routine. Only one simultaneous 2897 ; use is supported. Note: "open" trashes the trackbuf. 2898 ; 2899 ; open: Input: mangled filename in DS:DI 2900 ; Output: ZF set on file not found or zero length 2901 ; 2902 ; getc: Output: CF set on end of file 2903 ; Character loaded in AL 2904 ; 2905 open: 2906 00001087 E8FBFD call searchdir 2907 0000108A 7427 jz open_return 2908 0000108C 9C pushf 2909 0000108D A3E464 mov [FBytes1],ax 2910 00001090 8916E664 mov [FBytes2],dx 2911 00001094 0306F264 add ax,[ClustSize] 2912 00001098 83D200 adc dx,byte 0 2913 0000109B 83E801 sub ax,byte 1 2914 0000109E 83DA00 sbb dx,byte 0 2915 000010A1 F736F264 div word [ClustSize] 2916 000010A5 A30665 mov [FClust],ax ; Number of clusters 2917 000010A8 89360865 mov [FNextClust],si ; Cluster pointer 2918 000010AC A1FE64 mov ax,[EndOfGetCBuf] ; Pointer at end of buffer -> 2919 000010AF A30A65 mov [FPtr],ax ; nothing loaded yet 2920 000010B2 9D popf ; Restore no ZF 2921 000010B3 C3 open_return: ret 2922 2923 ; 2924 getc: 2925 000010B4 F9 stc ; If we exit here -> EOF 2926 000010B5 668B0EE464 mov ecx,[FBytes] 2927 000010BA 66E33B jecxz getc_ret 2928 000010BD 8B360A65 mov si,[FPtr] 2929 000010C1 3B36FE64 cmp si,[EndOfGetCBuf] 2930 000010C5 7226 jb getc_loaded 2931 ; Buffer empty -- load another set 2932 000010C7 8B0E0665 mov cx,[FClust] 2933 000010CB 3B0EF864 cmp cx,[BufSafe] 2934 000010CF 7604 jna getc_oksize 2935 000010D1 8B0EF864 mov cx,[BufSafe] 2936 000010D5 290E0665 getc_oksize: sub [FClust],cx ; Reduce remaining clusters 2937 000010D9 8B360865 mov si,[FNextClust] 2938 000010DD BB0096 mov bx,getcbuf 2939 000010E0 53 push bx 2940 000010E1 06 push es ; ES may be != DS, save old ES 2941 000010E2 1E push ds ; Trackbuf is in DS, not ES 2942 000010E3 07 pop es 2943 000010E4 E844F2 call getfssec ; Load a trackbuf full of data 2944 000010E7 89360865 mov [FNextClust],si ; Store new next pointer 2945 000010EB 07 pop es ; Restore ES 2946 000010EC 5E pop si ; SI -> newly loaded data 2947 000010ED AC getc_loaded: lodsb ; Load a byte 2948 000010EE 89360A65 mov [FPtr],si ; Update next byte pointer 2949 000010F2 66FF0EE464 dec dword [FBytes] ; Update bytes left counter (CF = 1) 2950 000010F7 F8 clc ; Not EOF 2951 000010F8 C3 getc_ret: ret 2952 2953 ; 2954 ; ungetc: Push a character (in AL) back into the getc buffer 2955 ; Note: if more than one byte is pushed back, this may cause 2956 ; bytes to be written below the getc buffer boundary. If there 2957 ; is a risk for this to occur, the getcbuf base address should 2958 ; be moved up. 2959 ; 2960 ungetc: 2961 000010F9 8B360A65 mov si,[FPtr] 2962 000010FD 4E dec si 2963 000010FE 8804 mov [si],al 2964 00001100 89360A65 mov [FPtr],si 2965 00001104 66FF06E464 inc dword [FBytes] 2966 00001109 C3 ret 2967 2968 ; 2969 ; skipspace: Skip leading whitespace using "getc". If we hit end-of-line 2970 ; or end-of-file, return with carry set; ZF = true of EOF 2971 ; ZF = false for EOLN; otherwise CF = ZF = 0. 2972 ; 2973 ; Otherwise AL = first character after whitespace 2974 ; 2975 skipspace: 2976 0000110A E8A7FF skipspace_loop: call getc 2977 0000110D 720D jc skipspace_eof 2978 0000110F 3C1A cmp al,1Ah ; DOS EOF 2979 00001111 7409 je skipspace_eof 2980 00001113 3C0A cmp al,0Ah 2981 00001115 7409 je skipspace_eoln 2982 00001117 3C20 cmp al,' ' 2983 00001119 76EF jbe skipspace_loop 2984 0000111B C3 ret ; CF = ZF = 0 2985 0000111C 38C0 skipspace_eof: cmp al,al ; Set ZF 2986 0000111E F9 stc ; Set CF 2987 0000111F C3 ret 2988 00001120 04FF skipspace_eoln: add al,0FFh ; Set CF, clear ZF 2989 00001122 C3 ret 2990 2991 ; 2992 ; getkeyword: Get a keyword from the current "getc" file; only the two 2993 ; first characters are considered significant. 2994 ; 2995 ; Lines beginning with ASCII characters 33-47 are treated 2996 ; as comments and ignored; other lines are checked for 2997 ; validity by scanning through the keywd_table. 2998 ; 2999 ; The keyword and subsequent whitespace is skipped. 3000 ; 3001 ; On EOF, CF = 1; otherwise, CF = 0, AL:AH = lowercase char pair 3002 ; 3003 getkeyword: 3004 00001123 E8E4FF gkw_find: call skipspace 3005 00001126 7438 jz gkw_eof ; end of file 3006 00001128 72F9 jc gkw_find ; end of line: try again 3007 0000112A 3C30 cmp al,'0' 3008 0000112C 7246 jb gkw_skipline ; skip comment line 3009 0000112E 50 push ax 3010 0000112F E882FF call getc 3011 00001132 5B pop bx 3012 00001133 722B jc gkw_eof 3013 00001135 88C7 mov bh,al ; Move character pair into BL:BH 3014 00001137 81CB2020 or bx,2020h ; Lower-case it 3015 0000113B BE[0C17] mov si,keywd_table 3016 0000113E AD gkw_check: lodsw 3017 0000113F 21C0 and ax,ax 3018 00001141 7429 jz gkw_badline ; Bad keyword, write message 3019 00001143 39D8 cmp ax,bx 3020 00001145 75F7 jne gkw_check 3021 00001147 50 push ax 3022 gkw_skiprest: 3023 00001148 E869FF call getc 3024 0000114B 7212 jc gkw_eof_pop 3025 0000114D 3C30 cmp al,'0' 3026 0000114F 77F7 ja gkw_skiprest 3027 00001151 E8A5FF call ungetc 3028 00001154 E8B3FF call skipspace 3029 00001157 7406 jz gkw_eof_pop 3030 00001159 7206 jc gkw_missingpar ; Missing parameter after keyword 3031 0000115B E89BFF call ungetc ; Return character to buffer 3032 0000115E F8 clc ; Successful return 3033 0000115F 58 gkw_eof_pop: pop ax 3034 00001160 C3 gkw_eof: ret ; CF = 1 on all EOF conditions 3035 00001161 58 gkw_missingpar: pop ax 3036 00001162 BE[A115] mov si,err_noparm 3037 00001165 E8EDEF call writestr 3038 00001168 E9B8FF jmp gkw_find 3039 0000116B 58 gkw_badline_pop: pop ax 3040 0000116C BE[7E15] gkw_badline: mov si,err_badcfg 3041 0000116F E8E3EF call writestr 3042 00001172 EBAF jmp short gkw_find 3043 00001174 3C0A gkw_skipline: cmp al,10 ; Scan for LF 3044 00001176 74AB je gkw_find 3045 00001178 E839FF call getc 3046 0000117B 72E3 jc gkw_eof 3047 0000117D EBF5 jmp short gkw_skipline 3048 3049 ; 3050 ; getint: Load an integer from the getc file. 3051 ; Return CF if error; otherwise return integer in EBX 3052 ; 3053 getint: 3054 0000117F BFA064 mov di,NumBuf 3055 00001182 81FFAF64 gi_getnum: cmp di,NumBufEnd ; Last byte in NumBuf 3056 00001186 730F jae gi_loaded 3057 00001188 57 push di 3058 00001189 E828FF call getc 3059 0000118C 5F pop di 3060 0000118D 7208 jc gi_loaded 3061 0000118F AA stosb 3062 00001190 3C2D cmp al,'-' 3063 00001192 73EE jnb gi_getnum 3064 00001194 E862FF call ungetc ; Unget non-numeric 3065 00001197 C60500 gi_loaded: mov byte [di],0 3066 0000119A BEA064 mov si,NumBuf 3067 ; Fall through to parseint 3068 3069 ; 3070 ; parseint: Convert an integer to a number in EBX 3071 ; Get characters from string in DS:SI 3072 ; Return CF on error 3073 ; DS:SI points to first character after number 3074 ; 3075 ; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M 3076 ; 3077 parseint: 3078 0000119D 6650 push eax 3079 0000119F 6651 push ecx 3080 000011A1 55 push bp 3081 000011A2 6631C0 xor eax,eax ; Current digit (keep eax == al) 3082 000011A5 6689C3 mov ebx,eax ; Accumulator 3083 000011A8 6689D9 mov ecx,ebx ; Base 3084 000011AB 31ED xor bp,bp ; Used for negative flag 3085 000011AD AC pi_begin: lodsb 3086 000011AE 3C2D cmp al,'-' 3087 000011B0 7506 jne pi_not_minus 3088 000011B2 81F50100 xor bp,1 ; Set unary minus flag 3089 000011B6 EBF5 jmp short pi_begin 3090 pi_not_minus: 3091 000011B8 3C30 cmp al,'0' 3092 000011BA 724F jb pi_err 3093 000011BC 7408 je pi_octhex 3094 000011BE 3C39 cmp al,'9' 3095 000011C0 7749 ja pi_err 3096 000011C2 B10A mov cl,10 ; Base = decimal 3097 000011C4 EB17 jmp short pi_foundbase 3098 pi_octhex: 3099 000011C6 AC lodsb 3100 000011C7 3C30 cmp al,'0' 3101 000011C9 7225 jb pi_km ; Value is zero 3102 000011CB 0C20 or al,20h ; Downcase 3103 000011CD 3C78 cmp al,'x' 3104 000011CF 7408 je pi_ishex 3105 000011D1 3C37 cmp al,'7' 3106 000011D3 7736 ja pi_err 3107 000011D5 B108 mov cl,8 ; Base = octal 3108 000011D7 EB04 jmp short pi_foundbase 3109 pi_ishex: 3110 000011D9 B030 mov al,'0' ; No numeric value accrued yet 3111 000011DB B110 mov cl,16 ; Base = hex 3112 pi_foundbase: 3113 000011DD E83A00 call unhexchar 3114 000011E0 720E jc pi_km ; Not a (hex) digit 3115 000011E2 38C8 cmp al,cl 3116 000011E4 730A jae pi_km ; Invalid for base 3117 000011E6 660FAFD9 imul ebx,ecx ; Multiply accumulated by base 3118 000011EA 6601C3 add ebx,eax ; Add current digit 3119 000011ED AC lodsb 3120 000011EE EBED jmp short pi_foundbase 3121 pi_km: 3122 000011F0 4E dec si ; Back up to last non-numeric 3123 000011F1 AC lodsb 3124 000011F2 0C20 or al,20h 3125 000011F4 3C6B cmp al,'k' 3126 000011F6 7416 je pi_isk 3127 000011F8 3C6D cmp al,'m' 3128 000011FA 7418 je pi_ism 3129 000011FC 4E dec si ; Back up 3130 000011FD 21ED pi_fini: and bp,bp 3131 000011FF 7404 jz pi_ret ; CF=0! 3132 00001201 66F7DB neg ebx ; Value was negative 3133 00001204 F8 pi_done: clc 3134 00001205 5D pi_ret: pop bp 3135 00001206 6659 pop ecx 3136 00001208 6658 pop eax 3137 0000120A C3 ret 3138 0000120B F9 pi_err: stc 3139 0000120C EBF7 jmp short pi_ret 3140 0000120E 66C1E30A pi_isk: shl ebx,10 ; x 2^10 3141 00001212 EBF0 jmp short pi_done 3142 00001214 66C1E314 pi_ism: shl ebx,20 ; x 2^20 3143 00001218 EBEA jmp short pi_done 3144 3145 ; 3146 ; unhexchar: Convert a hexadecimal digit in AL to the equivalent number; 3147 ; return CF=1 if not a hex digit 3148 ; 3149 unhexchar: 3150 0000121A 3C30 cmp al,'0' 3151 0000121C 7215 jb uxc_ret ; If failure, CF == 1 already 3152 0000121E 3C39 cmp al,'9' 3153 00001220 7703 ja uxc_1 3154 00001222 2C30 sub al,'0' ; CF <- 0 3155 00001224 C3 ret 3156 00001225 0C20 uxc_1: or al,20h ; upper case -> lower case 3157 00001227 3C61 cmp al,'a' 3158 00001229 7208 jb uxc_ret ; If failure, CF == 1 already 3159 0000122B 3C66 cmp al,'f' 3160 0000122D 7703 ja uxc_err 3161 0000122F 2C57 sub al,'a'-10 ; CF <- 0 3162 00001231 C3 ret 3163 00001232 F9 uxc_err: stc 3164 00001233 C3 uxc_ret: ret 3165 3166 ; 3167 ; 3168 ; getline: Get a command line, converting control characters to spaces 3169 ; and collapsing streches to one; a space is appended to the 3170 ; end of the string, unless the line is empty. 3171 ; The line is terminated by ^J, ^Z or EOF and is written 3172 ; to ES:DI. On return, DI points to first char after string. 3173 ; CF is set if we hit EOF. 3174 ; 3175 getline: 3176 00001234 E8D3FE call skipspace 3177 00001237 B201 mov dl,1 ; Empty line -> empty string. 3178 00001239 742B jz gl_eof ; eof 3179 0000123B 7226 jc gl_eoln ; eoln 3180 0000123D E8B9FE call ungetc 3181 00001240 52 gl_fillloop: push dx 3182 00001241 57 push di 3183 00001242 E86FFE call getc 3184 00001245 5F pop di 3185 00001246 5A pop dx 3186 00001247 721E jc gl_ret ; CF set! 3187 00001249 3C20 cmp al,' ' 3188 0000124B 7605 jna gl_ctrl 3189 0000124D 31D2 xor dx,dx 3190 0000124F AA gl_store: stosb 3191 00001250 EBEE jmp short gl_fillloop 3192 00001252 3C0A gl_ctrl: cmp al,10 3193 00001254 7411 je gl_ret ; CF clear! 3194 00001256 3C1A cmp al,26 3195 00001258 740C je gl_eof 3196 0000125A 20D2 and dl,dl 3197 0000125C 75E2 jnz gl_fillloop ; Ignore multiple spaces 3198 0000125E B020 mov al,' ' ; Ctrl -> space 3199 00001260 42 inc dx 3200 00001261 EBEC jmp short gl_store 3201 00001263 F8 gl_eoln: clc ; End of line is not end of file 3202 00001264 EB01 jmp short gl_ret 3203 00001266 F9 gl_eof: stc 3204 00001267 9C gl_ret: pushf ; We want the last char to be space! 3205 00001268 20D2 and dl,dl 3206 0000126A 7503 jnz gl_xret 3207 0000126C B020 mov al,' ' 3208 0000126E AA stosb 3209 0000126F 9D gl_xret: popf 3210 00001270 C3 ret 3211 3212 3213 %ifdef debug ; This code for debugging only 3214 ; 3215 ; dumpregs: Dumps the contents of all registers 3216 ; 3217 assume ds:_text, es:NOTHING, fs:NOTHING, gs:NOTHING 3218 dumpregs proc near ; When calling, IP is on stack 3219 pushf ; Store flags 3220 pusha 3221 push ds 3222 push es 3223 push fs 3224 push gs 3225 push cs ; Set DS <- CS 3226 pop ds 3227 cld ; Clear direction flag 3228 mov si,offset crlf_msg 3229 call writestr 3230 mov bx,sp 3231 add bx,byte 26 3232 mov si,offset regnames 3233 mov cx,2 ; 2*7 registers to dump 3234 dump_line: push cx 3235 mov cx,7 ; 7 registers per line 3236 dump_reg: push cx 3237 mov cx,4 ; 4 characters/register name 3238 wr_reg_name: lodsb 3239 call writechr 3240 loop wr_reg_name 3241 mov ax,ss:[bx] 3242 dec bx 3243 dec bx 3244 call writehex 3245 pop cx 3246 loop dump_reg 3247 mov al,0Dh ; 3248 call writechr 3249 mov al,0Ah ; 3250 call writechr 3251 pop cx 3252 loop dump_line 3253 pop gs 3254 pop fs 3255 pop es 3256 pop ds 3257 popa ; Restore the remainder 3258 popf ; Restore flags 3259 ret 3260 dumpregs endp 3261 3262 regnames db ' IP: FL: AX: CX: DX: BX: SP: BP: SI: DI: DS: ES: FS: GS:' 3263 3264 ; 3265 ; writehex: Writes a 16-bit hexadecimal number (in AX) 3266 ; 3267 writehex proc near 3268 push bx 3269 push cx 3270 mov cx,4 ; 4 numbers 3271 write_hexdig: xor bx,bx 3272 push cx 3273 mov cx,4 ; 4 bits/digit 3274 xfer_digit: shl ax,1 3275 rcl bx,1 3276 loop xfer_digit 3277 push ax 3278 mov ax,bx 3279 or al,'0' 3280 cmp al,'9' 3281 jna ok_digit 3282 add al,'A'-'0'-10 3283 ok_digit: call writechr 3284 pop ax 3285 pop cx 3286 loop write_hexdig 3287 pop cx 3288 pop bx 3289 ret 3290 writehex endp 3291 3292 debug_magic dw 0D00Dh 3293 3294 %endif ; debug 3295 ; 3296 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed 3297 ; to by ES:DI; ends on encountering any whitespace 3298 ; 3299 3300 mangle_name: 3301 00001271 B90B00 mov cx,11 ; # of bytes to write 3302 mn_loop: 3303 00001274 AC lodsb 3304 00001275 3C20 cmp al,' ' ; If control or space, end 3305 00001277 762B jna mn_end 3306 00001279 3C2E cmp al,'.' ; Period -> space-fill 3307 0000127B 740C je mn_is_period 3308 0000127D 3C61 cmp al,'a' 3309 0000127F 7220 jb mn_not_lower 3310 00001281 3C7A cmp al,'z' 3311 00001283 770F ja mn_not_uslower 3312 00001285 2C20 sub al,020h 3313 00001287 EB18 jmp short mn_not_lower 3314 00001289 B020 mn_is_period: mov al,' ' ; We need to space-fill 3315 0000128B 81F90300 mn_period_loop: cmp cx,3 ; If <= 3 characters left 3316 0000128F 76E3 jbe mn_loop ; Just ignore it 3317 00001291 AA stosb ; Otherwise, write a period 3318 00001292 E2F7 loop mn_period_loop ; Dec CX and (always) jump 3319 00001294 3C81 mn_not_uslower: cmp al,ucase_low 3320 00001296 7209 jb mn_not_lower 3321 00001298 3CA4 cmp al,ucase_high 3322 0000129A 7705 ja mn_not_lower 3323 0000129C BB[2812] mov bx,ucase_tab-ucase_low 3324 0000129F 2ED7 cs xlatb 3325 000012A1 AA mn_not_lower: stosb 3326 000012A2 E2D0 loop mn_loop ; Don't continue if too long 3327 mn_end: 3328 000012A4 B020 mov al,' ' ; Space-fill name 3329 000012A6 F3AA rep stosb ; Doesn't do anything if CX=0 3330 000012A8 C3 ret ; Done 3331 3332 ; 3333 ; Upper-case table for extended characters; this is technically code page 865, 3334 ; but code page 437 users will probably not miss not being able to use the 3335 ; cent sign in kernel images too much :-) 3336 ; 3337 ; The table only covers the range 129 to 164; the rest we can deal with. 3338 ; 3339 ucase_low equ 129 3340 ucase_high equ 164 3341 000012A9 9A90418E418F804545- ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII' 3342 000012B2 45494949 3343 000012B6 8E8F9092924F994F55- db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154 3344 000012BF 5559999A 3345 000012C3 9D9C9D9E9F41494F55- db 157, 156, 157, 158, 159, 'AIOU', 165 3346 000012CC A5 3347 3348 ; 3349 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled 3350 ; filename to the conventional representation. This is needed 3351 ; for the BOOT_IMAGE= parameter for the kernel. 3352 ; NOTE: A 13-byte buffer is mandatory, even if the string is 3353 ; known to be shorter. 3354 ; 3355 ; DS:SI -> input mangled file name 3356 ; ES:DI -> output buffer 3357 ; 3358 ; On return, DI points to the first byte after the output name, 3359 ; which is set to a null byte. 3360 ; 3361 unmangle_name: 3362 000012CD 56 push si ; Save pointer to original name 3363 000012CE B90800 mov cx,8 3364 000012D1 89FD mov bp,di 3365 000012D3 AC un_copy_body: lodsb 3366 000012D4 E82600 call lower_case 3367 000012D7 AA stosb 3368 000012D8 3C20 cmp al,' ' 3369 000012DA 7602 jbe un_cb_space 3370 000012DC 89FD mov bp,di ; Position of last nonblank+1 3371 000012DE E2F3 un_cb_space: loop un_copy_body 3372 000012E0 89EF mov di,bp 3373 000012E2 B02E mov al,'.' ; Don't save 3374 000012E4 AA stosb 3375 000012E5 B90300 mov cx,3 3376 000012E8 AC un_copy_ext: lodsb 3377 000012E9 E81100 call lower_case 3378 000012EC AA stosb 3379 000012ED 3C20 cmp al,' ' 3380 000012EF 7602 jbe un_ce_space 3381 000012F1 89FD mov bp,di 3382 000012F3 E2F3 un_ce_space: loop un_copy_ext 3383 000012F5 89EF mov di,bp 3384 000012F7 26C60500 mov byte [es:di], 0 3385 000012FB 5E pop si 3386 000012FC C3 ret 3387 3388 ; 3389 ; lower_case: Lower case a character in AL 3390 ; 3391 lower_case: 3392 000012FD 3C41 cmp al,'A' 3393 000012FF 7216 jb lc_ret 3394 00001301 3C5A cmp al,'Z' 3395 00001303 7703 ja lc_1 3396 00001305 0C20 or al,20h 3397 00001307 C3 ret 3398 00001308 3C80 lc_1: cmp al,lcase_low 3399 0000130A 720B jb lc_ret 3400 0000130C 3CA5 cmp al,lcase_high 3401 0000130E 7707 ja lc_ret 3402 00001310 53 push bx 3403 00001311 BB[9812] mov bx,lcase_tab-lcase_low 3404 00001314 2ED7 cs xlatb 3405 00001316 5B pop bx 3406 00001317 C3 lc_ret: ret 3407 3408 ; 3409 ; Lower-case table for codepage 865 3410 ; 3411 lcase_low equ 128 3412 lcase_high equ 165 3413 00001318 878182838485868788- lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138 3414 00001321 898A 3415 00001323 8B8C8D848682919193- db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149 3416 0000132C 9495 3417 0000132E 96979894819B9C9B9E- db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160 3418 00001337 9FA0 3419 00001339 A1A2A3A4A4 db 161, 162, 163, 164, 164 3420 ; 3421 ; Various initialized or semi-initialized variables 3422 ; 3423 0000133E 20436F707972696768- copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin' 3424 00001347 742028432920313939- 3425 00001350 342D3139393820482E- 3426 00001359 20506574657220416E- 3427 00001362 76696E 3428 00001365 0D0A00 db 0Dh, 0Ah, 0 3429 00001368 626F6F743A2000 boot_prompt db 'boot: ',0 3430 0000136F 08200800 wipe_char db 08h, ' ', 08h, 0 3431 00001373 436F756C64206E6F74- err_notfound db 'Could not find kernel image: ',0 3432 0000137C 2066696E64206B6572- 3433 00001385 6E656C20696D616765- 3434 0000138E 3A2000 3435 00001391 0D0A496E76616C6964- err_notkernel db 0Dh, 0Ah, 'Invalid or corrupt kernel image.', 0Dh, 0Ah, 0 3436 0000139A 206F7220636F727275- 3437 000013A3 7074206B65726E656C- 3438 000013AC 20696D6167652E0D0A- 3439 000013B5 00 3440 000013B6 497420617070656172- err_not386 db 'It appears your computer uses a 286 or lower CPU.' 3441 000013BF 7320796F757220636F- 3442 000013C8 6D7075746572207573- 3443 000013D1 657320612032383620- 3444 000013DA 6F72206C6F77657220- 3445 000013E3 4350552E 3446 000013E7 0D0A db 0Dh, 0Ah 3447 000013E9 596F752063616E6E6F- db 'You cannot run Linux unless you have a 386 or higher CPU' 3448 000013F2 742072756E204C696E- 3449 000013FB 757820756E6C657373- 3450 00001404 20796F752068617665- 3451 0000140D 206120333836206F72- 3452 00001416 206869676865722043- 3453 0000141F 5055 3454 00001421 0D0A db 0Dh, 0Ah 3455 00001423 696E20796F7572206D- db 'in your machine. If you get this message in error, hold' 3456 0000142C 616368696E652E2020- 3457 00001435 496620796F75206765- 3458 0000143E 742074686973206D65- 3459 00001447 737361676520696E20- 3460 00001450 6572726F722C20686F- 3461 00001459 6C64 3462 0000145B 0D0A db 0Dh, 0Ah 3463 0000145D 646F776E2074686520- db 'down the Ctrl key while booting, and I will take your' 3464 00001466 4374726C206B657920- 3465 0000146F 7768696C6520626F6F- 3466 00001478 74696E672C20616E64- 3467 00001481 20492077696C6C2074- 3468 0000148A 616B6520796F7572 3469 00001492 0D0A db 0Dh, 0Ah 3470 00001494 776F726420666F7220- db 'word for it.', 0Dh, 0Ah, 0 3471 0000149D 69742E0D0A00 3472 000014A3 497420617070656172- err_noram db 'It appears your computer has less than 608K of low ("DOS")' 3473 000014AC 7320796F757220636F- 3474 000014B5 6D7075746572206861- 3475 000014BE 73206C657373207468- 3476 000014C7 616E203630384B206F- 3477 000014D0 66206C6F7720282244- 3478 000014D9 4F532229 3479 000014DD 0D0A db 0Dh, 0Ah 3480 000014DF 52414D2E20204C696E- db 'RAM. Linux needs at least this amount to boot. If you get' 3481 000014E8 7578206E6565647320- 3482 000014F1 6174206C6561737420- 3483 000014FA 7468697320616D6F75- 3484 00001503 6E7420746F20626F6F- 3485 0000150C 742E2020496620796F- 3486 00001515 7520676574 3487 0000151A 0D0A db 0Dh, 0Ah 3488 0000151C 74686973206D657373- db 'this message in error, hold down the Ctrl key while' 3489 00001525 61676520696E206572- 3490 0000152E 726F722C20686F6C64- 3491 00001537 20646F776E20746865- 3492 00001540 204374726C206B6579- 3493 00001549 207768696C65 3494 0000154F 0D0A db 0Dh, 0Ah 3495 00001551 626F6F74696E672C20- db 'booting, and I will take your word for it.', 0Dh, 0Ah, 0 3496 0000155A 616E6420492077696C- 3497 00001563 6C2074616B6520796F- 3498 0000156C 757220776F72642066- 3499 00001575 6F722069742E0D0A00 3500 0000157E 556E6B6E6F776E206B- err_badcfg db 'Unknown keyword in syslinux.cfg.', 0Dh, 0Ah, 0 3501 00001587 6579776F726420696E- 3502 00001590 207379736C696E7578- 3503 00001599 2E6366672E0D0A00 3504 000015A1 4D697373696E672070- err_noparm db 'Missing parameter in syslinux.cfg.', 0Dh, 0Ah, 0 3505 000015AA 6172616D6574657220- 3506 000015B3 696E207379736C696E- 3507 000015BC 75782E6366672E0D0A- 3508 000015C5 00 3509 000015C6 0D0A436F756C64206E- err_noinitrd db 0Dh, 0Ah, 'Could not find ramdisk image: ', 0 3510 000015CF 6F742066696E642072- 3511 000015D8 616D6469736B20696D- 3512 000015E1 6167653A2000 3513 000015E7 4E6F7420656E6F7567- err_nohighmem db 'Not enough memory to load specified kernel.', 0Dh, 0Ah, 0 3514 000015F0 68206D656D6F727920- 3515 000015F9 746F206C6F61642073- 3516 00001602 706563696669656420- 3517 0000160B 6B65726E656C2E0D0A- 3518 00001614 00 3519 00001615 0D0A4B65726E656C20- err_highload db 0Dh, 0Ah, 'Kernel transfer failure.', 0Dh, 0Ah, 0 3520 0000161E 7472616E7366657220- 3521 00001627 6661696C7572652E0D- 3522 00001630 0A00 3523 00001632 43616E6E6F74206C6F- err_oldkernel db 'Cannot load a ramdisk with an old kernel image.' 3524 0000163B 616420612072616D64- 3525 00001644 69736B207769746820- 3526 0000164D 616E206F6C64206B65- 3527 00001656 726E656C20696D6167- 3528 0000165F 652E 3529 00001661 0D0A00 db 0Dh, 0Ah, 0 3530 00001664 3A20617474656D7074- err_notdos db ': attempted DOS system call', 0Dh, 0Ah, 0 3531 0000166D 656420444F53207379- 3532 00001676 7374656D2063616C6C- 3533 0000167F 0D0A00 3534 00001682 434F4D424F4F542069- err_comlarge db 'COMBOOT image too large.', 0Dh, 0Ah, 0 3535 0000168B 6D61676520746F6F20- 3536 00001694 6C617267652E0D0A00 3537 0000169D 496E76616C6964206F- err_bootsec db 'Invalid or corrupt boot sector image.', 0Dh, 0Ah, 0 3538 000016A6 7220636F7272757074- 3539 000016AF 20626F6F7420736563- 3540 000016B8 746F7220696D616765- 3541 000016C1 2E0D0A00 3542 000016C5 0D0A41323020676174- err_a20 db 0Dh, 0Ah, 'A20 gate not responding!', 0Dh, 0Ah, 0 3543 000016CE 65206E6F7420726573- 3544 000016D7 706F6E64696E67210D- 3545 000016E0 0A00 3546 000016E2 4C6F6164696E672000 loading_msg db 'Loading ', 0 3547 000016EB 2E dotdot_msg db '.' 3548 000016EC 2E00 dot_msg db '.', 0 3549 000016EE 2061626F727465642E aborted_msg db ' aborted.' ; Fall through to crlf_msg! 3550 000016F7 0D0A00 crlf_msg db 0Dh, 0Ah, 0 3551 000016FA 5359534C494E555843- syslinux_cfg db 'SYSLINUXCFG' 3552 00001703 4647 3553 ; 3554 ; Command line options we'd like to take a look at 3555 ; 3556 ; mem= and vga= are handled as normal 32-bit integer values 3557 00001705 696E697472643D initrd_cmd db 'initrd=' 3558 initrd_cmd_len equ 7 3559 ; 3560 ; Config file keyword table 3561 ; 3562 align 2 3563 0000170C 6170 keywd_table db 'ap' ; append 3564 0000170E 6465 db 'de' ; default 3565 00001710 7469 db 'ti' ; timeout 3566 00001712 666F db 'fo' ; font 3567 00001714 6B62 db 'kb' ; kbd 3568 00001716 6469 db 'di' ; display 3569 00001718 7072 db 'pr' ; prompt 3570 0000171A 6C61 db 'la' ; label 3571 0000171C 696D db 'im' ; implicit 3572 0000171E 6B65 db 'ke' ; kernel 3573 00001720 6631 db 'f1' ; F1 3574 00001722 6632 db 'f2' ; F2 3575 00001724 6633 db 'f3' ; F3 3576 00001726 6634 db 'f4' ; F4 3577 00001728 6635 db 'f5' ; F5 3578 0000172A 6636 db 'f6' ; F6 3579 0000172C 6637 db 'f7' ; F7 3580 0000172E 6638 db 'f8' ; F8 3581 00001730 6639 db 'f9' ; F9 3582 00001732 6630 db 'f0' ; F10 3583 00001734 0000 dw 0 3584 ; 3585 ; Extensions to search for (in *reverse* order). Note that the last 3586 ; (lexically first) entry in the table is a placeholder for the original 3587 ; extension, needed for error messages. The exten_table is shifted so 3588 ; the table is 1-based; this is because a "loop" cx is used as index. 3589 ; 3590 exten_table: 3591 00001736 00000000 OrigKernelExt: dd 0 ; Original extension 3592 0000173A 434F4D00 db 'COM',0 ; COMBOOT (same as DOS) 3593 0000173E 42532000 db 'BS ',0 ; Boot Sector 3594 00001742 42535300 db 'BSS',0 ; Boot Sector (add superblock) 3595 00001746 43425400 db 'CBT',0 ; COMBOOT (specific) 3596 3597 exten_count equ (($-exten_table) >> 2) - 1 ; Number of alternates 3598 ; 3599 ; Misc initialized (data) variables 3600 ; 3601 0000174A 0000 AppendLen dw 0 ; Bytes in append= command 3602 0000174C 0000 KbdTimeOut dw 0 ; Keyboard timeout (if any) 3603 0000174E 0000 FKeyMap dw 0 ; Bitmap for F-keys loaded 3604 00001750 0080 CmdLinePtr dw cmd_line_here ; Command line advancing pointer 3605 initrd_flag equ $ 3606 00001752 0000 initrd_ptr dw 0 ; Initial ramdisk pointer/flag 3607 00001754 0000 VKernelCtr dw 0 ; Number of registered vkernels 3608 00001756 0000 ForcePrompt dw 0 ; Force prompt 3609 00001758 0100 AllowImplicit dw 1 ; Allow implicit kernels 3610 ; 3611 ; Stuff for the command line; we do some trickery here with equ to avoid 3612 ; tons of zeros appended to our file and wasting space 3613 ; 3614 0000175A 6C696E757820 linuxauto_cmd db 'linux ' 3615 00001760 6175746F00 auto_cmd db 'auto',0 3616 linuxauto_len equ $-linuxauto_cmd 3617 auto_len equ $-auto_cmd 3618 00001765 424F4F545F494D4147- boot_image db 'BOOT_IMAGE=' 3619 0000176E 453D 3620 boot_image_len equ $-boot_image 3621 align 4, db 0 ; For the good of REP MOVSD 3622 command_line equ $ 3623 default_cmd equ $+(max_cmd_len+2) 3624 ldlinux_end equ default_cmd+(max_cmd_len+1) 3625 kern_cmd_len equ ldlinux_end-command_line 3626 ldlinux_len equ ldlinux_end-ldlinux_magic 3627 ; 3628 ; Put the getcbuf right after the code, aligned on a sector boundary 3629 ; 3630 end_of_code equ (ldlinux_end-bootsec)+7C00h 3631 getcbuf equ (end_of_code + 511) & 0FE00h