1 ; -*- fundamental -*- (asm-mode sucks) 2 ; $Id: ldlinux.asm,v 1.46 1999/03/19 21:08: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-1999 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 '1999' 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 2FBDF236 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 5820312E3433203139- 822 00000212 39392D30332D313920- 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 2FBDF236 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 B84917 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[3513] 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[AD13] 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[9A14] 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[CB0D]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 E8BE0A 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[5217] mov si,linuxauto_cmd ; Default command: "linux auto" 1230 0000044E BF[6918] 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[F116] mov di,syslinux_cfg 1245 00000466 E8150C call open 1246 00000469 0F849501 jz near no_config_file 1247 parse_config: 1248 0000046D E8AA0C 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[6918] pc_default: mov di,default_cmd ; "default" command 1275 000004BE E86A0D call getline 1276 000004C1 BE[5817] 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[4C17]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 E8530D call getline 1285 000004D8 81EF0062 sub di,AppendBuf 1286 000004DC 893E[4217] 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 E8430D 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[4C17]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 E81A0D call getline 1304 00000511 5E pop si 1305 00000512 BF0B60 mov di,VKernelBuf+vk_rname 1306 00000515 E8500D call mangle_name 1307 00000518 EB1A jmp short parse_config_2 1308 1309 0000051A E8590C 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[4417] 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 E84E0A call get_msg_file ; Load and display file 1320 00000534 E936FF parse_config_2: jmp parse_config 1321 1322 00000537 E83C0C pc_prompt: call getint ; "prompt" command 1323 0000053A 72F8 jc parse_config_2 1324 0000053C 891E[4E17] mov [ForcePrompt],bx 1325 00000540 EBF2 jmp short parse_config_2 1326 1327 00000542 E8310C pc_implicit: call getint ; "implicit" command 1328 00000545 72ED jc parse_config_2 1329 00000547 891E[5017] 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[4617] or [FKeyMap], ax ; Mark that we have this loaded 1341 00000562 BF0010 mov di,trackbuf 1342 00000565 57 push di 1343 00000566 E8C20C 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 E8F30C 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 E8AA0C call getline 1355 00000581 5E pop si 1356 00000582 BF0060 mov di,VKernelBuf+vk_vname 1357 00000585 E8E00C call mangle_name ; Mangle virtual name 1358 00000588 FF06[4C17] 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[4217] 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 E87309 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 E8A809 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 E8660C call getline 1387 000005C5 5E pop si 1388 000005C6 BF2465 mov di,MNameBuf 1389 000005C9 57 push di 1390 000005CA E89B0C call mangle_name 1391 000005CD 5F pop di 1392 000005CE E9AB08 jmp searchdir ; Tailcall 1393 1394 ; 1395 ; commit_vk: Store the current VKernelBuf into buffer segment 1396 ; 1397 commit_vk: 1398 000005D1 833E[4C17]00 cmp word [VKernelCtr],byte 0 1399 000005D6 741F je cvk_ret ; No VKernel = return 1400 000005D8 813E[4C17]8000 cmp word [VKernelCtr],max_vk ; Above limit? 1401 000005DE 7718 ja cvk_overflow 1402 000005E0 8B3E[4C17] 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[4C17]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[4E17]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[5F13] mov si,boot_prompt 1433 00000615 E83DFB call writestr 1434 1435 00000618 BF[6817] 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[4417] 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[6817] cmp di,command_line ; Space must not be first 1477 0000065F 74E8 je get_char 1478 00000661 81FF[6718] 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 E89408 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[6817] 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[6613] 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[4617] 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 E8D407 call searchdir 1508 000006A8 7405 jz fk_nofile 1509 000006AA E8D508 call get_msg_file 1510 000006AD EB06 jmp short fk_wrcmd 1511 fk_nofile: 1512 000006AF BE[EE16] mov si,crlf_msg 1513 000006B2 E8A0FA call writestr 1514 fk_wrcmd: 1515 000006B5 BE[5F13] 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[6817] 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[6918] mov si,default_cmd 1526 000006CC BF[6817] 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[EE16] mov si,crlf_msg 1532 000006DA E878FA call writestr 1533 000006DD 81FF[6817] 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[6817] mov si,command_line 1543 000006E9 BFD064 mov di,KernelName 1544 000006EC 56 push si 1545 000006ED 57 push di 1546 000006EE E8770B 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[4C17] 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[5017]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[4217] mov cx,[AppendLen] 1594 00000741 F3A4 rep movsb 1595 00000743 893E[4817] 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[2E17] mov [OrigKernelExt],eax 1606 0000075A 53 .search_loop: push bx 1607 0000075B BFD064 mov di,KernelName ; Search on disk 1608 0000075E E81B07 call searchdir 1609 00000761 5B pop bx 1610 00000762 756C jnz kernel_good 1611 00000764 668B87[2E17] 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 E8480B call unmangle_name ; Get human form 1620 0000077C BE[6A13] 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[EE16] mov si,crlf_msg 1625 00000789 E9DB06 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 E82F0B 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[4817] 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[8813] kernel_corrupt: mov si,err_notkernel 1664 000007CD E99706 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 E8EA0A 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[D916] 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 E8F705 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[4817] mov di,[CmdLinePtr] 1804 000008C4 BE[5D17] 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[4A17]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[FC16] 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 E84909 call mangle_name ; Mangle ramdisk name 1856 0000091F 5E pop si 1857 00000920 26803E2F6520 cmp byte [es:InitRD],' ' ; Null filename? 1858 00000926 260F9706[4A17] 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 E82F08 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 E82108 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[4A17]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 E8F408 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 E89E04 call searchdir ; Look for it in directory 1926 000009DE 07 pop es 1927 000009DF 7443 jz initrd_notthere 1928 000009E1 8936[4A17] 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 E8AC03 call loadinitrd ; Load initial ramdisk 1947 00000A22 EB18 jmp short initrd_end 1948 1949 initrd_notthere: 1950 00000A24 BE[BD15] mov si,err_noinitrd 1951 00000A27 E82BF7 call writestr 1952 00000A2A BE4765 mov si,InitRDCName 1953 00000A2D E825F7 call writestr 1954 00000A30 BE[EE16] mov si,crlf_msg 1955 00000A33 E93104 jmp abort_load 1956 1957 00000A36 BE[DE15] no_high_mem: mov si,err_nohighmem ; Error routine 1958 00000A39 E92B04 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[E216] 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[E316] mov si,dot_msg ; Progress report 2001 00000A9C E80E02 call cwritestr 2002 00000A9F E8AB03 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[E316] 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 E85D03 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[EE16] 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[4A17]01 test byte [initrd_flag],1 ; Old kernel can't have initrd 2135 00000B84 7406 jz load_old_kernel 2136 00000B86 BE[2916] mov si,err_oldkernel 2137 00000B89 E9DB02 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[7916] 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[5B16] 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[9416] 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 E85300 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 E85D00 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 call _io_delay 2394 %define IO_DELAY_PORT 80h ; Invalid port (we hope!) 2395 %define delaytime 1024 ; 4 x ISA bus cycles (@ 1.5 µs) 2396 2397 00000D35 50 _io_delay: push ax 2398 00000D36 E580 in ax,IO_DELAY_PORT 2399 00000D38 E580 in ax,IO_DELAY_PORT 2400 00000D3A E580 in ax,IO_DELAY_PORT 2401 00000D3C E580 in ax,IO_DELAY_PORT 2402 00000D3E 58 pop ax 2403 00000D3F C3 ret 2404 2405 enable_a20: 2406 00000D40 36C6062365FF mov byte [ss:A20Tries],255 ; Times to try to make this work 2407 2408 try_enable_a20: 2409 ; 2410 ; Flush the caches 2411 ; 2412 00000D46 E88200 call try_wbinvd 2413 2414 ; 2415 ; Enable the "fast A20 gate" 2416 ; 2417 00000D49 E492 in al, 092h 2418 00000D4B 0C02 or al,02h 2419 00000D4D E692 out 092h, al 2420 ; 2421 ; Enable the keyboard controller A20 gate 2422 ; 2423 00000D4F E86100 call empty_8042 2424 00000D52 B0D1 mov al,0D1h ; Command write 2425 00000D54 E664 out 064h, al 2426 00000D56 E85A00 call empty_8042 2427 00000D59 B0DF mov al,0DFh ; A20 on 2428 00000D5B E660 out 060h, al 2429 00000D5D E84500 call kbc_delay 2430 ; Verify that A20 actually is enabled. Do that by 2431 ; observing a word in low memory and the same word in 2432 ; the HMA until they are no longer coherent. Note that 2433 ; we don't do the same check in the disable case, because 2434 ; we don't want to *require* A20 masking (SYSLINUX should 2435 ; work fine without it, if the BIOS does.) 2436 00000D60 06 push es 2437 00000D61 51 push cx 2438 00000D62 B9FFFF mov cx,0FFFFh ; Times to try, also HMA 2439 00000D65 8EC1 mov es,cx ; HMA = segment 0FFFFh 2440 00000D67 36FF061865 .a20_wait: inc word [ss:A20Test] 2441 00000D6C E85C00 call try_wbinvd 2442 00000D6F 26A12865 mov ax,[es:A20Test+10h] 2443 00000D73 363B061865 cmp ax,[ss:A20Test] 2444 00000D78 7511 jne .a20_done 2445 00000D7A E2EB loop .a20_wait 2446 ; 2447 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up 2448 ; and report failure to the user. 2449 ; 2450 00000D7C 59 pop cx 2451 00000D7D 07 pop es 2452 2453 00000D7E 36FE0E2365 dec byte [ss:A20Tries] 2454 00000D83 75C1 jnz try_enable_a20 2455 2456 00000D85 BE[BC16] mov si, err_a20 2457 00000D88 E9DC00 jmp abort_load 2458 ; 2459 ; A20 unmasked, proceed... 2460 ; 2461 00000D8B 59 .a20_done: pop cx 2462 00000D8C 07 pop es 2463 00000D8D C3 ret 2464 2465 disable_a20: 2466 ; 2467 ; Flush the caches 2468 ; 2469 00000D8E E83A00 call try_wbinvd 2470 ; 2471 ; Disable the "fast A20 gate" 2472 ; 2473 00000D91 E492 in al, 092h 2474 00000D93 24FD and al,~02h 2475 00000D95 E692 out 092h, al 2476 ; 2477 ; Disable the keyboard controller A20 gate 2478 ; 2479 00000D97 E81900 call empty_8042 2480 00000D9A B0D1 mov al,0D1h 2481 00000D9C E664 out 064h, al ; Command write 2482 00000D9E E81200 call empty_8042 2483 00000DA1 B0DD mov al,0DDh ; A20 off 2484 00000DA3 E660 out 060h, al 2485 ; Fall through/tailcall to kbc_delay 2486 2487 00000DA5 E80B00 kbc_delay: call empty_8042 2488 00000DA8 51 push cx 2489 00000DA9 B90004 mov cx, delaytime 2490 00000DAC E886FF .delayloop: io_delay 2491 00000DAF E2FB loop .delayloop 2492 00000DB1 59 pop cx 2493 00000DB2 C3 ret 2494 2495 empty_8042: 2496 00000DB3 E87FFF io_delay 2497 00000DB6 E464 in al, 064h ; Status port 2498 00000DB8 A801 test al,1 2499 00000DBA 7407 jz .no_output 2500 00000DBC E876FF io_delay 2501 00000DBF E460 in al, 060h ; Read input 2502 00000DC1 EBF0 jmp short empty_8042 2503 .no_output: 2504 00000DC3 A802 test al,2 2505 00000DC5 75EC jnz empty_8042 2506 00000DC7 E86BFF io_delay 2507 00000DCA C3 ret 2508 2509 ; 2510 ; WBINVD instruction; gets auto-eliminated on 386 CPUs 2511 ; 2512 try_wbinvd: 2513 00000DCB 0F09 wbinvd 2514 00000DCD C3 ret 2515 2516 ; 2517 ; Load RAM disk into high memory 2518 ; 2519 loadinitrd: 2520 00000DCE 06 push es ; Save ES on entry 2521 00000DCF B80090 mov ax,real_mode_seg 2522 00000DD2 8EC0 mov es,ax 2523 00000DD4 8B36[4A17] mov si,[initrd_ptr] 2524 00000DD8 668B3EC064 mov edi,[InitRDat] ; initrd load address 2525 00000DDD 2666893E1802 mov [es:su_ramdiskat],edi ; Offset for ram disk 2526 00000DE3 56 push si 2527 00000DE4 BE4765 mov si,InitRDCName ; Write ramdisk name 2528 00000DE7 E8C3FE call cwritestr 2529 00000DEA BE[E216] mov si,dotdot_msg ; Write dots 2530 00000DED E8BDFE call cwritestr 2531 rd_load_loop: 2532 00000DF0 BE[E316] mov si,dot_msg ; Progress report 2533 00000DF3 E8B7FE call cwritestr 2534 00000DF6 5E pop si ; Restore cluster pointer 2535 00000DF7 E85300 call abort_check 2536 00000DFA 8B0E0265 mov cx,[InitRDClust] 2537 00000DFE 3B0E0465 cmp cx,[ClustPerMoby] 2538 00000E02 7604 jna rd_last_moby 2539 00000E04 8B0E0465 mov cx,[ClustPerMoby] 2540 rd_last_moby: 2541 00000E08 290E0265 sub [InitRDClust],cx 2542 00000E0C 31DB xor bx,bx ; Load at offset 0 2543 00000E0E 680070 push word xfer_buf_seg ; Bounce buffer segment 2544 00000E11 07 pop es 2545 00000E12 51 push cx 2546 00000E13 E815F5 call getfssec 2547 00000E16 59 pop cx 2548 00000E17 56 push si ; Save cluster pointer 2549 00000E18 66BE00000700 mov esi,(xfer_buf_seg << 4) 2550 00000E1E 668B3EC064 mov edi,[InitRDat] 2551 00000E23 66B900400000 mov ecx,4000h ; Copy 64K 2552 00000E29 E8B4FE call bcopy ; Does not change flags!! 2553 00000E2C 7210 jc rd_load_done ; EOF? 2554 00000E2E 668106C06400000100 add dword [InitRDat],10000h ; Point to next 64K 2555 00000E37 833E026500 cmp word [InitRDClust],byte 0 ; Are we done? 2556 00000E3C 75B2 jne rd_load_loop ; Apparently not 2557 rd_load_done: 2558 00000E3E 5E pop si ; Clean up the stack 2559 00000E3F BE[EE16] mov si,crlf_msg 2560 00000E42 E810F3 call writestr 2561 00000E45 BE[D916] mov si,loading_msg ; Write new "Loading " for 2562 00000E48 E80AF3 call writestr ; the benefit of the kernel 2563 00000E4B 07 pop es ; Restore original ES 2564 00000E4C C3 ret 2565 2566 ; 2567 ; abort_check: let the user abort with or 2568 ; 2569 abort_check: 2570 00000E4D 60 pusha 2571 ac1: 2572 00000E4E B401 mov ah,1 ; Check for pending keystroke 2573 00000E50 CD16 int 16h 2574 00000E52 7426 jz ac_ret ; If no pending keystroke 2575 00000E54 31C0 xor ax,ax ; Load pending keystroke 2576 00000E56 CD16 int 16h 2577 00000E58 BB0063 mov bx,KbdMap 2578 00000E5B D7 xlatb 2579 00000E5C 3C1B cmp al,27 ; aborts (DOS geeks) 2580 00000E5E 7404 je ac2 2581 00000E60 3C03 cmp al,3 ; So does Ctrl-C (UNIX geeks) 2582 00000E62 75EA jne ac1 ; Unknown key... try again 2583 ac2: ; If we get here, ABORT! 2584 00000E64 BE[E516] mov si,aborted_msg 2585 ; Fall through to abort_load 2586 ; 2587 ; abort_load: Called by various routines which wants to print a fatal 2588 ; error message and return to the command prompt. Since this 2589 ; may happen at just about any stage of the boot process, assume 2590 ; our state is messed up, and just reset the segment registers 2591 ; and the stack forcibly. 2592 ; 2593 ; SI = offset (in _text) of error message to print 2594 ; 2595 abort_load: 2596 00000E67 8CC8 mov ax,cs ; Restore CS = DS = ES 2597 00000E69 8ED8 mov ds,ax 2598 00000E6B 8EC0 mov es,ax 2599 00000E6D FA cli 2600 00000E6E BCFA5F mov sp,StackBuf-2*3 ; Reset stack 2601 00000E71 8ED0 mov ss,ax ; Just in case... 2602 00000E73 FB sti 2603 00000E74 E8DEF2 call writestr ; Expects SI -> error msg 2604 00000E77 E998F7 al_ok: jmp enter_command ; Return to command prompt 2605 ; 2606 ; End of abort_check 2607 ; 2608 00000E7A 61 ac_ret: popa 2609 00000E7B C3 ret 2610 2611 ; 2612 ; searchdir: Search the root directory for a pre-mangled filename in 2613 ; DS:DI. This routine is similar to the one in the boot 2614 ; sector, but is a little less Draconian when it comes to 2615 ; error handling, plus it reads the root directory in 2616 ; larger chunks than a sector at a time (which is probably 2617 ; a waste of coding effort, but I like to do things right). 2618 ; 2619 ; FIXME: usually we can load the entire root dir in memory, 2620 ; and files are usually at the beginning anyway. It probably 2621 ; would be worthwhile to remember if we have the first chunk 2622 ; in memory and skip the load if that (it would speed up online 2623 ; help, mainly.) 2624 ; 2625 ; NOTE: This file considers finding a zero-length file an 2626 ; error. This is so we don't have to deal with that special 2627 ; case elsewhere in the program (most loops have the test 2628 ; at the end). 2629 ; 2630 ; If successful: 2631 ; ZF clear 2632 ; SI = cluster # for the first cluster 2633 ; DX:AX = file length in bytes 2634 ; If unsuccessful 2635 ; ZF set 2636 ; 2637 2638 searchdir: 2639 00000E7C A1[1100] mov ax,[bsRootDirEnts] 2640 00000E7F A3EA64 mov [DirScanCtr],ax 2641 00000E82 A1E864 mov ax,[RootDirSize] 2642 00000E85 A3EC64 mov [DirBlocksLeft],ax 2643 00000E88 A1DC64 mov ax,[RootDir1] 2644 00000E8B 8B16DE64 mov dx,[RootDir2] 2645 scan_group: 2646 00000E8F 8B2EEC64 mov bp,[DirBlocksLeft] 2647 00000E93 21ED and bp,bp 2648 00000E95 7467 jz dir_return 2649 00000E97 3B2EFA64 cmp bp,[BufSafeSec] 2650 00000E9B 7604 jna load_last 2651 00000E9D 8B2EFA64 mov bp,[BufSafeSec] 2652 load_last: 2653 00000EA1 292EEC64 sub [DirBlocksLeft],bp 2654 00000EA5 50 push ax 2655 00000EA6 52 push dx 2656 00000EA7 A1[0B00] mov ax,[bsBytesPerSec] 2657 00000EAA F7E5 mul bp 2658 00000EAC 05E10F add ax,trackbuf-31 2659 00000EAF A3EE64 mov [EndofDirSec],ax ; End of loaded 2660 00000EB2 5A pop dx 2661 00000EB3 58 pop ax 2662 00000EB4 55 push bp ; Save number of sectors 2663 00000EB5 50 push ax ; Save present location 2664 00000EB6 52 push dx 2665 00000EB7 57 push di ; Save name 2666 00000EB8 BB0010 mov bx,trackbuf 2667 00000EBB E8B2F2 call getlinsec 2668 00000EBE 5F pop di 2669 00000EBF 5A pop dx 2670 00000EC0 58 pop ax 2671 00000EC1 5D pop bp 2672 00000EC2 BE0010 mov si,trackbuf 2673 00000EC5 803C00 dir_test_name: cmp byte [si],0 ; Directory high water mark 2674 00000EC8 7434 je dir_return ; Failed 2675 00000ECA F6440B18 test byte [si+11],18h ; Check it really is a file 2676 00000ECE 750B jnz dir_not_this 2677 00000ED0 57 push di 2678 00000ED1 56 push si 2679 00000ED2 B90B00 mov cx,11 ; Filename = 11 bytes 2680 00000ED5 F3A6 repe cmpsb 2681 00000ED7 5E pop si 2682 00000ED8 5F pop di 2683 00000ED9 7416 je dir_success 2684 00000EDB 83C620 dir_not_this: add si,byte 32 2685 00000EDE FF0EEA64 dec word [DirScanCtr] 2686 00000EE2 741A jz dir_return ; Out of it... 2687 00000EE4 3B36EE64 cmp si,[EndofDirSec] 2688 00000EE8 72DB jb dir_test_name 2689 00000EEA 01E8 add ax,bp ; Increment linear sector number 2690 00000EEC 83D200 adc dx,byte 0 2691 00000EEF EB9E jmp short scan_group 2692 dir_success: 2693 00000EF1 8B441C mov ax,[si+28] ; Length of file 2694 00000EF4 8B541E mov dx,[si+30] 2695 00000EF7 8B741A mov si,[si+26] ; Cluster pointer 2696 00000EFA 89C3 mov bx,ax 2697 00000EFC 09D3 or bx,dx ; Sets ZF iff DX:AX is zero 2698 dir_return: 2699 00000EFE C3 ret 2700 2701 ; 2702 ; writechr: Write a single character in AL to the screen without 2703 ; mangling any registers 2704 ; 2705 writechr: 2706 00000EFF 60 pusha 2707 00000F00 B40E mov ah,0Eh 2708 00000F02 BB0700 mov bx,0007h ; white text on this page 2709 00000F05 CD10 int 10h 2710 00000F07 61 popa 2711 00000F08 C3 ret 2712 2713 ; 2714 ; adjust_screen: Set the internal variables associated with the screen size. 2715 ; This is a subroutine in case we're loading a custom font. 2716 ; 2717 adjust_screen: 2718 00000F09 A08404 mov al,[BIOS_vidrows] 2719 00000F0C 20C0 and al,al 2720 00000F0E 7502 jnz vidrows_is_ok 2721 00000F10 B018 mov al,24 ; No vidrows in BIOS, assume 25 2722 ; (Remember: vidrows == rows-1) 2723 00000F12 A21F65 vidrows_is_ok: mov [VidRows],al 2724 00000F15 B40F mov ah,0fh 2725 00000F17 CD10 int 10h ; Read video state 2726 00000F19 883E1B65 mov [TextPage],bh 2727 00000F1D FECC dec ah ; Store count-1 (same as rows) 2728 00000F1F 88261E65 mov [VidCols],ah 2729 00000F23 C3 bf_ret: ret 2730 2731 ; 2732 ; loadfont: Load a .psf font file and install it onto the VGA console 2733 ; (if we're not on a VGA screen then ignore.) It is called with 2734 ; SI and DX:AX set by routine searchdir 2735 ; 2736 loadfont: 2737 00000F24 BB0010 mov bx,trackbuf ; The trackbuf is >= 16K; the part 2738 00000F27 8B0EF864 mov cx,[BufSafe] ; of a PSF file we care about is no 2739 00000F2B E8FDF3 call getfssec ; more than 8K+4 bytes 2740 2741 00000F2E A10010 mov ax,[trackbuf] ; Magic number 2742 00000F31 3D3604 cmp ax,0436h 2743 00000F34 75ED jne bf_ret 2744 2745 00000F36 A00210 mov al,[trackbuf+2] ; File mode 2746 00000F39 3C03 cmp al,3 ; Font modes 0-3 supported 2747 00000F3B 77E6 ja bf_ret 2748 2749 00000F3D 8A3E0310 mov bh,byte [trackbuf+3] ; Height of font 2750 00000F41 80FF02 cmp bh,2 ; VGA minimum 2751 00000F44 72DD jb bf_ret 2752 00000F46 80FF20 cmp bh,32 ; VGA maximum 2753 00000F49 77D8 ja bf_ret 2754 2755 00000F4B BD0410 mov bp,trackbuf+4 ; Address of font data 2756 00000F4E 30DB xor bl,bl 2757 00000F50 B90001 mov cx,256 2758 00000F53 31D2 xor dx,dx 2759 00000F55 B81011 mov ax,1110h 2760 00000F58 CD10 int 10h ; Load into VGA RAM 2761 2762 00000F5A 30DB xor bl,bl 2763 00000F5C B80311 mov ax,1103h ; Select page 0 2764 00000F5F CD10 int 10h 2765 2766 00000F61 EBA6 jmp short adjust_screen 2767 2768 ; 2769 ; loadkeys: Load a LILO-style keymap; SI and DX:AX set by searchdir 2770 ; 2771 loadkeys: 2772 00000F63 21D2 and dx,dx ; Should be 256 bytes exactly 2773 00000F65 751A jne loadkeys_ret 2774 00000F67 3D0001 cmp ax,256 2775 00000F6A 7515 jne loadkeys_ret 2776 2777 00000F6C BB0010 mov bx,trackbuf 2778 00000F6F B90100 mov cx,1 ; 1 cluster should be >= 256 bytes 2779 00000F72 E8B6F3 call getfssec 2780 2781 00000F75 BE0010 mov si,trackbuf 2782 00000F78 BF0063 mov di,KbdMap 2783 00000F7B B94000 mov cx,256 >> 2 2784 00000F7E F366A5 rep movsd 2785 2786 00000F81 C3 loadkeys_ret: ret 2787 2788 ; 2789 ; get_msg_file: Load a text file and write its contents to the screen, 2790 ; interpreting color codes. Is called with SI and DX:AX 2791 ; set by routine searchdir 2792 ; 2793 get_msg_file: 2794 00000F82 C7061265[D60F] mov word [NextCharJump],msg_putchar ; State machine for color 2795 00000F88 C6061A6507 mov byte [TextAttribute],07h ; Default grey on white 2796 00000F8D 60 pusha 2797 00000F8E 8A3E1B65 mov bh,[TextPage] 2798 00000F92 B403 mov ah,03h ; Read cursor position 2799 00000F94 CD10 int 10h 2800 00000F96 89161C65 mov [CursorDX],dx 2801 00000F9A 61 popa 2802 00000F9B 50 get_msg_chunk: push ax ; DX:AX = length of file 2803 00000F9C 52 push dx 2804 00000F9D BB0010 mov bx,trackbuf 2805 00000FA0 8B0EF864 mov cx,[BufSafe] 2806 00000FA4 E884F3 call getfssec 2807 00000FA7 5A pop dx 2808 00000FA8 58 pop ax 2809 00000FA9 56 push si ; Save current cluster 2810 00000FAA BE0010 mov si,trackbuf 2811 00000FAD 8B0EFC64 mov cx,[BufSafeBytes] ; No more than many bytes 2812 00000FB1 51 print_msg_file: push cx 2813 00000FB2 50 push ax 2814 00000FB3 52 push dx 2815 00000FB4 AC lodsb 2816 00000FB5 3C1A cmp al,1Ah ; ASCII EOF? 2817 00000FB7 7418 je msg_done_pop 2818 00000FB9 FF161265 call [NextCharJump] ; Do what shall be done 2819 00000FBD 5A pop dx 2820 00000FBE 58 pop ax 2821 00000FBF 59 pop cx 2822 00000FC0 83E801 sub ax,byte 1 2823 00000FC3 83DA00 sbb dx,byte 0 2824 00000FC6 89C3 mov bx,ax 2825 00000FC8 09D3 or bx,dx 2826 00000FCA 7408 jz msg_done 2827 00000FCC E2E3 loop print_msg_file 2828 00000FCE 5E pop si 2829 00000FCF EBCA jmp short get_msg_chunk 2830 msg_done_pop: 2831 00000FD1 83C406 add sp,byte 6 ; Lose 3 words on the stack 2832 msg_done: 2833 00000FD4 5E pop si 2834 00000FD5 C3 ret 2835 msg_putchar: ; Normal character 2836 00000FD6 3C0F cmp al,0Fh ; ^O = color code follows 2837 00000FD8 7431 je msg_ctrl_o 2838 00000FDA 3C0D cmp al,0Dh ; Ignore 2839 00000FDC 742C je msg_ignore 2840 00000FDE 3C0A cmp al,0Ah ; = newline 2841 00000FE0 7430 je msg_newline 2842 00000FE2 3C0C cmp al,0Ch ; = clear screen 2843 00000FE4 7455 je msg_formfeed 2844 00000FE6 8B1E1A65 mov bx,[TextAttrBX] 2845 00000FEA B409 mov ah,09h ; Write character/attribute 2846 00000FEC B90100 mov cx,1 ; One character only 2847 00000FEF CD10 int 10h ; Write to screen 2848 00000FF1 A01C65 mov al,[CursorCol] 2849 00000FF4 40 inc ax 2850 00000FF5 3A061E65 cmp al,[VidCols] 2851 00000FF9 7717 ja msg_newline 2852 00000FFB A21C65 mov [CursorCol],al 2853 00000FFE 8A3E1B65 msg_gotoxy: mov bh,[TextPage] 2854 00001002 8B161C65 mov dx,[CursorDX] 2855 00001006 B402 mov ah,02h ; Set cursor position 2856 00001008 CD10 int 10h 2857 0000100A C3 msg_ignore: ret 2858 msg_ctrl_o: ; ^O = color code follows 2859 0000100B C7061265[5010] mov word [NextCharJump],msg_setbg 2860 00001011 C3 ret 2861 msg_newline: ; Newline char or end of line 2862 00001012 C6061C6500 mov byte [CursorCol],0 2863 00001017 A01D65 mov al,[CursorRow] 2864 0000101A 40 inc ax 2865 0000101B 3A061F65 cmp al,[VidRows] 2866 0000101F 7705 ja msg_scroll 2867 00001021 A21D65 mov [CursorRow],al 2868 00001024 EBD8 jmp short msg_gotoxy 2869 00001026 31C9 msg_scroll: xor cx,cx ; Upper left hand corner 2870 00001028 8B161E65 mov dx,[ScreenSize] 2871 0000102C 88361D65 mov [CursorRow],dh ; New cursor at the bottom 2872 00001030 8A3E1A65 mov bh,[TextAttribute] 2873 00001034 B80106 mov ax,0601h ; Scroll up one line 2874 00001037 CD10 int 10h 2875 00001039 EBC3 jmp short msg_gotoxy 2876 msg_formfeed: ; Form feed character 2877 0000103B 31C9 xor cx,cx 2878 0000103D 890E1C65 mov [CursorDX],cx ; Upper lefthand corner 2879 00001041 8B161E65 mov dx,[ScreenSize] 2880 00001045 8A3E1A65 mov bh,[TextAttribute] 2881 00001049 B80006 mov ax,0600h ; Clear screen region 2882 0000104C CD10 int 10h 2883 0000104E EBAE jmp short msg_gotoxy 2884 msg_setbg: ; Color background character 2885 00001050 E8BE01 call unhexchar 2886 00001053 721D jc msg_color_bad 2887 00001055 C0E004 shl al,4 2888 00001058 A21A65 mov [TextAttribute],al 2889 0000105B C7061265[6210] mov word [NextCharJump],msg_setfg 2890 00001061 C3 ret 2891 msg_setfg: ; Color foreground character 2892 00001062 E8AC01 call unhexchar 2893 00001065 720B jc msg_color_bad 2894 00001067 08061A65 or [TextAttribute],al ; setbg set foreground to 0 2895 0000106B C7061265[D60F] mov word [NextCharJump],msg_putchar 2896 00001071 C3 ret 2897 msg_color_bad: 2898 00001072 C6061A6507 mov byte [TextAttribute],07h ; Default attribute 2899 00001077 C7061265[D60F] mov word [NextCharJump],msg_putchar 2900 0000107D C3 ret 2901 2902 ; 2903 ; open,getc: Load a file a character at a time for parsing in a manner 2904 ; similar to the C library getc routine. Only one simultaneous 2905 ; use is supported. Note: "open" trashes the trackbuf. 2906 ; 2907 ; open: Input: mangled filename in DS:DI 2908 ; Output: ZF set on file not found or zero length 2909 ; 2910 ; getc: Output: CF set on end of file 2911 ; Character loaded in AL 2912 ; 2913 open: 2914 0000107E E8FBFD call searchdir 2915 00001081 7427 jz open_return 2916 00001083 9C pushf 2917 00001084 A3E464 mov [FBytes1],ax 2918 00001087 8916E664 mov [FBytes2],dx 2919 0000108B 0306F264 add ax,[ClustSize] 2920 0000108F 83D200 adc dx,byte 0 2921 00001092 83E801 sub ax,byte 1 2922 00001095 83DA00 sbb dx,byte 0 2923 00001098 F736F264 div word [ClustSize] 2924 0000109C A30665 mov [FClust],ax ; Number of clusters 2925 0000109F 89360865 mov [FNextClust],si ; Cluster pointer 2926 000010A3 A1FE64 mov ax,[EndOfGetCBuf] ; Pointer at end of buffer -> 2927 000010A6 A30A65 mov [FPtr],ax ; nothing loaded yet 2928 000010A9 9D popf ; Restore no ZF 2929 000010AA C3 open_return: ret 2930 2931 ; 2932 getc: 2933 000010AB F9 stc ; If we exit here -> EOF 2934 000010AC 668B0EE464 mov ecx,[FBytes] 2935 000010B1 66E33B jecxz getc_ret 2936 000010B4 8B360A65 mov si,[FPtr] 2937 000010B8 3B36FE64 cmp si,[EndOfGetCBuf] 2938 000010BC 7226 jb getc_loaded 2939 ; Buffer empty -- load another set 2940 000010BE 8B0E0665 mov cx,[FClust] 2941 000010C2 3B0EF864 cmp cx,[BufSafe] 2942 000010C6 7604 jna getc_oksize 2943 000010C8 8B0EF864 mov cx,[BufSafe] 2944 000010CC 290E0665 getc_oksize: sub [FClust],cx ; Reduce remaining clusters 2945 000010D0 8B360865 mov si,[FNextClust] 2946 000010D4 BB0096 mov bx,getcbuf 2947 000010D7 53 push bx 2948 000010D8 06 push es ; ES may be != DS, save old ES 2949 000010D9 1E push ds ; Trackbuf is in DS, not ES 2950 000010DA 07 pop es 2951 000010DB E84DF2 call getfssec ; Load a trackbuf full of data 2952 000010DE 89360865 mov [FNextClust],si ; Store new next pointer 2953 000010E2 07 pop es ; Restore ES 2954 000010E3 5E pop si ; SI -> newly loaded data 2955 000010E4 AC getc_loaded: lodsb ; Load a byte 2956 000010E5 89360A65 mov [FPtr],si ; Update next byte pointer 2957 000010E9 66FF0EE464 dec dword [FBytes] ; Update bytes left counter (CF = 1) 2958 000010EE F8 clc ; Not EOF 2959 000010EF C3 getc_ret: ret 2960 2961 ; 2962 ; ungetc: Push a character (in AL) back into the getc buffer 2963 ; Note: if more than one byte is pushed back, this may cause 2964 ; bytes to be written below the getc buffer boundary. If there 2965 ; is a risk for this to occur, the getcbuf base address should 2966 ; be moved up. 2967 ; 2968 ungetc: 2969 000010F0 8B360A65 mov si,[FPtr] 2970 000010F4 4E dec si 2971 000010F5 8804 mov [si],al 2972 000010F7 89360A65 mov [FPtr],si 2973 000010FB 66FF06E464 inc dword [FBytes] 2974 00001100 C3 ret 2975 2976 ; 2977 ; skipspace: Skip leading whitespace using "getc". If we hit end-of-line 2978 ; or end-of-file, return with carry set; ZF = true of EOF 2979 ; ZF = false for EOLN; otherwise CF = ZF = 0. 2980 ; 2981 ; Otherwise AL = first character after whitespace 2982 ; 2983 skipspace: 2984 00001101 E8A7FF skipspace_loop: call getc 2985 00001104 720D jc skipspace_eof 2986 00001106 3C1A cmp al,1Ah ; DOS EOF 2987 00001108 7409 je skipspace_eof 2988 0000110A 3C0A cmp al,0Ah 2989 0000110C 7409 je skipspace_eoln 2990 0000110E 3C20 cmp al,' ' 2991 00001110 76EF jbe skipspace_loop 2992 00001112 C3 ret ; CF = ZF = 0 2993 00001113 38C0 skipspace_eof: cmp al,al ; Set ZF 2994 00001115 F9 stc ; Set CF 2995 00001116 C3 ret 2996 00001117 04FF skipspace_eoln: add al,0FFh ; Set CF, clear ZF 2997 00001119 C3 ret 2998 2999 ; 3000 ; getkeyword: Get a keyword from the current "getc" file; only the two 3001 ; first characters are considered significant. 3002 ; 3003 ; Lines beginning with ASCII characters 33-47 are treated 3004 ; as comments and ignored; other lines are checked for 3005 ; validity by scanning through the keywd_table. 3006 ; 3007 ; The keyword and subsequent whitespace is skipped. 3008 ; 3009 ; On EOF, CF = 1; otherwise, CF = 0, AL:AH = lowercase char pair 3010 ; 3011 getkeyword: 3012 0000111A E8E4FF gkw_find: call skipspace 3013 0000111D 7438 jz gkw_eof ; end of file 3014 0000111F 72F9 jc gkw_find ; end of line: try again 3015 00001121 3C30 cmp al,'0' 3016 00001123 7246 jb gkw_skipline ; skip comment line 3017 00001125 50 push ax 3018 00001126 E882FF call getc 3019 00001129 5B pop bx 3020 0000112A 722B jc gkw_eof 3021 0000112C 88C7 mov bh,al ; Move character pair into BL:BH 3022 0000112E 81CB2020 or bx,2020h ; Lower-case it 3023 00001132 BE[0417] mov si,keywd_table 3024 00001135 AD gkw_check: lodsw 3025 00001136 21C0 and ax,ax 3026 00001138 7429 jz gkw_badline ; Bad keyword, write message 3027 0000113A 39D8 cmp ax,bx 3028 0000113C 75F7 jne gkw_check 3029 0000113E 50 push ax 3030 gkw_skiprest: 3031 0000113F E869FF call getc 3032 00001142 7212 jc gkw_eof_pop 3033 00001144 3C30 cmp al,'0' 3034 00001146 77F7 ja gkw_skiprest 3035 00001148 E8A5FF call ungetc 3036 0000114B E8B3FF call skipspace 3037 0000114E 7406 jz gkw_eof_pop 3038 00001150 7206 jc gkw_missingpar ; Missing parameter after keyword 3039 00001152 E89BFF call ungetc ; Return character to buffer 3040 00001155 F8 clc ; Successful return 3041 00001156 58 gkw_eof_pop: pop ax 3042 00001157 C3 gkw_eof: ret ; CF = 1 on all EOF conditions 3043 00001158 58 gkw_missingpar: pop ax 3044 00001159 BE[9815] mov si,err_noparm 3045 0000115C E8F6EF call writestr 3046 0000115F E9B8FF jmp gkw_find 3047 00001162 58 gkw_badline_pop: pop ax 3048 00001163 BE[7515] gkw_badline: mov si,err_badcfg 3049 00001166 E8ECEF call writestr 3050 00001169 EBAF jmp short gkw_find 3051 0000116B 3C0A gkw_skipline: cmp al,10 ; Scan for LF 3052 0000116D 74AB je gkw_find 3053 0000116F E839FF call getc 3054 00001172 72E3 jc gkw_eof 3055 00001174 EBF5 jmp short gkw_skipline 3056 3057 ; 3058 ; getint: Load an integer from the getc file. 3059 ; Return CF if error; otherwise return integer in EBX 3060 ; 3061 getint: 3062 00001176 BFA064 mov di,NumBuf 3063 00001179 81FFAF64 gi_getnum: cmp di,NumBufEnd ; Last byte in NumBuf 3064 0000117D 730F jae gi_loaded 3065 0000117F 57 push di 3066 00001180 E828FF call getc 3067 00001183 5F pop di 3068 00001184 7208 jc gi_loaded 3069 00001186 AA stosb 3070 00001187 3C2D cmp al,'-' 3071 00001189 73EE jnb gi_getnum 3072 0000118B E862FF call ungetc ; Unget non-numeric 3073 0000118E C60500 gi_loaded: mov byte [di],0 3074 00001191 BEA064 mov si,NumBuf 3075 ; Fall through to parseint 3076 3077 ; 3078 ; parseint: Convert an integer to a number in EBX 3079 ; Get characters from string in DS:SI 3080 ; Return CF on error 3081 ; DS:SI points to first character after number 3082 ; 3083 ; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M 3084 ; 3085 parseint: 3086 00001194 6650 push eax 3087 00001196 6651 push ecx 3088 00001198 55 push bp 3089 00001199 6631C0 xor eax,eax ; Current digit (keep eax == al) 3090 0000119C 6689C3 mov ebx,eax ; Accumulator 3091 0000119F 6689D9 mov ecx,ebx ; Base 3092 000011A2 31ED xor bp,bp ; Used for negative flag 3093 000011A4 AC pi_begin: lodsb 3094 000011A5 3C2D cmp al,'-' 3095 000011A7 7506 jne pi_not_minus 3096 000011A9 81F50100 xor bp,1 ; Set unary minus flag 3097 000011AD EBF5 jmp short pi_begin 3098 pi_not_minus: 3099 000011AF 3C30 cmp al,'0' 3100 000011B1 724F jb pi_err 3101 000011B3 7408 je pi_octhex 3102 000011B5 3C39 cmp al,'9' 3103 000011B7 7749 ja pi_err 3104 000011B9 B10A mov cl,10 ; Base = decimal 3105 000011BB EB17 jmp short pi_foundbase 3106 pi_octhex: 3107 000011BD AC lodsb 3108 000011BE 3C30 cmp al,'0' 3109 000011C0 7225 jb pi_km ; Value is zero 3110 000011C2 0C20 or al,20h ; Downcase 3111 000011C4 3C78 cmp al,'x' 3112 000011C6 7408 je pi_ishex 3113 000011C8 3C37 cmp al,'7' 3114 000011CA 7736 ja pi_err 3115 000011CC B108 mov cl,8 ; Base = octal 3116 000011CE EB04 jmp short pi_foundbase 3117 pi_ishex: 3118 000011D0 B030 mov al,'0' ; No numeric value accrued yet 3119 000011D2 B110 mov cl,16 ; Base = hex 3120 pi_foundbase: 3121 000011D4 E83A00 call unhexchar 3122 000011D7 720E jc pi_km ; Not a (hex) digit 3123 000011D9 38C8 cmp al,cl 3124 000011DB 730A jae pi_km ; Invalid for base 3125 000011DD 660FAFD9 imul ebx,ecx ; Multiply accumulated by base 3126 000011E1 6601C3 add ebx,eax ; Add current digit 3127 000011E4 AC lodsb 3128 000011E5 EBED jmp short pi_foundbase 3129 pi_km: 3130 000011E7 4E dec si ; Back up to last non-numeric 3131 000011E8 AC lodsb 3132 000011E9 0C20 or al,20h 3133 000011EB 3C6B cmp al,'k' 3134 000011ED 7416 je pi_isk 3135 000011EF 3C6D cmp al,'m' 3136 000011F1 7418 je pi_ism 3137 000011F3 4E dec si ; Back up 3138 000011F4 21ED pi_fini: and bp,bp 3139 000011F6 7404 jz pi_ret ; CF=0! 3140 000011F8 66F7DB neg ebx ; Value was negative 3141 000011FB F8 pi_done: clc 3142 000011FC 5D pi_ret: pop bp 3143 000011FD 6659 pop ecx 3144 000011FF 6658 pop eax 3145 00001201 C3 ret 3146 00001202 F9 pi_err: stc 3147 00001203 EBF7 jmp short pi_ret 3148 00001205 66C1E30A pi_isk: shl ebx,10 ; x 2^10 3149 00001209 EBF0 jmp short pi_done 3150 0000120B 66C1E314 pi_ism: shl ebx,20 ; x 2^20 3151 0000120F EBEA jmp short pi_done 3152 3153 ; 3154 ; unhexchar: Convert a hexadecimal digit in AL to the equivalent number; 3155 ; return CF=1 if not a hex digit 3156 ; 3157 unhexchar: 3158 00001211 3C30 cmp al,'0' 3159 00001213 7215 jb uxc_ret ; If failure, CF == 1 already 3160 00001215 3C39 cmp al,'9' 3161 00001217 7703 ja uxc_1 3162 00001219 2C30 sub al,'0' ; CF <- 0 3163 0000121B C3 ret 3164 0000121C 0C20 uxc_1: or al,20h ; upper case -> lower case 3165 0000121E 3C61 cmp al,'a' 3166 00001220 7208 jb uxc_ret ; If failure, CF == 1 already 3167 00001222 3C66 cmp al,'f' 3168 00001224 7703 ja uxc_err 3169 00001226 2C57 sub al,'a'-10 ; CF <- 0 3170 00001228 C3 ret 3171 00001229 F9 uxc_err: stc 3172 0000122A C3 uxc_ret: ret 3173 3174 ; 3175 ; 3176 ; getline: Get a command line, converting control characters to spaces 3177 ; and collapsing streches to one; a space is appended to the 3178 ; end of the string, unless the line is empty. 3179 ; The line is terminated by ^J, ^Z or EOF and is written 3180 ; to ES:DI. On return, DI points to first char after string. 3181 ; CF is set if we hit EOF. 3182 ; 3183 getline: 3184 0000122B E8D3FE call skipspace 3185 0000122E B201 mov dl,1 ; Empty line -> empty string. 3186 00001230 742B jz gl_eof ; eof 3187 00001232 7226 jc gl_eoln ; eoln 3188 00001234 E8B9FE call ungetc 3189 00001237 52 gl_fillloop: push dx 3190 00001238 57 push di 3191 00001239 E86FFE call getc 3192 0000123C 5F pop di 3193 0000123D 5A pop dx 3194 0000123E 721E jc gl_ret ; CF set! 3195 00001240 3C20 cmp al,' ' 3196 00001242 7605 jna gl_ctrl 3197 00001244 31D2 xor dx,dx 3198 00001246 AA gl_store: stosb 3199 00001247 EBEE jmp short gl_fillloop 3200 00001249 3C0A gl_ctrl: cmp al,10 3201 0000124B 7411 je gl_ret ; CF clear! 3202 0000124D 3C1A cmp al,26 3203 0000124F 740C je gl_eof 3204 00001251 20D2 and dl,dl 3205 00001253 75E2 jnz gl_fillloop ; Ignore multiple spaces 3206 00001255 B020 mov al,' ' ; Ctrl -> space 3207 00001257 42 inc dx 3208 00001258 EBEC jmp short gl_store 3209 0000125A F8 gl_eoln: clc ; End of line is not end of file 3210 0000125B EB01 jmp short gl_ret 3211 0000125D F9 gl_eof: stc 3212 0000125E 9C gl_ret: pushf ; We want the last char to be space! 3213 0000125F 20D2 and dl,dl 3214 00001261 7503 jnz gl_xret 3215 00001263 B020 mov al,' ' 3216 00001265 AA stosb 3217 00001266 9D gl_xret: popf 3218 00001267 C3 ret 3219 3220 3221 %ifdef debug ; This code for debugging only 3222 ; 3223 ; dumpregs: Dumps the contents of all registers 3224 ; 3225 assume ds:_text, es:NOTHING, fs:NOTHING, gs:NOTHING 3226 dumpregs proc near ; When calling, IP is on stack 3227 pushf ; Store flags 3228 pusha 3229 push ds 3230 push es 3231 push fs 3232 push gs 3233 push cs ; Set DS <- CS 3234 pop ds 3235 cld ; Clear direction flag 3236 mov si,offset crlf_msg 3237 call writestr 3238 mov bx,sp 3239 add bx,byte 26 3240 mov si,offset regnames 3241 mov cx,2 ; 2*7 registers to dump 3242 dump_line: push cx 3243 mov cx,7 ; 7 registers per line 3244 dump_reg: push cx 3245 mov cx,4 ; 4 characters/register name 3246 wr_reg_name: lodsb 3247 call writechr 3248 loop wr_reg_name 3249 mov ax,ss:[bx] 3250 dec bx 3251 dec bx 3252 call writehex 3253 pop cx 3254 loop dump_reg 3255 mov al,0Dh ; 3256 call writechr 3257 mov al,0Ah ; 3258 call writechr 3259 pop cx 3260 loop dump_line 3261 pop gs 3262 pop fs 3263 pop es 3264 pop ds 3265 popa ; Restore the remainder 3266 popf ; Restore flags 3267 ret 3268 dumpregs endp 3269 3270 regnames db ' IP: FL: AX: CX: DX: BX: SP: BP: SI: DI: DS: ES: FS: GS:' 3271 3272 ; 3273 ; writehex: Writes a 16-bit hexadecimal number (in AX) 3274 ; 3275 writehex proc near 3276 push bx 3277 push cx 3278 mov cx,4 ; 4 numbers 3279 write_hexdig: xor bx,bx 3280 push cx 3281 mov cx,4 ; 4 bits/digit 3282 xfer_digit: shl ax,1 3283 rcl bx,1 3284 loop xfer_digit 3285 push ax 3286 mov ax,bx 3287 or al,'0' 3288 cmp al,'9' 3289 jna ok_digit 3290 add al,'A'-'0'-10 3291 ok_digit: call writechr 3292 pop ax 3293 pop cx 3294 loop write_hexdig 3295 pop cx 3296 pop bx 3297 ret 3298 writehex endp 3299 3300 debug_magic dw 0D00Dh 3301 3302 %endif ; debug 3303 ; 3304 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed 3305 ; to by ES:DI; ends on encountering any whitespace 3306 ; 3307 3308 mangle_name: 3309 00001268 B90B00 mov cx,11 ; # of bytes to write 3310 mn_loop: 3311 0000126B AC lodsb 3312 0000126C 3C20 cmp al,' ' ; If control or space, end 3313 0000126E 762B jna mn_end 3314 00001270 3C2E cmp al,'.' ; Period -> space-fill 3315 00001272 740C je mn_is_period 3316 00001274 3C61 cmp al,'a' 3317 00001276 7220 jb mn_not_lower 3318 00001278 3C7A cmp al,'z' 3319 0000127A 770F ja mn_not_uslower 3320 0000127C 2C20 sub al,020h 3321 0000127E EB18 jmp short mn_not_lower 3322 00001280 B020 mn_is_period: mov al,' ' ; We need to space-fill 3323 00001282 81F90300 mn_period_loop: cmp cx,3 ; If <= 3 characters left 3324 00001286 76E3 jbe mn_loop ; Just ignore it 3325 00001288 AA stosb ; Otherwise, write a period 3326 00001289 E2F7 loop mn_period_loop ; Dec CX and (always) jump 3327 0000128B 3C81 mn_not_uslower: cmp al,ucase_low 3328 0000128D 7209 jb mn_not_lower 3329 0000128F 3CA4 cmp al,ucase_high 3330 00001291 7705 ja mn_not_lower 3331 00001293 BB[1F12] mov bx,ucase_tab-ucase_low 3332 00001296 2ED7 cs xlatb 3333 00001298 AA mn_not_lower: stosb 3334 00001299 E2D0 loop mn_loop ; Don't continue if too long 3335 mn_end: 3336 0000129B B020 mov al,' ' ; Space-fill name 3337 0000129D F3AA rep stosb ; Doesn't do anything if CX=0 3338 0000129F C3 ret ; Done 3339 3340 ; 3341 ; Upper-case table for extended characters; this is technically code page 865, 3342 ; but code page 437 users will probably not miss not being able to use the 3343 ; cent sign in kernel images too much :-) 3344 ; 3345 ; The table only covers the range 129 to 164; the rest we can deal with. 3346 ; 3347 ucase_low equ 129 3348 ucase_high equ 164 3349 000012A0 9A90418E418F804545- ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII' 3350 000012A9 45494949 3351 000012AD 8E8F9092924F994F55- db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154 3352 000012B6 5559999A 3353 000012BA 9D9C9D9E9F41494F55- db 157, 156, 157, 158, 159, 'AIOU', 165 3354 000012C3 A5 3355 3356 ; 3357 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled 3358 ; filename to the conventional representation. This is needed 3359 ; for the BOOT_IMAGE= parameter for the kernel. 3360 ; NOTE: A 13-byte buffer is mandatory, even if the string is 3361 ; known to be shorter. 3362 ; 3363 ; DS:SI -> input mangled file name 3364 ; ES:DI -> output buffer 3365 ; 3366 ; On return, DI points to the first byte after the output name, 3367 ; which is set to a null byte. 3368 ; 3369 unmangle_name: 3370 000012C4 56 push si ; Save pointer to original name 3371 000012C5 B90800 mov cx,8 3372 000012C8 89FD mov bp,di 3373 000012CA AC un_copy_body: lodsb 3374 000012CB E82600 call lower_case 3375 000012CE AA stosb 3376 000012CF 3C20 cmp al,' ' 3377 000012D1 7602 jbe un_cb_space 3378 000012D3 89FD mov bp,di ; Position of last nonblank+1 3379 000012D5 E2F3 un_cb_space: loop un_copy_body 3380 000012D7 89EF mov di,bp 3381 000012D9 B02E mov al,'.' ; Don't save 3382 000012DB AA stosb 3383 000012DC B90300 mov cx,3 3384 000012DF AC un_copy_ext: lodsb 3385 000012E0 E81100 call lower_case 3386 000012E3 AA stosb 3387 000012E4 3C20 cmp al,' ' 3388 000012E6 7602 jbe un_ce_space 3389 000012E8 89FD mov bp,di 3390 000012EA E2F3 un_ce_space: loop un_copy_ext 3391 000012EC 89EF mov di,bp 3392 000012EE 26C60500 mov byte [es:di], 0 3393 000012F2 5E pop si 3394 000012F3 C3 ret 3395 3396 ; 3397 ; lower_case: Lower case a character in AL 3398 ; 3399 lower_case: 3400 000012F4 3C41 cmp al,'A' 3401 000012F6 7216 jb lc_ret 3402 000012F8 3C5A cmp al,'Z' 3403 000012FA 7703 ja lc_1 3404 000012FC 0C20 or al,20h 3405 000012FE C3 ret 3406 000012FF 3C80 lc_1: cmp al,lcase_low 3407 00001301 720B jb lc_ret 3408 00001303 3CA5 cmp al,lcase_high 3409 00001305 7707 ja lc_ret 3410 00001307 53 push bx 3411 00001308 BB[8F12] mov bx,lcase_tab-lcase_low 3412 0000130B 2ED7 cs xlatb 3413 0000130D 5B pop bx 3414 0000130E C3 lc_ret: ret 3415 3416 ; 3417 ; Lower-case table for codepage 865 3418 ; 3419 lcase_low equ 128 3420 lcase_high equ 165 3421 0000130F 878182838485868788- lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138 3422 00001318 898A 3423 0000131A 8B8C8D848682919193- db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149 3424 00001323 9495 3425 00001325 96979894819B9C9B9E- db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160 3426 0000132E 9FA0 3427 00001330 A1A2A3A4A4 db 161, 162, 163, 164, 164 3428 ; 3429 ; Various initialized or semi-initialized variables 3430 ; 3431 00001335 20436F707972696768- copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin' 3432 0000133E 742028432920313939- 3433 00001347 342D3139393920482E- 3434 00001350 20506574657220416E- 3435 00001359 76696E 3436 0000135C 0D0A00 db 0Dh, 0Ah, 0 3437 0000135F 626F6F743A2000 boot_prompt db 'boot: ',0 3438 00001366 08200800 wipe_char db 08h, ' ', 08h, 0 3439 0000136A 436F756C64206E6F74- err_notfound db 'Could not find kernel image: ',0 3440 00001373 2066696E64206B6572- 3441 0000137C 6E656C20696D616765- 3442 00001385 3A2000 3443 00001388 0D0A496E76616C6964- err_notkernel db 0Dh, 0Ah, 'Invalid or corrupt kernel image.', 0Dh, 0Ah, 0 3444 00001391 206F7220636F727275- 3445 0000139A 7074206B65726E656C- 3446 000013A3 20696D6167652E0D0A- 3447 000013AC 00 3448 000013AD 497420617070656172- err_not386 db 'It appears your computer uses a 286 or lower CPU.' 3449 000013B6 7320796F757220636F- 3450 000013BF 6D7075746572207573- 3451 000013C8 657320612032383620- 3452 000013D1 6F72206C6F77657220- 3453 000013DA 4350552E 3454 000013DE 0D0A db 0Dh, 0Ah 3455 000013E0 596F752063616E6E6F- db 'You cannot run Linux unless you have a 386 or higher CPU' 3456 000013E9 742072756E204C696E- 3457 000013F2 757820756E6C657373- 3458 000013FB 20796F752068617665- 3459 00001404 206120333836206F72- 3460 0000140D 206869676865722043- 3461 00001416 5055 3462 00001418 0D0A db 0Dh, 0Ah 3463 0000141A 696E20796F7572206D- db 'in your machine. If you get this message in error, hold' 3464 00001423 616368696E652E2020- 3465 0000142C 496620796F75206765- 3466 00001435 742074686973206D65- 3467 0000143E 737361676520696E20- 3468 00001447 6572726F722C20686F- 3469 00001450 6C64 3470 00001452 0D0A db 0Dh, 0Ah 3471 00001454 646F776E2074686520- db 'down the Ctrl key while booting, and I will take your' 3472 0000145D 4374726C206B657920- 3473 00001466 7768696C6520626F6F- 3474 0000146F 74696E672C20616E64- 3475 00001478 20492077696C6C2074- 3476 00001481 616B6520796F7572 3477 00001489 0D0A db 0Dh, 0Ah 3478 0000148B 776F726420666F7220- db 'word for it.', 0Dh, 0Ah, 0 3479 00001494 69742E0D0A00 3480 0000149A 497420617070656172- err_noram db 'It appears your computer has less than 608K of low ("DOS")' 3481 000014A3 7320796F757220636F- 3482 000014AC 6D7075746572206861- 3483 000014B5 73206C657373207468- 3484 000014BE 616E203630384B206F- 3485 000014C7 66206C6F7720282244- 3486 000014D0 4F532229 3487 000014D4 0D0A db 0Dh, 0Ah 3488 000014D6 52414D2E20204C696E- db 'RAM. Linux needs at least this amount to boot. If you get' 3489 000014DF 7578206E6565647320- 3490 000014E8 6174206C6561737420- 3491 000014F1 7468697320616D6F75- 3492 000014FA 6E7420746F20626F6F- 3493 00001503 742E2020496620796F- 3494 0000150C 7520676574 3495 00001511 0D0A db 0Dh, 0Ah 3496 00001513 74686973206D657373- db 'this message in error, hold down the Ctrl key while' 3497 0000151C 61676520696E206572- 3498 00001525 726F722C20686F6C64- 3499 0000152E 20646F776E20746865- 3500 00001537 204374726C206B6579- 3501 00001540 207768696C65 3502 00001546 0D0A db 0Dh, 0Ah 3503 00001548 626F6F74696E672C20- db 'booting, and I will take your word for it.', 0Dh, 0Ah, 0 3504 00001551 616E6420492077696C- 3505 0000155A 6C2074616B6520796F- 3506 00001563 757220776F72642066- 3507 0000156C 6F722069742E0D0A00 3508 00001575 556E6B6E6F776E206B- err_badcfg db 'Unknown keyword in syslinux.cfg.', 0Dh, 0Ah, 0 3509 0000157E 6579776F726420696E- 3510 00001587 207379736C696E7578- 3511 00001590 2E6366672E0D0A00 3512 00001598 4D697373696E672070- err_noparm db 'Missing parameter in syslinux.cfg.', 0Dh, 0Ah, 0 3513 000015A1 6172616D6574657220- 3514 000015AA 696E207379736C696E- 3515 000015B3 75782E6366672E0D0A- 3516 000015BC 00 3517 000015BD 0D0A436F756C64206E- err_noinitrd db 0Dh, 0Ah, 'Could not find ramdisk image: ', 0 3518 000015C6 6F742066696E642072- 3519 000015CF 616D6469736B20696D- 3520 000015D8 6167653A2000 3521 000015DE 4E6F7420656E6F7567- err_nohighmem db 'Not enough memory to load specified kernel.', 0Dh, 0Ah, 0 3522 000015E7 68206D656D6F727920- 3523 000015F0 746F206C6F61642073- 3524 000015F9 706563696669656420- 3525 00001602 6B65726E656C2E0D0A- 3526 0000160B 00 3527 0000160C 0D0A4B65726E656C20- err_highload db 0Dh, 0Ah, 'Kernel transfer failure.', 0Dh, 0Ah, 0 3528 00001615 7472616E7366657220- 3529 0000161E 6661696C7572652E0D- 3530 00001627 0A00 3531 00001629 43616E6E6F74206C6F- err_oldkernel db 'Cannot load a ramdisk with an old kernel image.' 3532 00001632 616420612072616D64- 3533 0000163B 69736B207769746820- 3534 00001644 616E206F6C64206B65- 3535 0000164D 726E656C20696D6167- 3536 00001656 652E 3537 00001658 0D0A00 db 0Dh, 0Ah, 0 3538 0000165B 3A20617474656D7074- err_notdos db ': attempted DOS system call', 0Dh, 0Ah, 0 3539 00001664 656420444F53207379- 3540 0000166D 7374656D2063616C6C- 3541 00001676 0D0A00 3542 00001679 434F4D424F4F542069- err_comlarge db 'COMBOOT image too large.', 0Dh, 0Ah, 0 3543 00001682 6D61676520746F6F20- 3544 0000168B 6C617267652E0D0A00 3545 00001694 496E76616C6964206F- err_bootsec db 'Invalid or corrupt boot sector image.', 0Dh, 0Ah, 0 3546 0000169D 7220636F7272757074- 3547 000016A6 20626F6F7420736563- 3548 000016AF 746F7220696D616765- 3549 000016B8 2E0D0A00 3550 000016BC 0D0A41323020676174- err_a20 db 0Dh, 0Ah, 'A20 gate not responding!', 0Dh, 0Ah, 0 3551 000016C5 65206E6F7420726573- 3552 000016CE 706F6E64696E67210D- 3553 000016D7 0A00 3554 000016D9 4C6F6164696E672000 loading_msg db 'Loading ', 0 3555 000016E2 2E dotdot_msg db '.' 3556 000016E3 2E00 dot_msg db '.', 0 3557 000016E5 2061626F727465642E aborted_msg db ' aborted.' ; Fall through to crlf_msg! 3558 000016EE 0D0A00 crlf_msg db 0Dh, 0Ah, 0 3559 000016F1 5359534C494E555843- syslinux_cfg db 'SYSLINUXCFG' 3560 000016FA 4647 3561 ; 3562 ; Command line options we'd like to take a look at 3563 ; 3564 ; mem= and vga= are handled as normal 32-bit integer values 3565 000016FC 696E697472643D initrd_cmd db 'initrd=' 3566 initrd_cmd_len equ 7 3567 ; 3568 ; Config file keyword table 3569 ; 3570 00001703 90 align 2 3571 00001704 6170 keywd_table db 'ap' ; append 3572 00001706 6465 db 'de' ; default 3573 00001708 7469 db 'ti' ; timeout 3574 0000170A 666F db 'fo' ; font 3575 0000170C 6B62 db 'kb' ; kbd 3576 0000170E 6469 db 'di' ; display 3577 00001710 7072 db 'pr' ; prompt 3578 00001712 6C61 db 'la' ; label 3579 00001714 696D db 'im' ; implicit 3580 00001716 6B65 db 'ke' ; kernel 3581 00001718 6631 db 'f1' ; F1 3582 0000171A 6632 db 'f2' ; F2 3583 0000171C 6633 db 'f3' ; F3 3584 0000171E 6634 db 'f4' ; F4 3585 00001720 6635 db 'f5' ; F5 3586 00001722 6636 db 'f6' ; F6 3587 00001724 6637 db 'f7' ; F7 3588 00001726 6638 db 'f8' ; F8 3589 00001728 6639 db 'f9' ; F9 3590 0000172A 6630 db 'f0' ; F10 3591 0000172C 0000 dw 0 3592 ; 3593 ; Extensions to search for (in *reverse* order). Note that the last 3594 ; (lexically first) entry in the table is a placeholder for the original 3595 ; extension, needed for error messages. The exten_table is shifted so 3596 ; the table is 1-based; this is because a "loop" cx is used as index. 3597 ; 3598 exten_table: 3599 0000172E 00000000 OrigKernelExt: dd 0 ; Original extension 3600 00001732 434F4D00 db 'COM',0 ; COMBOOT (same as DOS) 3601 00001736 42532000 db 'BS ',0 ; Boot Sector 3602 0000173A 42535300 db 'BSS',0 ; Boot Sector (add superblock) 3603 0000173E 43425400 db 'CBT',0 ; COMBOOT (specific) 3604 3605 exten_count equ (($-exten_table) >> 2) - 1 ; Number of alternates 3606 ; 3607 ; Misc initialized (data) variables 3608 ; 3609 00001742 0000 AppendLen dw 0 ; Bytes in append= command 3610 00001744 0000 KbdTimeOut dw 0 ; Keyboard timeout (if any) 3611 00001746 0000 FKeyMap dw 0 ; Bitmap for F-keys loaded 3612 00001748 0080 CmdLinePtr dw cmd_line_here ; Command line advancing pointer 3613 initrd_flag equ $ 3614 0000174A 0000 initrd_ptr dw 0 ; Initial ramdisk pointer/flag 3615 0000174C 0000 VKernelCtr dw 0 ; Number of registered vkernels 3616 0000174E 0000 ForcePrompt dw 0 ; Force prompt 3617 00001750 0100 AllowImplicit dw 1 ; Allow implicit kernels 3618 ; 3619 ; Stuff for the command line; we do some trickery here with equ to avoid 3620 ; tons of zeros appended to our file and wasting space 3621 ; 3622 00001752 6C696E757820 linuxauto_cmd db 'linux ' 3623 00001758 6175746F00 auto_cmd db 'auto',0 3624 linuxauto_len equ $-linuxauto_cmd 3625 auto_len equ $-auto_cmd 3626 0000175D 424F4F545F494D4147- boot_image db 'BOOT_IMAGE=' 3627 00001766 453D 3628 boot_image_len equ $-boot_image 3629 align 4, db 0 ; For the good of REP MOVSD 3630 command_line equ $ 3631 default_cmd equ $+(max_cmd_len+2) 3632 ldlinux_end equ default_cmd+(max_cmd_len+1) 3633 kern_cmd_len equ ldlinux_end-command_line 3634 ldlinux_len equ ldlinux_end-ldlinux_magic 3635 ; 3636 ; Put the getcbuf right after the code, aligned on a sector boundary 3637 ; 3638 end_of_code equ (ldlinux_end-bootsec)+7C00h 3639 getcbuf equ (end_of_code + 511) & 0FE00h