home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 May / pcp151c.iso / misc / src / trees / syslinux-1.40 / ldlinux.asm < prev    next >
Encoding:
Assembly Source File  |  1998-05-07  |  89.6 KB  |  3,387 lines

  1. ; -*- fundamental -*- (asm-mode sucks)
  2. ; $Id: ldlinux.asm,v 1.36 1998/05/08 01:45:36 hpa Exp $
  3. ; ****************************************************************************
  4. ;
  5. ;  ldlinux.asm
  6. ;
  7. ;  A program to boot Linux kernels off an MS-DOS formatted floppy disk.     This
  8. ;  functionality is good to have for installation floppies, where it may
  9. ;  be hard to find a functional Linux system to run LILO off.
  10. ;
  11. ;  This program allows manipulation of the disk to take place entirely
  12. ;  from MS-LOSS, and can be especially useful in conjunction with the
  13. ;  umsdos filesystem.
  14. ;
  15. ;  This file is loaded in stages; first the boot sector at offset 7C00h,
  16. ;  then the first sector (cluster, really, but we can only assume 1 sector)
  17. ;  of LDLINUX.SYS at 7E00h and finally the remainder of LDLINUX.SYS at 8000h.
  18. ;
  19. ;   Copyright (C) 1994-1998  H. Peter Anvin
  20. ;
  21. ;  This program is free software; you can redistribute it and/or modify
  22. ;  it under the terms of the GNU General Public License as published by
  23. ;  the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
  24. ;  USA; either version 2 of the License, or (at your option) any later
  25. ;  version; incorporated herein by reference.
  26. ; ****************************************************************************
  27.  
  28. ;
  29. ; Some semi-configurable constants... change on your own risk
  30. ;
  31. max_cmd_len    equ 255            ; Must be odd; 255 is the kernel limit
  32. retry_count    equ 6            ; How patient are we with the disk?
  33.  
  34. ;
  35. ; Should be updated with every release to avoid bootsector/SYS file mismatch
  36. ;
  37. %define    version_str    VERSION        ; Must be 4 characters long!
  38. %define date        DATE_STR    ; Defined from the Makefile
  39. %define    year        '1998'
  40. ;
  41. ; Debgging stuff
  42. ;
  43. ; %define debug 1            ; Uncomment to enable debugging
  44. ;
  45. ; ID for SYSLINUX (reported to kernel)
  46. ;
  47. syslinux_id    equ 031h        ; SYSLINUX (3) version 1.x (1)
  48. ;
  49. ; Segments used by Linux
  50. ;
  51. real_mode_seg    equ 9000h
  52.         struc real_mode_seg_t
  53.         resb 20h-($-$$)        ; org 20h
  54. kern_cmd_magic    resw 1            ; Magic # for command line
  55. kern_cmd_offset resw 1            ; Offset for kernel command line
  56.         resb 497-($-$$)        ; org 497d
  57. bs_setupsecs    resb 1            ; Sectors for setup code (0 -> 4)
  58. bs_rootflags    resw 1            ; Root readonly flag
  59. bs_syssize    resw 1
  60. bs_swapdev    resw 1            ; Swap device (obsolete)
  61. bs_ramsize    resw 1            ; Ramdisk flags, formerly ramdisk size
  62. bs_vidmode    resw 1            ; Video mode
  63. bs_rootdev    resw 1            ; Root device
  64. bs_bootsign    resw 1            ; Boot sector signature (0AA55h)
  65. su_jump        resb 1            ; 0EBh
  66. su_jump2    resb 1
  67. su_header    resd 1            ; New setup code: header
  68. su_version    resw 1            ; See linux/arch/i386/boot/setup.S
  69. su_switch    resw 1
  70. su_setupseg    resw 1
  71. su_startsys    resw 1
  72. su_kver        resw 1            ; Kernel version pointer
  73. su_loader    resb 1            ; Loader ID
  74. su_loadflags    resb 1            ; Load high flag
  75. su_movesize    resw 1
  76. su_code32start    resd 1            ; Start of code loaded high
  77. su_ramdiskat    resd 1            ; Start of initial ramdisk
  78. su_ramdisklen    equ $            ; Length of initial ramdisk
  79. su_ramdisklen1    resw 1
  80. su_ramdisklen2    resw 1
  81. su_bsklugeoffs    resw 1
  82. su_bsklugeseg    resw 1
  83. su_heapend    resw 1
  84.         resb (8000h-12)-($-$$)    ; Were bootsect.S puts it...
  85. linux_stack    equ $
  86. linux_fdctab    equ $
  87.         resb 8000h-($-$$)
  88. cmd_line_here    equ $            ; Should be out of the way
  89.         endstruc
  90.  
  91. setup_seg    equ 9020h
  92.         struc setup_seg_t
  93.         org 0h            ; as 9020:0000, not 9000:0200
  94. setup_entry    equ $
  95.         endstruc
  96.  
  97. ;
  98. ; Magic number of su_header field
  99. ;
  100. HEADER_ID       equ 'HdrS'        ; HdrS (in littleendian hex)
  101. ;
  102. ; Flags for the su_loadflags field
  103. ;
  104. LOAD_HIGH    equ 01h            ; Large kernel, load high
  105. CAN_USE_HEAP    equ 80h                 ; Boot loader reports heap size
  106. ;
  107. ; The following structure is used for "virtual kernels"; i.e. LILO-style
  108. ; option labels.  The options we permit here are `kernel' and `append
  109. ; Since there is no room in the bottom 64K for all of these, we
  110. ; stick them at 8000:0000 and copy them down before we need them.
  111. ;
  112. ; Note: this structure can be added to, but it must 
  113. ;
  114. %define vk_power    7        ; log2(max number of vkernels)
  115. %define    max_vk        (1 << vk_power)    ; Maximum number of vkernels
  116. %define vk_shift    (16-vk_power)    ; Number of bits to shift
  117. %define vk_size        (1 << vk_shift)    ; Size of a vkernel buffer
  118.  
  119.         struc vkernel
  120. vk_vname:    resb 11            ; Virtual name **MUST BE FIRST!**
  121. vk_rname:    resb 11            ; Real name
  122. vk_appendlen:    resw 1
  123.         alignb 4
  124. vk_append:    resb max_cmd_len+1    ; Command line
  125.         alignb 4
  126. vk_end:        equ $            ; Should be <= vk_size
  127.         endstruc
  128.  
  129. %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
  130. %error "Too many vkernels defined, reduce vk_power"
  131. %endif
  132.  
  133. ;
  134. ; Segment assignments in the bottom 640K
  135. ; 0000h - main code/data segment (and BIOS segment)
  136. ; 9000h - real_mode_seg
  137. ;
  138. vk_seg          equ 8000h        ; This is where we stick'em
  139. xfer_buf_seg    equ 7000h        ; Bounce buffer for I/O to high mem
  140. fat_seg        equ 5000h        ; 128K area for FAT (2x64K)
  141. comboot_seg    equ 1000h        ; COMBOOT image loading zone
  142.  
  143. ;
  144. ; For our convenience: define macros for jump-over-unconditinal jumps
  145. ;
  146. %macro    jmpz    1
  147.     jnz %%skip
  148.     jmp %1
  149. %%skip:
  150. %endmacro
  151.  
  152. %macro    jmpnz    1
  153.     jz %%skip
  154.     jmp %1
  155. %%skip:
  156. %endmacro
  157.  
  158. %macro    jmpe    1
  159.     jne %%skip
  160.     jmp %1
  161. %%skip:
  162. %endmacro
  163.  
  164. %macro    jmpne    1
  165.     je %%skip
  166.     jmp %1
  167. %%skip:
  168. %endmacro
  169.  
  170. %macro    jmpc    1
  171.     jnc %%skip
  172.     jmp %1
  173. %%skip:
  174. %endmacro
  175.  
  176. %macro    jmpnc    1
  177.     jc %%skip
  178.     jmp %1
  179. %%skip:
  180. %endmacro
  181.  
  182. %macro    jmpb    1
  183.     jnb %%skip
  184.     jmp %1
  185. %%skip:
  186. %endmacro
  187.  
  188. %macro    jmpnb    1
  189.     jb %%skip
  190.     jmp %1
  191. %%skip:
  192. %endmacro
  193.  
  194. ;
  195. ; Macros similar to res[bwd], but which works in the code segment (after
  196. ; section .text)
  197. ;
  198. %macro    zb    1
  199.     times %1 db 0
  200. %endmacro
  201.  
  202. %macro    zw    1
  203.     times %1 dw 0
  204. %endmacro
  205.  
  206. %macro    zd    1
  207.     times %1 dd 0
  208. %endmacro
  209.  
  210. ; ---------------------------------------------------------------------------
  211. ;   BEGIN THE BIOS/CODE/DATA SEGMENT
  212. ; ---------------------------------------------------------------------------
  213.         absolute 4*1Eh        ; In the interrupt table
  214. fdctab        equ $
  215. fdctab1        resw 1
  216. fdctab2        resw 1
  217.  
  218. %ifdef debug
  219.         org 0100h
  220. ..start:    
  221. ;
  222. ; Hook for debugger stuff.  This gets automatically removed when
  223. ; generating the real thing.
  224. ;
  225. ; Initialize the registers for debugger operation
  226. ;
  227.         cli
  228.         mov ax,cs
  229.         mov ds,ax
  230.         mov es,ax
  231.         mov ss,ax
  232.         mov sp,StackBuf
  233.         sti
  234.         cld
  235. ;
  236. ; Load the actual boot sector so we can copy the data block
  237. ;
  238.         xor ax,ax        ; Reset floppy
  239.         xor dx,dx
  240.         int 13h
  241.         mov cx,6        ; Retry count...
  242. debug_tryloop:    push cx
  243.         mov bx,trackbuf
  244.         mov cx,0001h
  245.         xor dx,dx
  246.         mov ax,0201h
  247.         int 13h
  248.         pop cx
  249.         jnc debug_okay
  250.         loop debug_tryloop
  251.         int 3            ; Halt! (Breakpoint)
  252. debug_okay:    mov si,trackbuf+0bh
  253.         mov di,bsBytesPerSec
  254.         mov cx,33h
  255.         rep movsb
  256. ;
  257. ; Save bogus "BIOS floppy block" info to the stack in case we hit kaboom
  258. ;
  259.         push si
  260.         push si
  261.         push si            ; Writing to the trackbuf is harmless
  262. ;
  263. ; Copy the BIOS data area
  264. ;
  265.                 push ds
  266.                 xor ax,ax
  267.                 mov ds,ax
  268.                 mov si,0400h
  269.                 mov di,si
  270.                 mov cx,0100h
  271.                 rep movsw
  272.                 pop ds
  273. ;
  274. ;
  275. ; A NOP where we can breakpoint, then jump into the code *after*
  276. ; the segment register initialization section
  277. ;
  278.         nop
  279.         jmp debugentrypt
  280. %endif
  281.  
  282.                 absolute 0484h
  283. BIOS_vidrows    resb 1                    ; Number of screen rows
  284.  
  285. ;
  286. ; Memory below this point is reserved for the BIOS and the MBR
  287. ;
  288.          absolute 1000h
  289. trackbuf    equ $            ; Track buffer goes here
  290. trackbufsize    equ 16384        ; Safe size of track buffer
  291. ;        trackbuf ends at 5000h
  292.  
  293.                 absolute 6000h          ; Here we keep our BSS stuff
  294. StackBuf    equ $            ; Start the stack here (grow down - 4K)
  295. VKernelBuf:    resb vk_size        ; "Current" vkernel
  296.         alignb 4
  297. AppendBuf       resb max_cmd_len+1    ; append=
  298. KbdMap        resb 256        ; Keyboard map
  299. FKeyName    resb 10*16        ; File names for F-key help
  300. NumBuf        resb 16            ; Buffer to load number
  301. NumBufEnd    equ NumBuf+15        ; Pointer to last byte in NumBuf
  302.         alignb 4
  303. PartInfo    resb 16            ; Partition table entry
  304. InitRDat    resd 1            ; Load address (linear) for initrd
  305. HiLoadAddr      resd 1            ; Address pointer for high load loop
  306. KernelName      resb 12                ; Mangled name for kernel
  307.                     ; (note the spare byte after!)
  308. RootDir        equ $            ; Location of root directory
  309. RootDir1    resw 1
  310. RootDir2    resw 1
  311. DataArea    equ $            ; Location of data area
  312. DataArea1    resw 1
  313. DataArea2    resw 1
  314. FBytes        equ $            ; Used by open/getc
  315. FBytes1        resw 1
  316. FBytes2        resw 1
  317. RootDirSize    resw 1            ; Root dir size in sectors
  318. DirScanCtr    resw 1            ; Used while searching directory
  319. DirBlocksLeft    resw 1            ; Ditto
  320. EndofDirSec    resw 1            ; = trackbuf+bsBytesPerSec-31
  321. RunLinClust    resw 1            ; Cluster # for LDLINUX.SYS
  322. ClustSize    resw 1            ; Bytes/cluster
  323. SecPerClust    resw 1            ; Same as bsSecPerClust, but a word
  324. NextCluster    resw 1            ; Pointer to "nextcluster" routine
  325. BufSafe        resw 1            ; Clusters we can load into trackbuf
  326. BufSafeSec    resw 1            ; = how many sectors?
  327. BufSafeBytes    resw 1            ; = how many bytes?
  328. EndOfGetCBuf    resw 1            ; = getcbuf+BufSafeBytes
  329. HighMemSize    resw 1            ; High memory (K)
  330. KernelClust    resw 1            ; Kernel size in clusters
  331. KernelK        resw 1            ; Kernel size in kilobytes
  332. InitRDClust    resw 1            ; Ramdisk size in clusters
  333. ClustPerMoby    resw 1            ; Clusters per 64K
  334. FClust        resw 1            ; Number of clusters in open/getc file
  335. FNextClust    resw 1            ; Pointer to next cluster in d:o
  336. FPtr        resw 1            ; Pointer to next char in buffer
  337. CmdOptPtr       resw 1            ; Pointer to first option on cmd line
  338. KernelCNameLen  resw 1            ; Length of unmangled kernel name
  339. InitRDCNameLen  resw 1            ; Length of unmangled initrd name
  340. NextCharJump    resw 1            ; Routine to interpret next print char
  341. SetupSecs    resw 1            ; Number of setup sectors
  342. SavedSP        resw 1            ; Our SP while running a COMBOOT image
  343. A20test        resw 1            ; Counter for testing status of A20
  344. TextAttrBX      equ $
  345. TextAttribute   resb 1            ; Text attribute for message file
  346. TextPage        resb 1            ; Active display page
  347. CursorDX        equ $
  348. CursorCol       resb 1            ; Cursor column for message file
  349. CursorRow       resb 1            ; Cursor row for message file
  350. ScreenSize      equ $
  351. VidCols         resb 1            ; Columns on screen-1
  352. VidRows         resb 1            ; Rows on screen-1
  353. RetryCount      resb 1            ; Used for disk access retries
  354. KbdFlags    resb 1            ; Check for keyboard escapes
  355. LoadFlags    resb 1            ; Loadflags from kernel
  356. MNameBuf        resb 11                ; Generic mangled file name buffer
  357. InitRD          resb 11                 ; initrd= mangled name
  358. KernelCName     resb 13                 ; Unmangled kernel name
  359. InitRDCName     resb 13                ; Unmangled initrd name
  360.  
  361.         section .text
  362.                 org 7C00h
  363. ;
  364. ; Primary entry point.  Tempting as though it may be, we can't put the
  365. ; initial "cli" here; the jmp opcode in the first byte is part of the
  366. ; "magic number" (using the term very loosely) for the DOS superblock.
  367. ;
  368. bootsec        equ $
  369.         jmp short start        ; 2 bytes
  370.         nop            ; 1 byte
  371. ;
  372. ; "Superblock" follows -- it's in the boot sector, so it's already
  373. ; loaded and ready for us
  374. ;
  375. bsOemName    db 'SYSLINUX'        ; The SYS command sets this, so...
  376. superblock    equ $
  377. bsBytesPerSec    zw 1
  378. bsSecPerClust    zb 1
  379. bsResSectors    zw 1
  380. bsFATs        zb 1
  381. bsRootDirEnts    zw 1
  382. bsSectors    zw 1
  383. bsMedia        zb 1
  384. bsFATsecs    zw 1
  385. bsSecPerTrack    zw 1
  386. bsHeads        zw 1
  387. bsHiddenSecs    equ $
  388. bsHidden1    zw 1
  389. bsHidden2    zw 1
  390. bsHugeSectors    equ $
  391. bsHugeSec1    zw 1
  392. bsHugeSec2    zw 1
  393. bsDriveNumber    zb 1
  394. bsReserved1    zb 1
  395. bsBootSignature zb 1            ; 29h if the following fields exist
  396. bsVolumeID    zd 1
  397. bsVolumeLabel    zb 11
  398. bsFileSysType    zb 8            ; Must be FAT12 for this version
  399. superblock_len    equ $-superblock
  400. ;
  401. ; Note we don't check the constraints above now; we did that at install
  402. ; time (we hope!)
  403. ;
  404.  
  405. ;floppy_table    equ $            ; No sense in wasting memory, overwrite start
  406.  
  407. start:
  408.         cli            ; No interrupts yet, please
  409.         cld            ; Copy upwards
  410. ;
  411. ; Set up the stack
  412. ;
  413.         xor cx,cx
  414.         mov ss,cx
  415.         mov sp,StackBuf        ; Just below BSS
  416.         mov es,cx
  417. ;
  418. ; DS:SI may contain a partition table entry.  Preserve it for us.
  419. ;
  420.         mov cl,8        ; Save partition info (CH == 0)
  421.         mov di,PartInfo
  422.         rep movsw
  423. ;
  424. ; Now sautee the BIOS floppy info block to that it will support decent-
  425. ; size transfers; the floppy block is 11 bytes and is stored in the
  426. ; INT 1Eh vector (brilliant waste of resources, eh?)
  427. ;
  428. ; Of course, if BIOSes had been properly programmed, we wouldn't have
  429. ; had to waste precious boot sector space with this code.
  430. ;
  431. ; This code no longer fits.  Hope that noone really needs it anymore.
  432. ; (If so, it needs serious updating.)  In fact, some indications is that
  433. ; this code does more harm than good with all the new kinds of drives and
  434. ; media.
  435. ;
  436. %ifdef SUPPORT_REALLY_BROKEN_BIOSES
  437.         lds si,[ss:fdctab]    ; DS:SI -> original
  438.         push ds            ; Save on stack in case
  439.         push si            ; we have to bail
  440.         push bx
  441.         mov cx,6        ; 12 bytes
  442.         mov di,floppy_table
  443.         push di
  444.         cld
  445.         rep movsw        ; Faster to move words
  446.         pop di
  447.         mov ds,ax        ; Now we can point DS to here, too
  448.         mov cl,[bsSecPerTrack]  ; Patch the sector count
  449.         mov [di+4],cl
  450.         mov [fdctab+2],ax    ; Segment 0
  451.         mov [fdctab],di        ; offset floppy_block
  452. %else
  453.         mov ds,cx        ; CX == 0
  454. %endif
  455. ;
  456. ; Ready to enable interrupts, captain
  457. ;
  458.         sti
  459. ;
  460. ; The drive number and possibly partition information was passed to us
  461. ; by the BIOS or previous boot loader (MBR).  Current "best practice" is to
  462. ; trust that rather than what the superblock contains.
  463. ;
  464. ; Would it be better to zero out bsHidden if we don't have a partition table?
  465. ;
  466. ; Note: di points to beyond the end of PartInfo
  467. ;
  468.         mov [bsDriveNumber],dl
  469.         test dl,80h        ; If floppy disk (00-7F), assume no
  470.         jz not_harddisk        ; partition table
  471.         test byte [di-16],7Fh    ; Sanity check: "active flag" should
  472.         jnz no_partition    ; be 00 or 80
  473.         lea si,[di-8]        ; Partition offset (dword)
  474.         mov di,bsHidden1
  475.         mov cl,2        ; CH == 0
  476.         rep movsw
  477. no_partition:
  478. ;
  479. ; Get disk drive parameters (don't trust the superblock.)  Don't do this for
  480. ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
  481. ; what the *drive* supports, not about the *media*.  Fortunately floppy disks
  482. ; tend to have a fixed, well-defined geometry which is stored in the superblock.
  483. ;
  484.         ; DL == drive # still
  485.         mov ah,08h
  486.         int 13h
  487.         jc no_driveparm
  488.         and ah,ah
  489.         jnz no_driveparm
  490.         inc dh            ; Contains # of heads - 1
  491.         mov [bsHeads],dh
  492.         and cx,3fh
  493.         mov [bsSecPerTrack],cx
  494. no_driveparm:
  495. not_harddisk:
  496. ;
  497. ; Now we have to do some arithmetric to figure out where things are located.
  498. ; If Micro$oft had had brains they would already have done this for us,
  499. ; and stored it in the superblock at format time, but here we go,
  500. ; wasting precious boot sector space again...
  501. ;
  502. debugentrypt:
  503.         xor ax,ax        ; INT 13:08 destroys ES
  504.         mov es,ax
  505.         mov al,[bsFATs]        ; Number of FATs (AH == 0)
  506.         mul word [bsFATsecs]    ; Get the size of the FAT area
  507.         add ax,[bsHidden1]    ; Add hidden sectors
  508.         adc dx,[bsHidden2]
  509.         add ax,[bsResSectors]    ; And reserved sectors
  510.         adc dx,byte 0
  511.  
  512.         mov [RootDir1],ax    ; Location of root directory
  513.         mov [RootDir2],dx
  514.         mov [DataArea1],ax
  515.         mov [DataArea2],dx
  516.         push ax
  517.         push dx
  518.  
  519.         mov ax,32        ; Size of a directory entry
  520.         mul word [bsRootDirEnts]
  521.         mov bx,[bsBytesPerSec]
  522.         add ax,bx        ; Round up, not down
  523.         dec ax
  524.         div bx            ; Now we have the size of the root dir
  525.         mov [RootDirSize],ax
  526.         mov [DirScanCtr],ax
  527.         add bx,trackbuf-31
  528.         mov [EndofDirSec],bx    ; End of a single directory sector
  529.  
  530.         add [DataArea1],ax
  531.         adc word [DataArea2],byte 0
  532.  
  533.         pop dx            ; Reload root directory starting point
  534.         pop ax
  535. ;
  536. ; Now the fun begins.  We have to search the root directory for
  537. ; LDLINUX.SYS and load the first sector, so we have a little more
  538. ; space to have fun with.  Then we can go chasing through the FAT.
  539. ; Joy!!
  540. ;
  541. sd_nextsec:    push ax
  542.         push dx
  543.         mov bx,trackbuf
  544.         push bx
  545.         call getonesec
  546.         pop si
  547. sd_nextentry:    cmp byte [si],0        ; Directory high water mark
  548.         je kaboom
  549.         mov di,ldlinux_name
  550.         mov cx,11
  551.         push si
  552.         repe cmpsb
  553.         pop si
  554.         je found_it
  555.         add si,byte 32        ; Distance to next
  556.         cmp si,[EndofDirSec]
  557.         jb sd_nextentry
  558.         pop dx
  559.         pop ax
  560.         add ax,byte 1
  561.         adc dx,byte 0
  562.         dec word [DirScanCtr]
  563.         jnz sd_nextsec
  564. ;
  565. ; kaboom: write a message and bail out.
  566. ;
  567. kaboom:
  568.         xor si,si
  569.         mov ss,si        
  570.         mov sp,StackBuf     ; Reset stack
  571.         mov ds,si        ; Reset data segment
  572.         mov si,bailmsg
  573.         call writestr        ; Returns with AL = 0
  574.         cbw            ; AH <- 0
  575.         int 16h            ; Wait for keypress
  576.         int 19h            ; And try once more to boot...
  577. norge:        jmp short norge        ; If int 19h returned; this is the end
  578.  
  579. ;
  580. ; found_it: now we compute the location of the first sector, then
  581. ;        load it and JUMP (since we're almost out of space)
  582. ;
  583. found_it:    ; Note: we actually leave two words on the stack here
  584.         ; (who cares?)
  585.         xor ax,ax
  586.         mov al,[bsSecPerClust]
  587.         mov bp,ax        ; Load an entire cluster
  588.         mov bx,[si+26]        ; First cluster
  589.         mov [RunLinClust],bx    ; Save for later use
  590.         dec bx            ; First cluster is "cluster 2"
  591.         dec bx
  592.         mul bx
  593.         add ax,[DataArea1]
  594.         adc dx,[DataArea2]
  595.         mov bx,ldlinux_sys
  596.         call getlinsec
  597.         mov si,bs_magic
  598.         mov di,ldlinux_magic
  599.         mov cx,magic_len
  600.         repe cmpsb        ; Make sure that the bootsector
  601.         jne kaboom        ; matches LDLINUX.SYS
  602. ;
  603. ; Done! Jump to the entry point!
  604. ;
  605.         jmp ldlinux_ent
  606.  
  607. ;
  608. ;
  609. ; writestr: write a null-terminated string to the console
  610. ;
  611. writestr:
  612. wstr_1:         lodsb
  613.         and al,al
  614.                 jz return
  615.         mov ah,0Eh        ; Write to screen as TTY
  616.         mov bx,0007h        ; White on black, current page
  617.         int 10h
  618.         jmp short wstr_1
  619. ;
  620. ; disk_error: decrement the retry count and bail if zero
  621. ;
  622. disk_error:    dec si            ; SI holds the disk retry counter
  623.         jz kaboom
  624.         xchg ax,bx        ; Shorter than MOV
  625.         pop bx            ; <I>
  626.         pop cx            ; <H>
  627.         pop dx            ; <G>
  628.         jmp short disk_try_again
  629.  
  630. return:        ret
  631.  
  632. ;
  633. ; getonesec: like getlinsec, but pre-sets the count to 1
  634. ;
  635. getonesec:
  636.         mov bp,1
  637.         ; Fall through to getlinsec
  638.  
  639. ;
  640. ; getlinsec: load a sequence of BP floppy sector given by the linear sector
  641. ;         number in DX:AX into the buffer at ES:BX.    We try to optimize
  642. ;         by loading up to a whole track at a time, but the user
  643. ;         is responsible for not crossing a 64K boundary.
  644. ;         (Yes, BP is weird for a count, but it was available...)
  645. ;
  646. ;         On return, BX points to the first byte after the transferred
  647. ;         block.
  648. ;
  649. ;         The "stupid patch area" gets replaced by the code
  650. ;         mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
  651. ;         the -s option.
  652. ;
  653. ; Stylistic note: use "xchg" instead of "mov" when the source is a register
  654. ; that is dead from that point; this saves space.  However, please keep
  655. ; the order to dst,src to keep things sane.
  656. ;
  657. getlinsec:
  658.         mov si,[bsSecPerTrack]
  659.         ;
  660.         ; Dividing by sectors to get (track,sector): we may have
  661.         ; up to 2^18 tracks, so we need to do this in two steps
  662.         ; to produce a 32-bit quotient.
  663.         ;
  664.         xchg cx,ax        ; CX <- LSW of LBA
  665.         xchg ax,dx
  666.         xor dx,dx        ; DX:AX now == MSW of LBA
  667.         div si            ; Obtain MSW of track #
  668.         xchg ax,cx        ; Remainder -> MSW of new dividend
  669.                     ; LSW of LBA -> LSW of new dividend
  670.                     ; Quotient -> MSW of track # 
  671.         div si            ; Obtain LSW of track #, remainder
  672.         xchg cx,dx        ; CX <- Sector index (0-based)
  673.                     ; DX <- MSW of track #
  674.         div word [bsHeads]    ; Convert track to head/cyl
  675.         ;
  676.         ; Now we have AX = cyl, DX = head, CX = sector (0-based),
  677.         ; BP = sectors to transfer, SI = bsSecPerTrack,
  678.         ; ES:BX = data target
  679.         ;
  680. gls_nextchunk:    push si            ; <A> bsSecPerTrack
  681.         push bp            ; <B> Sectors to transfer
  682.  
  683. __BEGIN_STUPID_PATCH_AREA:
  684.         sub si,cx        ; Sectors left on track
  685.         cmp bp,si
  686.         jna gls_lastchunk
  687.         mov bp,si        ; No more than a trackful, please!
  688. __END_STUPID_PATCH_AREA:
  689. gls_lastchunk:    
  690.         push ax            ; <C> Cylinder #
  691.         push dx            ; <D> Head #
  692.         push bp            ; <E> Number of sectors we're transferring
  693.  
  694.         push cx            ; <F> Sector #
  695.         mov cl,6        ; Because IBM was STOOPID
  696.         shl ah,cl        ; and thought 8 bits were enough
  697.                     ; then thought 10 bits were enough...
  698.         pop cx            ; <F> Sector #
  699.         push cx
  700.         inc cx            ; Sector numbers are 1-based
  701.         or cl,ah
  702.         mov ch,al
  703.         mov dh,dl
  704.         mov dl,[bsDriveNumber]
  705.         xchg ax,bp        ; Sector to transfer count
  706.                     ; (xchg shorter than mov)
  707.         mov ah,02h        ; Read it!
  708. ;
  709. ; Do the disk transfer... save the registers in case we fail :(
  710. ;
  711.         mov si,retry_count    ; # of times to retry a disk access
  712. disk_try_again: push dx            ; <G>
  713.         push cx            ; <H>
  714.         push bx            ; <I>
  715.         push ax            ; <J>
  716.         push si            ; <K>
  717.         int 13h
  718.         pop si            ; <K>
  719.         pop bx            ; <J>
  720.         jc disk_error
  721. ;
  722. ; Disk access successful
  723. ;
  724.         pop bx            ; <I> Buffer location
  725.         pop ax            ; <H> No longer needed
  726.         pop ax            ; <G> No longer needed
  727.         pop cx            ; <F> Sector #
  728.         pop di            ; <E> Sector transferred count
  729.         mov ax,di        ; Reduce sector left count
  730.         mul word [bsBytesPerSec] ; Figure out how much to advance ptr
  731.         add bx,ax        ; Update buffer location
  732.         pop dx            ; <D> Head #
  733.         pop ax            ; <C> Cyl #
  734.         pop bp            ; <B> Sectors left to transfer
  735.         pop si            ; <A> Number of sectors/track
  736.         sub bp,di        ; Reduce with # of sectors just read
  737.         jz return        ; Done!
  738.         add cx,di
  739.         cmp cx,si
  740.         jb gls_nextchunk
  741.         inc dx            ; Next track on cyl
  742.         cmp dx,[bsHeads]    ; Was this the last one?
  743.         jb gls_nonewcyl
  744.         inc ax            ; If so, new cylinder
  745.         xor dx,dx        ; First head on new cylinder
  746. gls_nonewcyl:    sub cx,si        ; First sector on new track
  747.         jmp short gls_nextchunk
  748.  
  749. bailmsg:    db 'Boot failed', 0Dh, 0Ah, 0
  750.  
  751. bs_checkpt    equ $            ; Must be <= 1EFh
  752.  
  753.         zb 1EFh-($-$$)
  754. bs_magic    equ $            ; From here to the magic_len equ
  755.                     ; must match ldlinux_magic
  756. ldlinux_name:    db 'LDLINUX SYS'    ; Looks like this in the root dir
  757.         dd HEXDATE        ; Hopefully unique between compiles
  758.  
  759. bootsignature    dw 0AA55h
  760. magic_len    equ $-bs_magic
  761.  
  762. ;
  763. ; ===========================================================================
  764. ;  End of boot sector
  765. ; ===========================================================================
  766. ;  Start of LDLINUX.SYS
  767. ; ===========================================================================
  768.  
  769. ldlinux_sys:
  770.  
  771. syslinux_banner    db 0Dh, 0Ah, 'SYSLINUX ', version_str, ' ', date, ' ', 0
  772.         db 0Dh, 0Ah, 1Ah    ; EOF if we "type" this in DOS
  773.  
  774. ldlinux_magic    db 'LDLINUX SYS'
  775.         dd HEXDATE
  776.         dw 0AA55h
  777.  
  778.         align 4
  779.  
  780. ;
  781. ; Entry point.  Note that some BIOSes are buggy and put the boot sector
  782. ; at 07C0:0000 instead of 0000:7C00 and the like.  We don't want to add
  783. ; anything more to the boot sector, so it is written to not assume a fixed
  784. ; value in CS, but we don't want to deal with that anymore from now on.
  785. ;
  786. ldlinux_ent:
  787.         jmp 0:ldlinux_ent2
  788. ldlinux_ent2:
  789. ;
  790. ; Tell the user we got this far
  791. ;
  792.         mov si,syslinux_banner
  793.         call writestr
  794. ;
  795. ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
  796. ; We can really only rely on a single sector having been loaded.  Hence
  797. ; we should load the FAT into RAM and start chasing pointers...
  798. ;
  799.         mov dx,1            ; 64K
  800.         xor ax,ax
  801.         div word [bsBytesPerSec]    ; sectors/64K
  802.         mov si,ax
  803.  
  804.         push es
  805.         mov bx,fat_seg            ; Load into fat_seg:0000
  806.         mov es,bx
  807.         
  808.         mov ax,[bsHidden1]        ; Hidden sectors
  809.         mov dx,[bsHidden2]
  810.         add ax,[bsResSectors]        ; plus reserved sectors = FAT
  811.         adc dx,byte 0
  812.         mov cx,[bsFATsecs]        ; Sectors/FAT
  813. fat_load_loop:    
  814.         mov bp,cx
  815.         cmp bp,si
  816.         jna fat_load
  817.         mov bp,si            ; A full 64K moby
  818. fat_load:    
  819.         xor bx,bx            ; Offset 0 in the current ES
  820.         call getlinsecsr
  821.         sub cx,bp
  822.         jz fat_load_done        ; Last moby?
  823.         add ax,bp            ; Advance sector count
  824.         adc dx,byte 0
  825.         mov bx,es            ; Next 64K moby
  826.         add bx,1000h
  827.         mov es,bx
  828.         jmp short fat_load_loop
  829. fat_load_done:
  830.         pop es
  831. ;
  832. ; Fine, now we have the FAT in memory.    How big is a cluster, really?
  833. ; Also figure out how many clusters will fit in an 8K buffer, and how
  834. ; many sectors and bytes that is
  835. ;
  836.         mov di,[bsBytesPerSec]        ; Used a lot below
  837.  
  838.         mov al,[bsSecPerClust]        ; We do this in the boot
  839.         xor ah,ah            ; sector, too, but there
  840.         mov [SecPerClust],ax        ; wasn't space to save it
  841.         mov si,ax            ; Also used a lot...
  842.         mul di
  843.         mov [ClustSize],ax        ; Bytes/cluster
  844.         mov bx,ax
  845.         mov ax,trackbufsize
  846.         xor dx,dx
  847.         div bx
  848.         mov [BufSafe],ax        ; # of cluster in trackbuf
  849.         mul word [SecPerClust]
  850.         mov [BufSafeSec],ax
  851.         mul di
  852.         mov [BufSafeBytes],ax
  853.         add ax,getcbuf            ; Size of getcbuf is the same
  854.         mov [EndOfGetCBuf],ax        ; as for trackbuf
  855. ;
  856. ; FAT12 or FAT16?  This computation is fscking ridiculous...
  857. ;
  858.         xor dx,dx
  859.         xor cx,cx
  860.         mov ax,[bsSectors]
  861.         and ax,ax
  862.         jnz have_secs
  863.         mov ax,[bsHugeSectors]
  864.         mov dx,[bsHugeSectors+2]
  865. have_secs:    sub ax,[bsResSectors]
  866.         sbb dx,byte 0
  867.         mov cl,[bsFATs]
  868. sec_fat_loop:    sub ax,[bsFATsecs]
  869.         sbb dx,byte 0
  870.         loop sec_fat_loop
  871.         push ax
  872.         push dx
  873.         mov ax,[bsRootDirEnts]
  874.         mov bx,32            ; Smaller than shift since we
  875.         mul bx                ; need the doubleword product
  876.         add ax,di
  877.         adc dx,byte 0
  878.         sub ax,byte 1
  879.         sbb dx,byte 0
  880.         div di
  881.         mov bx,ax
  882.         pop dx
  883.         pop ax
  884.         sub ax,bx
  885.         sbb dx,byte 0
  886.         div si
  887.         cmp ax,4086            ; Right value?
  888.         mov ax,nextcluster_fat16
  889.         ja have_fat_type
  890. have_fat12:    mov ax,nextcluster_fat12
  891. have_fat_type:    mov word [NextCluster],ax
  892.  
  893. ;
  894. ; Now we read the rest of LDLINUX.SYS.    Don't bother loading the first
  895. ; cluster again, though.
  896. ;
  897. load_rest:
  898.         mov cx,[ClustSize]
  899.         mov bx,ldlinux_sys
  900.         add bx,cx
  901.         mov si,[RunLinClust]
  902.         call [NextCluster]
  903.         xor dx,dx
  904.         mov ax,ldlinux_len-1        ; To be on the safe side
  905.         add ax,cx
  906.         div cx                ; the number of clusters
  907.         dec ax                ; We've already read one
  908.         jz all_read_jmp
  909.         mov cx,ax
  910.         call getfssec
  911. ;
  912. ; All loaded up
  913. ;
  914. all_read_jmp:
  915.         jmp all_read
  916. ;
  917. ; -----------------------------------------------------------------------------
  918. ; Subroutines that have to be in the first sector
  919. ; -----------------------------------------------------------------------------
  920. ;
  921. ; getfssec: Get multiple clusters from a file, given the starting cluster.
  922. ;
  923. ;    This routine makes sure the subtransfers do not cross a 64K boundary,
  924. ;    and will correct the situation if it does, UNLESS *sectors* cross
  925. ;    64K boundaries.
  926. ;
  927. ;    ES:BX    -> Buffer
  928. ;    SI    -> Starting cluster number (2-based)
  929. ;    CX    -> Cluster count (0FFFFh = until end of file)
  930. ;
  931.                         ; 386 check
  932. getfssec:
  933. getfragment:    xor bp,bp            ; Fragment sector count
  934.         mov ax,si            ; Get sector address
  935.         dec ax                ; Convert to 0-based
  936.         dec ax
  937.         mul word [SecPerClust]
  938.         add ax,[DataArea1]
  939.         adc dx,[DataArea2]
  940. getseccnt:                    ; See if we can read > 1 clust
  941.         add bp,[SecPerClust]
  942.         dec cx                ; Reduce clusters left to find
  943.         mov di,si            ; Predict next cluster
  944.         inc di
  945.         call [NextCluster]
  946.         jc gfs_eof            ; At EOF?
  947.         jcxz endfragment        ; Or was it the last we wanted?
  948.         cmp si,di            ; Is file continuous?
  949.         jz getseccnt            ; Yes, we can get
  950. endfragment:    clc                ; Not at EOF
  951. gfs_eof:    pushf                ; Remember EOF or not
  952.         push si
  953.         push cx
  954. gfs_getchunk:
  955.         push ax
  956.         push dx
  957.         mov ax,es            ; Check for 64K boundaries.
  958.         mov cl,4
  959.         shl ax,cl
  960.         add ax,bx
  961.         xor dx,dx
  962.         neg ax
  963.         jnz gfs_partseg
  964.         inc dx                ; Full 64K segment
  965. gfs_partseg:
  966.         div word [bsBytesPerSec]    ; How many sectors fit?
  967.         mov si,bp
  968.         sub si,ax            ; Compute remaining sectors
  969.         jbe gfs_lastchunk
  970.         mov bp,ax
  971.         pop dx
  972.         pop ax
  973.         call getlinsecsr
  974.         add ax,bp
  975.         adc dx,byte 0
  976.         mov bp,si            ; Remaining sector count
  977.         jmp short gfs_getchunk
  978. gfs_lastchunk:    pop dx
  979.         pop ax        
  980.         call getlinsec
  981.         pop cx
  982.         pop si
  983.         popf
  984.         jcxz gfs_return            ; If we hit the count limit
  985.         jnc getfragment            ; If we didn't hit EOF
  986. gfs_return:    ret
  987.  
  988. ;
  989. ; getlinsecsr: save registers, call getlinsec, restore registers
  990. ;
  991. getlinsecsr:    push ax
  992.         push dx
  993.         push cx
  994.         push bp
  995.         push si
  996.         push di
  997.         call getlinsec
  998.         pop di
  999.         pop si
  1000.         pop bp
  1001.         pop cx
  1002.         pop dx
  1003.         pop ax
  1004.         ret
  1005.  
  1006. ;
  1007. ; nextcluster: Advance a cluster pointer in SI to the next cluster
  1008. ;           pointed at in the FAT tables (note: FAT12 assumed)
  1009. ;           Sets CF on return if end of file.
  1010. ;
  1011. ;           The variable NextCluster gets set to the appropriate
  1012. ;           value here.
  1013. ;
  1014. nextcluster_fat12:
  1015.         push ax
  1016.         push ds
  1017.         mov ax,fat_seg
  1018.         mov ds,ax
  1019.         mov ax,si            ; Multiply by 3/2
  1020.         shr ax,1
  1021.         pushf                ; CF now set if odd
  1022.         add si,ax
  1023.         mov si,[si]
  1024.         popf
  1025.         jnc nc_even
  1026.         shr si,1            ; Needed for odd only
  1027.         shr si,1
  1028.         shr si,1
  1029.         shr si,1
  1030. nc_even:
  1031.         and si,0FFFh
  1032.         cmp si,0FF0h            ; Clears CF if at end of file
  1033.         cmc                ; But we want it SET...
  1034.         pop ds
  1035.         pop ax
  1036. nc_return:    ret
  1037.  
  1038. ;
  1039. ; FAT16 decoding routine.  Note that a 16-bit FAT can be up to 128K,
  1040. ; so we have to decide if we're in the "low" or the "high" 64K-segment...
  1041. ;
  1042. nextcluster_fat16:
  1043.         push ax
  1044.         push ds
  1045.         mov ax,fat_seg
  1046.         shl si,1
  1047.         jnc .seg0
  1048.         mov ax,fat_seg+1000h
  1049. .seg0:        mov ds,ax
  1050.         mov si,[si]
  1051.         cmp si,0FFF0h
  1052.         cmc
  1053.         pop ds
  1054.         pop ax
  1055.         ret
  1056. ;
  1057. ; Debug routine
  1058. ;
  1059. %ifdef debug
  1060. safedumpregs:
  1061.         cmp word [Debug_Magic],0D00Dh
  1062.         jnz nc_return
  1063.         jmp dumpregs
  1064. %endif
  1065.  
  1066. rl_checkpt    equ $                ; Must be <= 400h
  1067.  
  1068. ; ----------------------------------------------------------------------------
  1069. ;  End of code and data that have to be in the first sector
  1070. ; ----------------------------------------------------------------------------
  1071.  
  1072. all_read:
  1073. ;
  1074. ; Let the user (and programmer!) know we got this far.  This used to be
  1075. ; in Sector 1, but makes a lot more sense here.
  1076. ;
  1077.         mov si,copyright_str
  1078.         call writestr
  1079. ;
  1080. ; Check that no moron is trying to boot Linux on a 286 or so.  According
  1081. ; to Intel, the way to check is to see if the high 4 bits of the FLAGS
  1082. ; register are either all stuck at 1 (8086/8088) or all stuck at 0
  1083. ; (286 in real mode), if not it is a 386 or higher.  They didn't
  1084. ; say how to check for a 186/188, so I *hope* it falls out as a 8086
  1085. ; or 286 in this test.
  1086. ;
  1087. ; Also, provide an escape route in case it doesn't work.
  1088. ;
  1089. check_escapes:
  1090.         mov ah,02h            ; Check keyboard flags
  1091.         int 16h
  1092.         mov [KbdFlags],al        ; Save for boot prompt check
  1093.         test al,04h            ; Ctrl->skip 386 check
  1094.         jnz skip_checks
  1095. test_8086:
  1096.         pushf                ; Get flags
  1097.         pop ax
  1098.         and ax,0FFFh            ; Clear top 4 bits
  1099.         push ax                ; Load into FLAGS
  1100.         popf
  1101.         pushf                ; And load back
  1102.         pop ax
  1103.         and ax,0F000h            ; Get top 4 bits
  1104.         cmp ax,0F000h            ; If set -> 8086/8088
  1105.         je not_386
  1106. test_286:
  1107.         pushf                ; Get flags
  1108.         pop ax
  1109.         or ax,0F000h            ; Set top 4 bits
  1110.         push ax
  1111.         popf
  1112.         pushf
  1113.         pop ax
  1114.         and ax,0F000h            ; Get top 4 bits
  1115.         jnz is_386            ; If not clear -> 386
  1116. not_386:
  1117.         mov si,err_not386
  1118.         call writestr
  1119.         jmp kaboom
  1120. is_386:
  1121.         ; Now we know it's a 386 or higher
  1122. ;
  1123. ; Now check that there is at least 608K of low (DOS) memory
  1124. ; (608K = 9800h segments)
  1125. ;
  1126.         int 12h
  1127.         cmp ax,608
  1128.         jae enough_ram
  1129.         mov si,err_noram
  1130.         call writestr
  1131.         jmp kaboom
  1132. enough_ram:
  1133. skip_checks:
  1134.  
  1135. ;
  1136. ; Initialization that does not need to go into the any of the pre-load
  1137. ; areas
  1138. ;
  1139.         call adjust_screen
  1140. ;
  1141. ; Now we're all set to start with our *real* business.    First load the
  1142. ; configuration file (if any) and parse it.
  1143. ;
  1144. ; In previous versions I avoided using 32-bit registers because of a
  1145. ; rumour some BIOSes clobbered the upper half of 32-bit registers at
  1146. ; random.  I figure, though, that if there are any of those still left
  1147. ; they probably won't be trying to install Linux on them...
  1148. ;
  1149. ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
  1150. ; to take'm out.  In fact, we may want to put them back if we're going
  1151. ; to boot ELKS at some point.
  1152. ;
  1153.         mov si,linuxauto_cmd        ; Default command: "linux auto"
  1154.         mov di,default_cmd
  1155.                 mov cx,linuxauto_len
  1156.         rep movsb
  1157.  
  1158.         mov di,KbdMap            ; Default keymap 1:1
  1159.         xor al,al
  1160.         mov cx,256
  1161. mkkeymap:    stosb
  1162.         inc al
  1163.         loop mkkeymap
  1164.  
  1165. ;
  1166. ; Load configuration file
  1167. ;
  1168.         mov di,syslinux_cfg
  1169.         call open
  1170.         jz near no_config_file
  1171. parse_config:
  1172.         call getkeyword
  1173.                 jc near end_config_file        ; Config file loaded
  1174.         cmp ax,'de'            ; DEfault
  1175.         je pc_default
  1176.         cmp ax,'ap'            ; APpend
  1177.         je pc_append
  1178.         cmp ax,'ti'            ; TImeout
  1179.         je near pc_timeout
  1180.         cmp ax,'pr'            ; PRompt
  1181.         je near pc_prompt
  1182.         cmp ax,'fo'            ; FOnt
  1183.         je near pc_font
  1184.         cmp ax,'kb'            ; KBd
  1185.         je near pc_kbd
  1186.         cmp ax,'di'            ; DIsplay
  1187.         je near pc_display
  1188.         cmp ax,'la'            ; LAbel
  1189.         je near pc_label
  1190.         cmp ax,'ke'            ; KErnel
  1191.         je pc_kernel
  1192.                 cmp ax,'im'                     ; IMplicit
  1193.                 je near pc_implicit
  1194.         cmp al,'f'            ; F-key
  1195.         jne parse_config
  1196.         jmp pc_fkey
  1197.  
  1198. pc_default:    mov di,default_cmd        ; "default" command
  1199.         call getline
  1200.         mov si,auto_cmd            ; add "auto"+null
  1201.                 mov cx,auto_len
  1202.         rep movsb
  1203.         jmp short parse_config
  1204.  
  1205. pc_append:      cmp word [VKernelCtr],byte 0    ; "append" command
  1206.         ja pc_append_vk
  1207.                 mov di,AppendBuf
  1208.         call getline
  1209.                 sub di,AppendBuf
  1210. pc_app1:        mov [AppendLen],di
  1211.                 jmp short parse_config
  1212. pc_append_vk:    mov di,VKernelBuf+vk_append    ; "append" command (vkernel)
  1213.         call getline
  1214.         sub di,VKernelBuf+vk_append
  1215.                 cmp di,byte 2
  1216.                 jne pc_app2
  1217.                 cmp byte [VKernelBuf+vk_append],'-'
  1218.                 jne pc_app2
  1219.                 mov di,0                        ; If "append -" -> null string
  1220. pc_app2:        mov [VKernelBuf+vk_appendlen],di
  1221.         jmp short parse_config_2    
  1222.  
  1223. pc_kernel:    cmp word [VKernelCtr],byte 0    ; "kernel" command
  1224.         je near parse_config        ; ("label" section only)
  1225.         mov di,trackbuf
  1226.         push di
  1227.         call getline
  1228.         pop si
  1229.         mov di,VKernelBuf+vk_rname
  1230.         call mangle_name
  1231.         jmp short parse_config_2
  1232.  
  1233. pc_timeout:    call getint            ; "timeout" command
  1234.         jc parse_config_2
  1235.         mov ax,0D215h            ; There are approx 1.D215h
  1236.         mul bx                ; clock ticks per 1/10 s
  1237.         add bx,dx
  1238.         mov [KbdTimeOut],bx
  1239.         jmp short parse_config_2
  1240.  
  1241. pc_display:    call pc_getfile            ; "display" command
  1242.         jz parse_config_2        ; File not found?
  1243.         call get_msg_file        ; Load and display file
  1244. parse_config_2: jmp parse_config
  1245.  
  1246. pc_prompt:    call getint            ; "prompt" command
  1247.         jc parse_config_2
  1248.         mov [ForcePrompt],bx
  1249.         jmp short parse_config_2
  1250.  
  1251. pc_implicit:    call getint                     ; "implicit" command
  1252.                 jc parse_config_2
  1253.                 mov [AllowImplicit],bx
  1254.                 jmp short parse_config_2
  1255.  
  1256. pc_fkey:    sub ah,'1'
  1257.         jnb pc_fkey1
  1258.         mov ah,9            ; F10
  1259. pc_fkey1:    xor cx,cx
  1260.         mov cl,ah
  1261.         push cx
  1262.         mov ax,1
  1263.         shl ax,cl
  1264.         or [FKeyMap], ax        ; Mark that we have this loaded
  1265.         mov di,trackbuf
  1266.         push di
  1267.         call getline            ; Get filename to display
  1268.         pop si
  1269.         pop di
  1270.         shl di,4            ; Multiply number by 16
  1271.         add di,FKeyName
  1272.         call mangle_name        ; Mangle file name
  1273.         jmp short parse_config_2
  1274.  
  1275. pc_label:    call commit_vk            ; Commit any current vkernel
  1276.         mov di,trackbuf            ; Get virtual filename
  1277.         push di
  1278.         call getline
  1279.         pop si
  1280.         mov di,VKernelBuf+vk_vname
  1281.         call mangle_name        ; Mangle virtual name
  1282.         inc word [VKernelCtr]        ; One more vkernel
  1283.         mov si,VKernelBuf+vk_vname     ; By default, rname == vname
  1284.         mov di,VKernelBuf+vk_rname
  1285.         mov cx,11
  1286.         rep movsb
  1287.                 mov si,AppendBuf             ; Default append==global append
  1288.                 mov di,VKernelBuf+vk_append
  1289.                 mov cx,[AppendLen]
  1290.                 mov [VKernelBuf+vk_appendlen],cx
  1291.                 rep movsb
  1292.         jmp short parse_config_2
  1293.  
  1294. pc_font:    call pc_getfile            ; "font" command
  1295.         jz parse_config_2        ; File not found?
  1296.         call loadfont            ; Load and install font
  1297.         jmp short parse_config_3
  1298.  
  1299. pc_kbd:        call pc_getfile            ; "kbd" command
  1300.         jz parse_config_3
  1301.         call loadkeys
  1302. parse_config_3:    jmp parse_config
  1303.  
  1304. ;
  1305. ; pc_getfile:    For command line options that take file argument, this
  1306. ;         routine decodes the file argument and runs it through searchdir
  1307. ;
  1308. pc_getfile:    mov di,trackbuf
  1309.         push di
  1310.         call getline
  1311.         pop si
  1312.         mov di,MNameBuf
  1313.         push di
  1314.         call mangle_name
  1315.         pop di
  1316.         jmp searchdir            ; Tailcall
  1317.  
  1318. ;
  1319. ; commit_vk: Store the current VKernelBuf into buffer segment
  1320. ;
  1321. commit_vk:
  1322.         cmp word [VKernelCtr],byte 0
  1323.         je cvk_ret            ; No VKernel = return
  1324.         cmp word [VKernelCtr],max_vk    ; Above limit?
  1325.         ja cvk_overflow
  1326.         mov di,[VKernelCtr]
  1327.         dec di
  1328.         shl di,vk_shift
  1329.         mov si,VKernelBuf
  1330.         mov cx,(vk_size >> 2)
  1331.         push es
  1332.         push word vk_seg
  1333.         pop es
  1334.         rep movsd            ; Copy to buffer segment
  1335.         pop es
  1336. cvk_ret:    ret
  1337. cvk_overflow:    mov word [VKernelCtr],max_vk    ; No more than max_vk, please
  1338.         ret
  1339.  
  1340. ;
  1341. ; End of configuration file
  1342. ;
  1343. end_config_file:
  1344.         call commit_vk            ; Commit any current vkernel
  1345. no_config_file:
  1346. ;
  1347. ; Check whether or not we are supposed to display the boot prompt.
  1348. ;
  1349. check_for_key:
  1350.         cmp word [ForcePrompt],byte 0    ; Force prompt?
  1351.         jnz enter_command
  1352.         test byte [KbdFlags],5Bh    ; Caps, Scroll, Shift, Alt
  1353.         jz near auto_boot        ; If neither, default boot
  1354.  
  1355. enter_command:
  1356.         mov si,boot_prompt
  1357.         call writestr
  1358.  
  1359.         mov di,command_line
  1360. ;
  1361. ; get the very first character -- we can either time
  1362. ; out, or receive a character press at this time.  Some dorky BIOSes stuff
  1363. ; a return in the buffer on bootup, so wipe the keyboard buffer first.
  1364. ;
  1365. clear_buffer:    mov ah,1            ; Check for pending char
  1366.         int 16h
  1367.         jz get_char_time
  1368.         xor ax,ax            ; Get char
  1369.         int 16h
  1370.         jmp short clear_buffer
  1371. get_char_time:    mov cx,[KbdTimeOut]
  1372.         and cx,cx
  1373.         jz get_char            ; Timeout == 0 -> no timeout
  1374.         inc cx                ; The first loop will happen
  1375.                         ; immediately as we don't
  1376.                         ; know the appropriate DX value
  1377. time_loop:    push cx
  1378. tick_loop:    push dx
  1379.         mov ah,1            ; Check for pending keystroke
  1380.         int 16h
  1381.         jnz get_char_pop
  1382.         xor ax,ax
  1383.         int 1Ah                ; Get time "of day"
  1384.         pop ax
  1385.         cmp dx,ax            ; Has the timer advanced?
  1386.         je tick_loop
  1387.         pop cx
  1388.         loop time_loop            ; If so, decrement counter
  1389.         jmp command_done        ; Timeout!
  1390. get_char_pop:    pop eax                ; Clear the stack
  1391. get_char:    xor ax,ax            ; Get char
  1392.         int 16h
  1393.         and al,al
  1394.         jz func_key
  1395.         mov bx,KbdMap            ; Keyboard map translation
  1396.         xlatb
  1397.         cmp al,' '            ; ASCII?
  1398.         jb not_ascii
  1399.         ja enter_char
  1400.         cmp di,command_line        ; Space must not be first
  1401.         je get_char
  1402. enter_char:    cmp di,max_cmd_len+command_line ; Check there's space
  1403.         jnb get_char
  1404.         stosb                ; Save it
  1405.         call writechr            ; Echo to screen
  1406.         jmp short get_char
  1407. not_ascii:    cmp al,0Dh            ; Enter
  1408.         je command_done
  1409.         cmp al,08h            ; Backspace
  1410.         jne get_char
  1411.         cmp di,command_line        ; Make sure there is anything
  1412.         je get_char            ; to erase
  1413.         dec di                ; Unstore one character
  1414.         mov si,wipe_char        ; and erase it from the screen
  1415.         call writestr
  1416.         jmp short get_char
  1417. func_key:
  1418.         push di
  1419.         cmp ah,68            ; F10
  1420.         ja get_char
  1421.         sub ah,59            ; F1
  1422.         jb get_char
  1423.         mov cl,ah
  1424.         shr ax,4            ; Convert to x16
  1425.         mov bx,1
  1426.         shl bx,cl
  1427.         and bx,[FKeyMap]
  1428.         jz get_char            ; Undefined F-key
  1429.         mov di,ax
  1430.         add di,FKeyName
  1431.         call searchdir
  1432.         jz fk_nofile
  1433.         call get_msg_file
  1434.         jmp short fk_wrcmd
  1435. fk_nofile:
  1436.         mov si,crlf_msg
  1437.         call writestr
  1438. fk_wrcmd:
  1439.         mov si,boot_prompt
  1440.         call writestr
  1441.         pop di                ; Command line write pointer
  1442.         push di
  1443.         mov byte [di],0            ; Null-terminate command line
  1444.         mov si,command_line
  1445.         call writestr            ; Write command line so far
  1446.         pop di
  1447.         jmp short get_char
  1448. auto_boot:
  1449.         mov si,default_cmd
  1450.         mov di,command_line
  1451.         mov cx,(max_cmd_len+4) >> 2
  1452.         rep movsd
  1453.         jmp short load_kernel
  1454. command_done:
  1455.         mov si,crlf_msg
  1456.         call writestr
  1457.         cmp di,command_line        ; Did we just hit return?
  1458.         je auto_boot
  1459.         xor al,al            ; Store a final null
  1460.         stosb
  1461.  
  1462. load_kernel:                    ; Load the kernel now
  1463. ;
  1464. ; First we need to mangle the kernel name the way DOS would...
  1465. ;
  1466.         mov si,command_line
  1467.                 mov di,KernelName
  1468.                 push si
  1469.                 push di
  1470.         call mangle_name
  1471.         pop di
  1472.                 pop si
  1473. ;
  1474. ; Fast-forward to first option (we start over from the beginning, since
  1475. ; mangle_name doesn't necessarily return a consistent ending state.)
  1476. ;
  1477. clin_non_wsp:   lodsb
  1478.                 cmp al,' '
  1479.                 ja clin_non_wsp
  1480. clin_is_wsp:    and al,al
  1481.                 jz clin_opt_ptr
  1482.                 lodsb
  1483.                 cmp al,' '
  1484.                 jbe clin_is_wsp
  1485. clin_opt_ptr:   dec si                          ; Point to first nonblank
  1486.                 mov [CmdOptPtr],si        ; Save ptr to first option
  1487. ;
  1488. ; Now check if it is a "virtual kernel"
  1489. ;
  1490.         mov cx,[VKernelCtr]
  1491.         push ds
  1492.         push word vk_seg
  1493.         pop ds
  1494.         cmp cx,byte 0
  1495.         je not_vk
  1496.         xor si,si            ; Point to first vkernel
  1497. vk_check:    pusha
  1498.         mov cx,11
  1499.         repe cmpsb            ; Is this it?
  1500.         je near vk_found
  1501.         popa
  1502.         add si,vk_size
  1503.         loop vk_check
  1504. not_vk:        pop ds
  1505. ;
  1506. ; Not a "virtual kernel" - check that's OK and construct the command line
  1507. ;
  1508.                 cmp word [AllowImplicit],byte 0
  1509.                 je bad_implicit
  1510.                 push es
  1511.                 push si
  1512.                 push di
  1513.                 mov di,real_mode_seg
  1514.                 mov es,di
  1515.                 mov si,AppendBuf
  1516.                 mov di,cmd_line_here
  1517.                 mov cx,[AppendLen]
  1518.                 rep movsb
  1519.                 mov [CmdLinePtr],di
  1520.                 pop di
  1521.                 pop si
  1522.                 pop es
  1523.         mov bx,exten_count << 2        ; Alternates to try
  1524. ;
  1525. ; Find the kernel on disk
  1526. ;
  1527. get_kernel:     mov byte [KernelName+11],0    ; Zero-terminate filename/extension
  1528.         mov eax,[KernelName+8]        ; Save initial extension
  1529.         mov [OrigKernelExt],eax
  1530. .search_loop:    push bx
  1531.                 mov di,KernelName              ; Search on disk
  1532.                 call searchdir
  1533.         pop bx
  1534.                 jnz kernel_good
  1535.         mov eax,[exten_table+bx]    ; Try a different extension
  1536.         mov [KernelName+8],eax
  1537.         sub bx,byte 4
  1538.         jnb .search_loop
  1539. bad_kernel:     
  1540.         mov si,KernelName
  1541.                 mov di,KernelCName
  1542.         push di
  1543.                 call unmangle_name              ; Get human form
  1544.         mov si,err_notfound        ; Complain about missing kernel
  1545.         call writestr
  1546.         pop si                ; KernelCName
  1547.                 call writestr
  1548.                 mov si,crlf_msg
  1549.                 jmp abort_load                  ; Ask user for clue
  1550. ;
  1551. ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
  1552. ;
  1553. bad_implicit:   mov si,KernelName        ; For the error message
  1554.                 mov di,KernelCName
  1555.                 call unmangle_name
  1556.                 jmp short bad_kernel
  1557. ;
  1558. ; vk_found: We *are* using a "virtual kernel"
  1559. ;
  1560. vk_found:    popa
  1561.         push di
  1562.         mov di,VKernelBuf
  1563.         mov cx,vk_size >> 2
  1564.         rep movsd
  1565.         push es                ; Restore old DS
  1566.         pop ds
  1567.         push es
  1568.         push word real_mode_seg
  1569.         pop es
  1570.         mov di,cmd_line_here
  1571.         mov si,VKernelBuf+vk_append
  1572.         mov cx,[VKernelBuf+vk_appendlen]
  1573.         rep movsb
  1574.         mov [CmdLinePtr],di        ; Where to add rest of cmd
  1575.         pop es
  1576.                 pop di                          ; DI -> KernelName
  1577.         push di    
  1578.         mov si,VKernelBuf+vk_rname
  1579.         mov cx,11            ; We need ECX == CX later
  1580.         rep movsb
  1581.         pop di
  1582.         xor bx,bx            ; Try only one version
  1583.         jmp get_kernel
  1584. ;
  1585. ; kernel_corrupt: Called if the kernel file does not seem healthy
  1586. ;
  1587. kernel_corrupt: mov si,err_notkernel
  1588.                 jmp abort_load
  1589. ;
  1590. ; This is it!  We have a name (and location on the disk)... let's load
  1591. ; that sucker!!  First we have to decide what kind of file this is; base
  1592. ; that decision on the file extension.  The following extensions are
  1593. ; recognized:
  1594. ;
  1595. ; .COM     - COMBOOT image
  1596. ; .CBT    - COMBOOT image
  1597. ; .BS    - Boot sector
  1598. ; .BSS    - Boot sector, but transfer over DOS superblock
  1599. ;
  1600. ; Anything else is assumed to be a Linux kernel.
  1601. ;
  1602. kernel_good:
  1603.         pusha
  1604.         mov si,KernelName
  1605.                 mov di,KernelCName
  1606.                 call unmangle_name              ; Get human form
  1607.                 sub di,KernelCName
  1608.                 mov [KernelCNameLen],di
  1609.         popa
  1610.  
  1611.         mov ecx,[KernelName+8]        ; Get (mangled) extension
  1612.         cmp ecx,'COM'
  1613.         je near is_comboot_image
  1614.         cmp ecx,'CBT'
  1615.         je near is_comboot_image
  1616.         cmp ecx,'BS '
  1617.         je near is_bootsector
  1618.         cmp ecx,'BSS'
  1619.         je near is_bss_sector
  1620.         ; Otherwise Linux kernel
  1621. ;
  1622. ; A Linux kernel consists of three parts: boot sector, setup code, and
  1623. ; kernel code.    The boot sector is never executed when using an external
  1624. ; booting utility, but it contains some status bytes that are necessary.
  1625. ; The boot sector and setup code together form exactly 5 sectors that
  1626. ; should be loaded at 9000:0.  The subsequent code should be loaded
  1627. ; at 1000:0.  For simplicity, we load the whole thing at 0F60:0, and
  1628. ; copy the latter stuff afterwards.
  1629. ;
  1630. ; NOTE: In the previous code I have avoided making any assumptions regarding
  1631. ; the size of a sector, in case this concept ever gets extended to other
  1632. ; media like CD-ROM (not that a CD-ROM would be bloody likely to use a FAT
  1633. ; filesystem, of course).  However, a "sector" when it comes to Linux booting
  1634. ; stuff means 512 bytes *no matter what*, so here I am using that piece
  1635. ; of knowledge.
  1636. ;
  1637. ; First check that our kernel is at least 64K and less than 8M (if it is
  1638. ; more than 8M, we need to change the logic for loading it anyway...)
  1639. ;
  1640. is_linux_kernel:
  1641.                 cmp dx,80h            ; 8 megs
  1642.         ja kernel_corrupt
  1643.         and dx,dx
  1644.         jz kernel_corrupt
  1645. kernel_sane:    push ax
  1646.         push dx
  1647.         push si
  1648.         mov si,loading_msg
  1649.                 call cwritestr
  1650. ;
  1651. ; Now start transferring the kernel
  1652. ;
  1653.         push word real_mode_seg
  1654.         pop es
  1655.  
  1656.         push ax
  1657.         push dx
  1658.         div word [ClustSize]        ; # of clusters total
  1659.         and dx,dx            ; Round up
  1660.         setnz dl
  1661.         movzx dx,dl
  1662.         add ax,dx
  1663.                 mov [KernelClust],ax
  1664.         pop dx
  1665.         pop ax
  1666.         add ax,1023
  1667.         adc dx,byte 0
  1668.         mov bx,1024
  1669.         div bx                ; Get number of kilobytes
  1670.         mov [KernelK],ax
  1671. ;
  1672. ; Now, if we transfer these straight, we'll hit 64K boundaries.     Hence we
  1673. ; have to see if we're loading more than 64K, and if so, load it step by
  1674. ; step.
  1675. ;
  1676.         mov dx,1            ; 10000h
  1677.         xor ax,ax
  1678.         div word [ClustSize]
  1679.         mov [ClustPerMoby],ax        ; Clusters/64K
  1680. ;
  1681. ; Start by loading the bootsector/setup code, to see if we need to
  1682. ; do something funky.  It should fit in the first 32K (loading 64K won't
  1683. ; work since we might have funny stuff up near the end of memory).
  1684. ; If we have larger than 32K clusters, yes, we're hosed.
  1685. ;
  1686.         call abort_check        ; Check for abort key
  1687.         mov cx,[ClustPerMoby]
  1688.         shr cx,1            ; Half a moby
  1689.         sub [KernelClust],cx
  1690.         xor bx,bx
  1691.                 pop si                          ; Cluster pointer on stack
  1692.         call getfssec
  1693.         jc near kernel_corrupt        ; Failure in first 32K
  1694.                 cmp word [es:bs_bootsign],0AA55h
  1695.         jne near kernel_corrupt        ; Boot sec signature missing
  1696. ;
  1697. ; Get the BIOS' idea of what the size of high memory is
  1698. ;
  1699.         push si                ; Save our cluster pointer!
  1700.         mov ah,88h
  1701.         int 15h
  1702.         cmp ax,14*1024            ; Don't trust memory >15M
  1703.         jna hms_ok
  1704.         mov ax,14*1024
  1705. hms_ok:        mov [HighMemSize],ax
  1706. ;
  1707. ; Construct the command line (append options have already been copied)
  1708. ;
  1709.         mov word [es:kern_cmd_magic],0A33Fh    ; Command line magic no
  1710.         mov word [es:kern_cmd_offset],cmd_line_here
  1711.         mov di,[CmdLinePtr]
  1712.                 mov si,boot_image            ; BOOT_IMAGE=
  1713.                 mov cx,boot_image_len
  1714.                 rep movsb
  1715.                 mov si,KernelCName           ; Unmangled kernel name
  1716.                 mov cx,[KernelCNameLen]
  1717.                 rep movsb
  1718.                 mov al,' '                      ; Space
  1719.                 stosb
  1720.                 mov si,[CmdOptPtr]              ; Options from user input
  1721.         mov cx,(kern_cmd_len+3) >> 2
  1722.         rep movsd
  1723. ;
  1724. %ifdef debug
  1725.                 push ds                         ; DEBUG DEBUG DEBUG
  1726.                 push es
  1727.                 pop ds
  1728.                 mov si,offset cmd_line_here
  1729.                 call cwritestr
  1730.                 pop ds
  1731.                 mov si,offset crlf_msg
  1732.                 call cwritestr
  1733. %endif
  1734. ;
  1735. ; Scan through the command line for anything that looks like we might be
  1736. ; interested in.  The original version of this code automatically assumed
  1737. ; the first option was BOOT_IMAGE=, but that is no longer certain.
  1738. ;
  1739.         mov si,cmd_line_here
  1740.                 mov byte [initrd_flag],0
  1741.                 push es                ; Set DS <- real_mode_seg
  1742.                 pop ds
  1743. get_next_opt:   lodsb
  1744.         and al,al
  1745.         jz near cmdline_end
  1746.         cmp al,' '
  1747.         jbe get_next_opt
  1748.         dec si
  1749.                 mov eax,[si]
  1750.                 cmp eax,'vga='
  1751.         je is_vga_cmd
  1752.                 cmp eax,'mem='
  1753.         je is_mem_cmd
  1754.                 push es                         ; Save ES -> real_mode_seg
  1755.                 push ss
  1756.                 pop es                          ; Set ES <- normal DS
  1757.                 mov di,initrd_cmd
  1758.         mov cx,initrd_cmd_len
  1759.         repe cmpsb
  1760.                 jne not_initrd
  1761.         mov di,InitRD
  1762.                 push si                         ; mangle_dir mangles si
  1763.                 call mangle_name                ; Mangle ramdisk name
  1764.                 pop si
  1765.         cmp byte [es:InitRD],' '    ; Null filename?
  1766.                 seta byte [es:initrd_flag]    ; Set flag if not
  1767. not_initrd:    pop es                          ; Restore ES -> real_mode_seg
  1768. skip_this_opt:  lodsb                           ; Load from command line
  1769.                 cmp al,' '
  1770.                 ja skip_this_opt
  1771.                 dec si
  1772.                 jmp short get_next_opt
  1773. is_vga_cmd:
  1774.                 add si,byte 4
  1775.                 mov eax,[si]
  1776.                 mov bx,-1
  1777.                 cmp eax, 'norm'                 ; vga=normal
  1778.                 je vc0
  1779.                 and eax,0ffffffh        ; 3 bytes
  1780.                 mov bx,-2
  1781.                 cmp eax, 'ext'                  ; vga=ext
  1782.                 je vc0
  1783.                 mov bx,-3
  1784.                 cmp eax, 'ask'                  ; vga=ask
  1785.                 je vc0
  1786.                 call parseint                   ; vga=<number>
  1787.         jc skip_this_opt        ; Not an integer
  1788. vc0:        mov [bs_vidmode],bx        ; Set video mode
  1789.         jmp short skip_this_opt
  1790. is_mem_cmd:
  1791.                 add si,byte 4
  1792.                 call parseint
  1793.         jc skip_this_opt        ; Not an integer
  1794.         shr ebx,10            ; Convert to kilobytes
  1795.                 sub ebx,1024                    ; Don't count first meg
  1796.         cmp ebx,14*1024            ; Only trust < 15M point
  1797.                 jna memcmd_fair
  1798.         mov bx,14*1024
  1799. memcmd_fair:    mov [ss:HighMemSize],bx
  1800.         jmp short skip_this_opt
  1801. cmdline_end:
  1802.                 push ss                         ; Restore standard DS
  1803.                 pop ds
  1804. ;
  1805. ; Now check if we have a large kernel, which needs to be loaded high
  1806. ;
  1807.         cmp dword [es:su_header],HEADER_ID    ; New setup code ID
  1808.         jne near old_kernel        ; Old kernel, load low
  1809.         cmp word [es:su_version],0200h    ; Setup code version 2.0
  1810.         jb near old_kernel        ; Old kernel, load low
  1811.                 cmp word [es:su_version],0201h    ; Version 2.01+?
  1812.                 jb new_kernel                   ; If 2.00, skip this step
  1813.                 mov word [es:su_heapend],linux_stack    ; Set up the heap
  1814.                 or byte [es:su_loadflags],80h    ; Let the kernel know we care
  1815. ;
  1816. ; We definitely have a new-style kernel.  Let the kernel know who we are,
  1817. ; and that we are clueful
  1818. ;
  1819. new_kernel:
  1820.         mov byte [es:su_loader],syslinux_id    ; Show some ID
  1821.         movzx ax,byte [es:bs_setupsecs]    ; Variable # of setup sectors
  1822.         mov [SetupSecs],ax
  1823. ;
  1824. ; Now see if we have an initial RAMdisk; if so, do requisite computation
  1825. ;
  1826.                 test byte [initrd_flag],1
  1827.                 jz nk_noinitrd
  1828.                 push es                         ; ES->real_mode_seg
  1829.                 push ds
  1830.                 pop es                          ; We need ES==DS
  1831.                 mov si,InitRD
  1832.                 mov di,InitRDCName
  1833.                 call unmangle_name              ; Create human-readable name
  1834.                 sub di,InitRDCName
  1835.                 mov [InitRDCNameLen],di
  1836.                 mov di,InitRD
  1837.                 call searchdir                  ; Look for it in directory
  1838.                 pop es
  1839.         jz initrd_notthere
  1840.         mov [initrd_ptr],si        ; Save cluster pointer
  1841.         mov [es:su_ramdisklen1],ax    ; Ram disk length
  1842.         mov [es:su_ramdisklen2],dx
  1843.         div word [ClustSize]
  1844.         and dx,dx            ; Round up
  1845.         setnz dl
  1846.         movzx dx,dl
  1847.         add ax,dx
  1848.         mov [InitRDClust],ax        ; Ramdisk clusters
  1849.         movzx edx,word [HighMemSize]    ; End of memory
  1850.         add edx,1024            ; Add "low" memory
  1851.         shl edx,10            ; Convert to bytes
  1852.         sub edx,[es:su_ramdisklen]    ; Subtract size of ramdisk
  1853.                 xor dx,dx            ; Round down to 64K boundary
  1854.                 mov [InitRDat],edx        ; Load address
  1855.         call loadinitrd            ; Load initial ramdisk
  1856.         jmp short initrd_end
  1857.  
  1858. initrd_notthere:
  1859.                 mov si,err_noinitrd
  1860.                 call writestr
  1861.                 mov si,InitRDCName
  1862.                 call writestr
  1863.                 mov si,crlf_msg
  1864.                 jmp abort_load
  1865.  
  1866. no_high_mem:    mov si,err_nohighmem        ; Error routine
  1867.                 jmp abort_load
  1868. ;
  1869. ; About to load the kernel.  This is a modern kernel, so use the boot flags
  1870. ; we were provided.
  1871. ;
  1872. nk_noinitrd:
  1873. initrd_end:
  1874.                 mov al,[es:su_loadflags]
  1875.         mov [LoadFlags],al
  1876. ;
  1877. ; Load the kernel.  We always load it at 100000h even if we're supposed to
  1878. ; load it "low"; for a "low" load we copy it down to low memory right before
  1879. ; jumping to it.
  1880. ;
  1881. read_kernel:
  1882.                 mov si,KernelCName        ; Print kernel name part of
  1883.                 call cwritestr                  ; "Loading" message
  1884.                 mov si,dotdot_msg        ; Print dots
  1885.                 call cwritestr
  1886.  
  1887.                 mov ax,[HighMemSize]
  1888.         cmp ax,[KernelK]
  1889.         jb no_high_mem            ; Not enough high memory
  1890. ;
  1891. ; Move the stuff beyond the setup code to high memory at 100000h
  1892. ;
  1893.         movzx esi,word [SetupSecs]    ; Setup sectors
  1894.         inc esi                ; plus 1 boot sector
  1895.                 shl esi,9            ; Convert to bytes
  1896.                 mov ecx,108000h            ; 108000h = 1M + 32K
  1897.                 sub ecx,esi            ; Adjust pointer to 2nd block
  1898.                 mov [HiLoadAddr],ecx
  1899.         sub ecx,100000h            ; Turn into a counter
  1900.         shr ecx,2            ; Convert to dwords
  1901.         add esi,90000h            ; Pointer to source
  1902.                 mov edi,100000h                 ; Copy to address 100000h
  1903.                 call bcopy            ; Transfer to high memory
  1904. ;
  1905.                 push word xfer_buf_seg        ; Segment 7000h is xfer buffer
  1906.                 pop es
  1907. high_load_loop: 
  1908.                 mov si,dot_msg            ; Progress report
  1909.                 call cwritestr
  1910.                 call abort_check
  1911.                 mov cx,[KernelClust]
  1912.         cmp cx,[ClustPerMoby]
  1913.         jna high_last_moby
  1914.         mov cx,[ClustPerMoby]
  1915. high_last_moby:
  1916.         sub [KernelClust],cx
  1917.         xor bx,bx            ; Load at offset 0
  1918.                 pop si                          ; Restore cluster pointer
  1919.                 call getfssec
  1920.                 push si                         ; Save cluster pointer
  1921.                 pushf                           ; Save EOF
  1922.                 xor bx,bx
  1923.         mov esi,(xfer_buf_seg << 4)
  1924.                 mov edi,[HiLoadAddr]        ; Destination address
  1925.                 mov ecx,4000h            ; Cheating - transfer 64K
  1926.                 call bcopy            ; Transfer to high memory
  1927.         mov [HiLoadAddr],edi        ; Point to next target area
  1928.                 popf                            ; Restore EOF
  1929.                 jc high_load_done               ; If EOF we are done
  1930.                 cmp word [KernelClust],byte 0    ; Are we done?
  1931.         jne high_load_loop        ; Apparently not
  1932. high_load_done:
  1933.         pop si                ; No longer needed
  1934.                 mov ax,real_mode_seg        ; Set to real mode seg
  1935.                 mov es,ax
  1936.  
  1937.                 mov si,dot_msg
  1938.                 call cwritestr
  1939. ;
  1940. ; Abandon hope, ye that enter here!  We do no longer permit aborts.
  1941. ;
  1942.                 call abort_check            ; Last chance!!
  1943.  
  1944. ;
  1945. ; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
  1946. ; setup sectors, but the boot protocol had not yet been defined.  They
  1947. ; rely on a signature to figure out if they need to copy stuff from
  1948. ; the "protected mode" kernel area.  Unfortunately, we used that area
  1949. ; as a transfer buffer, so it's going to find the signature there.
  1950. ; Hence, zero the low 32K beyond the setup area.
  1951. ;
  1952.         mov di,[SetupSecs]
  1953.         inc di                ; Setup + boot sector
  1954.         mov cx,32768/512        ; Sectors/32K
  1955.         sub cx,di            ; Remaining sectors
  1956.         shl di,9            ; Sectors -> bytes
  1957.         shl cx,7            ; Sectors -> dwords
  1958.         xor eax,eax
  1959.         rep stosd            ; Clear region
  1960. ;
  1961. ; Now, if we were supposed to load "low", copy the kernel down to 10000h
  1962. ;
  1963.         test byte [LoadFlags],LOAD_HIGH
  1964.         jnz in_proper_place        ; If high load, we're done
  1965.  
  1966.         movzx ecx,word [KernelK]
  1967.         shl ecx,8            ; K -> dword
  1968.         mov esi,100000h
  1969.         mov edi,10000h
  1970.         call bcopy
  1971. in_proper_place:
  1972. ;
  1973. ; If the default root device is set to FLOPPY (0000h), change to
  1974. ; /dev/fd0 (0200h)
  1975. ;
  1976.         cmp word [es:bs_rootdev],byte 0
  1977.         jne root_not_floppy
  1978.         mov word [es:bs_rootdev],0200h
  1979. root_not_floppy:
  1980. ;
  1981. ; Copy the disk table to high memory, then re-initialize the floppy
  1982. ; controller
  1983. ;
  1984.         push ds
  1985.         lds si,[fdctab]
  1986.         mov di,linux_fdctab
  1987.         mov cx,3            ; 12 bytes
  1988.         push di
  1989.         rep movsd
  1990.         pop di
  1991.         cli
  1992.         mov [fdctab1],di        ; Save new floppy tab pos
  1993.         mov [fdctab2],es
  1994.         sti
  1995.         xor ax,ax
  1996.         xor dx,dx
  1997.         int 13h
  1998.         pop ds
  1999. ;
  2000. ; Linux wants the floppy motor shut off before starting the kernel,
  2001. ; at least bootsect.S seems to imply so
  2002. ;
  2003. kill_motor:
  2004.         mov dx,03F2h
  2005.         xor al,al
  2006.         out dx,al
  2007. ;
  2008. ; Now we're as close to be done as we can be and still use our normal
  2009. ; routines, print a CRLF to end the row of dots
  2010. ;
  2011.         mov si,crlf_msg
  2012.         call writestr
  2013. ;
  2014. ; If we're debugging, wait for a keypress so we can read any debug messages
  2015. ;
  2016. %ifdef debug
  2017.                 xor ax,ax
  2018.                 int 16h
  2019. %endif
  2020. ;
  2021. ; Set up segment registers and the Linux real-mode stack
  2022. ;
  2023.         mov ax,real_mode_seg
  2024.         mov ds,ax
  2025.                 mov es,ax
  2026.         mov fs,ax
  2027.         mov gs,ax
  2028.         cli
  2029.         mov ss,ax
  2030.         mov sp,linux_stack
  2031.         sti
  2032. ;
  2033. ; We're done... now RUN THAT KERNEL!!!!
  2034. ;
  2035.         jmp setup_seg:setup_entry
  2036. ;
  2037. ; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
  2038. ; initrd, and are always loaded low.
  2039. ;
  2040. old_kernel:
  2041.                 test byte [initrd_flag],1    ; Old kernel can't have initrd
  2042.                 jz load_old_kernel
  2043.                 mov si,err_oldkernel
  2044.                 jmp abort_load
  2045. load_old_kernel:
  2046.         mov word [SetupSecs],4        ; Always 4 setup sectors
  2047.         mov byte [LoadFlags],0        ; Always low
  2048.         jmp read_kernel
  2049.  
  2050. ;
  2051. ; Load a COMBOOT image.  A COMBOOT image is basically a DOS .COM file,
  2052. ; except that it may, of course, not contain any DOS system calls.  We
  2053. ; do, however, allow the execution of INT 20h to return to SYSLINUX.
  2054. ;
  2055. is_comboot_image:
  2056.         and dx,dx
  2057.         jnz comboot_too_large
  2058.         cmp ax,0ff00h        ; Max size in bytes
  2059.         jae comboot_too_large
  2060.  
  2061.         ;
  2062.         ; Set up the DOS vectors in the IVT (INT 20h-3fh)
  2063.         ;
  2064.         mov dword [4*0x20],comboot_return    ; INT 20h vector
  2065.         mov eax,comboot_bogus
  2066.         mov di,4*0x21
  2067.         mov cx,31        ; All remaining DOS vectors
  2068.         rep stosd
  2069.     
  2070.         mov cx,comboot_seg
  2071.         mov es,cx
  2072.  
  2073.         mov bx,100h        ; Load at <seg>:0100h
  2074.  
  2075.         mov cx,[ClustPerMoby]    ; Absolute maximum # of clusters
  2076.         call getfssec
  2077.  
  2078.         xor di,di
  2079.         mov cx,64        ; 256 bytes (size of PSP)
  2080.         xor eax,eax        ; Clear PSP
  2081.         rep stosd
  2082.  
  2083.         mov word [es:0], 020CDh    ; INT 20h instruction
  2084.         ; First non-free paragraph
  2085.         mov word [es:02h], comboot_seg+1000h
  2086.  
  2087.         ; Copy the command line from high memory
  2088.         mov cx,125        ; Max cmdline len (minus space and CR)
  2089.         mov si,[CmdOptPtr]
  2090.         mov di,081h        ; Offset in PSP for command line
  2091.         mov al,' '        ; DOS command lines begin with a space
  2092.         stosb
  2093.  
  2094. comboot_cmd_cp:    lodsb
  2095.         and al,al
  2096.         jz comboot_end_cmd
  2097.         stosb
  2098.         loop comboot_cmd_cp
  2099. comboot_end_cmd: mov al,0Dh        ; CR after last character
  2100.         stosb
  2101.         mov al,126        ; Include space but not CR
  2102.         sub al,cl
  2103.         mov [es:80h], al    ; Store command line length
  2104.  
  2105.         mov ax,es
  2106.         mov ds,ax
  2107.         mov ss,ax
  2108.         xor sp,sp
  2109.         push word 0        ; Return to address 0 -> exit
  2110.  
  2111.         jmp comboot_seg:100h    ; Run it
  2112.  
  2113. ; Looks like a COMBOOT image but too large
  2114. comboot_too_large:
  2115.         mov si,err_comlarge
  2116.         call writestr
  2117. cb_enter:    jmp enter_command
  2118.  
  2119. ; Proper return vector
  2120. comboot_return:    cli            ; Don't trust anyone
  2121.         xor ax,ax
  2122.         mov ss,ax
  2123.         mov sp,[ss:SavedSP]
  2124.         mov ds,ax
  2125.         mov es,ax
  2126.         sti
  2127.         cld
  2128.         jmp short cb_enter
  2129.  
  2130. ; Attempted to execute DOS system call
  2131. comboot_bogus:    cli            ; Don't trust anyone
  2132.         xor ax,ax
  2133.         mov ss,ax
  2134.         mov sp,[ss:SavedSP]
  2135.         mov ds,ax
  2136.         mov es,ax
  2137.         sti
  2138.         cld
  2139.         mov si,KernelCName
  2140.         call writestr
  2141.         mov si,err_notdos
  2142.         call writestr
  2143.         jmp short cb_enter
  2144.  
  2145. ;
  2146. ; Load a boot sector
  2147. ;
  2148. is_bootsector:
  2149.         ; Transfer zero bytes
  2150.         push word 0
  2151.         jmp short load_bootsec
  2152. is_bss_sector:
  2153.         ; Transfer the superblock
  2154.         push word superblock_len
  2155. load_bootsec:
  2156.         and dx,dx
  2157.         jnz bad_bootsec
  2158.         mov bx,[bsBytesPerSec]
  2159.         cmp ax,bx
  2160.         jne bad_bootsec
  2161.  
  2162.         ; Make sure we don't test this uninitialized
  2163.         mov [bx+trackbuf-2],dx    ; Note DX == 0
  2164.  
  2165.         mov bx,trackbuf
  2166.         mov cx,1        ; 1 cluster >= 1 sector
  2167.         call getfssec
  2168.  
  2169.         mov bx,[bsBytesPerSec]
  2170.         mov ax,[bx+trackbuf-2]
  2171.         cmp ax,0AA55h        ; Boot sector signature
  2172.         jne bad_bootsec
  2173.  
  2174.         mov si,superblock
  2175.         mov di,trackbuf+(superblock-bootsec)
  2176.         pop cx            ; Transfer count
  2177.         rep movsb
  2178. ;
  2179. ; Okay, here we go... copy over our own boot sector and run the new one
  2180. ;
  2181.         cli            ; Point of no return
  2182.     
  2183.         mov dl,[bsDriveNumber]    ; May not be in new bootsector!
  2184.  
  2185.         mov si,trackbuf
  2186.         mov di,bootsec
  2187.         mov cx,[bsBytesPerSec]
  2188.         rep movsb        ; Copy the boot sector!
  2189.         
  2190.         mov si,PartInfo
  2191.         mov di,800h-18        ; Put partition info here
  2192.         push di
  2193.         mov cx,8        ; 16 bytes
  2194.         rep movsw
  2195.         pop si            ; DS:SI points to partition info
  2196.  
  2197.         jmp bootsec
  2198.  
  2199. bad_bootsec:
  2200.         mov si,err_bootsec
  2201.         call writestr
  2202.         jmp enter_command
  2203.  
  2204. ;
  2205. ; cwritestr: write a null-terminated string to the console, saving
  2206. ;            registers on entry (we can't use this in the boot sector,
  2207. ;            since we haven't verified 386-ness yet)
  2208. ;
  2209. cwritestr:
  2210.                 pusha
  2211. cwstr_1:        lodsb
  2212.         and al,al
  2213.                 jz cwstr_2
  2214.         mov ah,0Eh        ; Write to screen as TTY
  2215.         mov bx,0007h        ; White on black, current page
  2216.         int 10h
  2217.                 jmp short cwstr_1
  2218. cwstr_2:        popa
  2219.                 ret
  2220.  
  2221. ;
  2222. ; 32-bit bcopy routine for real mode
  2223. ;
  2224. ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
  2225. ; and then exit.  IMPORTANT: This code assumes cs == ss == 0.
  2226. ;
  2227. ; This code is probably excessively anal-retentive in its handling of
  2228. ; segments, but this stuff is painful enough as it is without having to rely
  2229. ; on everything happening "as it ought to."
  2230. ;
  2231.         align 2
  2232. bcopy_gdt_ptr:    dw bcopy_gdt_size-1
  2233.         dd bcopy_gdt
  2234.  
  2235.         align 4
  2236. bcopy_gdt:    dd 0            ; Null descriptor
  2237.         dd 0
  2238.         dd 0000ffffh        ; Code segment, use16, readable,
  2239.         dd 00009b00h        ; present, dpl 0, cover 64K
  2240.         dd 0000ffffh        ; Data segment, use16, read/write,
  2241.         dd 008f9300h        ; present, dpl 0, cover all 4G
  2242.         dd 0000ffffh        ; Data segment, use16, read/write,
  2243.         dd 00009300h        ; present, dpl 0, cover 64K
  2244. bcopy_gdt_size:    equ $-bcopy_gdt
  2245.  
  2246. bcopy:
  2247.         push eax
  2248.         pushf            ; Saves, among others, the IF flag
  2249.         push gs
  2250.         push fs
  2251.         push ds
  2252.         push es
  2253.  
  2254.         cli
  2255.         call enable_a20
  2256.  
  2257.         o32 lgdt [bcopy_gdt_ptr]
  2258.         mov eax,cr0
  2259.         or al,1
  2260.         mov cr0,eax        ; Enter protected mode
  2261.         jmp 8:.in_pm
  2262.  
  2263. .in_pm:        mov ax,16        ; Data segment selector
  2264.         mov es,ax
  2265.         mov ds,ax
  2266.  
  2267.         mov al,24        ; "Real-mode-like" data segment
  2268.         mov ss,ax
  2269.         mov fs,ax
  2270.         mov gs,ax    
  2271.     
  2272.         a32 rep movsd        ; Do our business
  2273.         
  2274.         mov es,ax        ; Set to "real-mode-like"
  2275.         mov ds,ax
  2276.     
  2277.         mov eax,cr0
  2278.         and al,0feh
  2279.         mov cr0,eax        ; Disable protected mode
  2280.         jmp 0:.in_rm
  2281.  
  2282. .in_rm:        xor ax,ax        ; Back in real mode
  2283.         mov ss,ax
  2284.         pop es
  2285.         pop ds
  2286.         pop fs
  2287.         pop gs
  2288.         call disable_a20
  2289.  
  2290.         popf            ; Re-enables interrupts
  2291.         pop eax
  2292.         ret
  2293.  
  2294. ;
  2295. ; Routines to enable and disable (yuck) A20.  These routines are gathered
  2296. ; from tips from a couple of sources, including the Linux kernel and
  2297. ; http://www.x86.org/.  The need for the delay to be as large as given here
  2298. ; is indicated by Donnie Barnes of RedHat, the problematic system being an
  2299. ; IBM ThinkPad 760EL.
  2300. ;
  2301. ; We typically toggle A20 twice for every 64K transferred.
  2302. %define    io_delay  times 4 out 0EDh,al    ; Invalid port (we hope)
  2303. %define delaytime 256            ; 4 x ISA bus cycles (@ 1.5 â•¡s)
  2304.  
  2305. enable_a20:
  2306.         call empty_8042
  2307.         mov al,0D1h        ; Command write
  2308.         out 064h, al
  2309.         call empty_8042
  2310.         mov al,0DFh        ; A20 on
  2311.         out 060h, al
  2312.         call kbc_delay
  2313.         ; Verify that A20 actually is enabled.  Do that by
  2314.         ; observing a word in low memory and the same word in
  2315.         ; the HMA until they are no longer coherent.  Note that
  2316.         ; we don't do the same check in the disable case, because
  2317.         ; we don't want to *require* A20 masking (SYSLINUX should
  2318.         ; work fine without it, if the BIOS does.)
  2319.         push es
  2320.         mov ax,0FFFFh        ; HMA
  2321.         mov es,ax
  2322. .a20_wait:    inc word [ss:A20test]
  2323.         mov ax,[es:A20test+10h]
  2324.         cmp ax,[ss:A20test]
  2325.         je .a20_wait
  2326.         pop es
  2327.         ret
  2328.  
  2329. disable_a20:
  2330.         call empty_8042
  2331.         mov al,0D1h
  2332.         out 064h, al        ; Command write
  2333.         call empty_8042
  2334.         mov al,0DDh        ; A20 off
  2335.         out 060h, al
  2336.         jmp short kbc_delay
  2337.  
  2338. kbc_delay:    call empty_8042
  2339.         push cx
  2340.         mov cx, delaytime
  2341. .delayloop:    io_delay
  2342.         loop .delayloop
  2343.         pop cx
  2344.         ret
  2345.  
  2346. empty_8042:
  2347.         io_delay
  2348.         in al, 064h        ; Status port
  2349.         test al,1
  2350.         jz .no_output
  2351.         io_delay
  2352.         in al, 060h        ; Read input
  2353.         jmp short empty_8042
  2354. .no_output:
  2355.         test al,2
  2356.         jnz empty_8042
  2357.         io_delay
  2358.         ret    
  2359.  
  2360. ;
  2361. ; Load RAM disk into high memory
  2362. ;
  2363. loadinitrd:
  2364.                 push es                         ; Save ES on entry
  2365.                 mov ax,real_mode_seg
  2366.                 mov es,ax
  2367.                 mov si,[initrd_ptr]
  2368.                 mov edi,[InitRDat]        ; initrd load address
  2369.         mov [es:su_ramdiskat],edi    ; Offset for ram disk
  2370.         push si
  2371.                 mov si,InitRDCName        ; Write ramdisk name
  2372.                 call cwritestr
  2373.                 mov si,dotdot_msg        ; Write dots
  2374.                 call cwritestr
  2375. rd_load_loop:    
  2376.         mov si,dot_msg            ; Progress report
  2377.                 call cwritestr
  2378.         pop si                ; Restore cluster pointer
  2379.                 call abort_check
  2380.                 mov cx,[InitRDClust]
  2381.         cmp cx,[ClustPerMoby]
  2382.         jna rd_last_moby
  2383.         mov cx,[ClustPerMoby]
  2384. rd_last_moby:
  2385.         sub [InitRDClust],cx
  2386.         xor bx,bx            ; Load at offset 0
  2387.                 push word xfer_buf_seg        ; Bounce buffer segment
  2388.         pop es
  2389.         push cx
  2390.         call getfssec
  2391.         pop cx
  2392.                 push si                ; Save cluster pointer
  2393.         mov esi,(xfer_buf_seg << 4)
  2394.         mov edi,[InitRDat]
  2395.         mov ecx,4000h            ; Copy 64K
  2396.         call bcopy            ; Does not change flags!!
  2397.                 jc rd_load_done                 ; EOF?
  2398.                 add dword [InitRDat],10000h    ; Point to next 64K
  2399.         cmp word [InitRDClust],byte 0    ; Are we done?
  2400.         jne rd_load_loop        ; Apparently not
  2401. rd_load_done:
  2402.                 pop si                          ; Clean up the stack
  2403.                 mov si,crlf_msg
  2404.         call writestr
  2405.                 mov si,loading_msg        ; Write new "Loading " for
  2406.                 call writestr                   ; the benefit of the kernel
  2407.                 pop es                          ; Restore original ES
  2408.                 ret
  2409.  
  2410. ;
  2411. ; abort_check: let the user abort with <ESC> or <Ctrl-C>
  2412. ;
  2413. abort_check:
  2414.                 pusha
  2415. ac1:
  2416.         mov ah,1            ; Check for pending keystroke
  2417.         int 16h
  2418.                 jz ac_ret                       ; If no pending keystroke
  2419.         xor ax,ax            ; Load pending keystroke
  2420.         int 16h
  2421.         mov bx,KbdMap
  2422.         xlatb
  2423.         cmp al,27            ; <ESC> aborts (DOS geeks)
  2424.         je ac2
  2425.         cmp al,3            ; So does Ctrl-C (UNIX geeks)
  2426.         jne ac1                ; Unknown key... try again
  2427. ac2:                        ; If we get here, ABORT!
  2428.                 mov si,aborted_msg
  2429.                 ; Fall through to abort_load
  2430. ;
  2431. ; abort_load: Called by various routines which wants to print a fatal
  2432. ;             error message and return to the command prompt.  Since this
  2433. ;             may happen at just about any stage of the boot process, assume
  2434. ;             our state is messed up, and just reset the segment registers
  2435. ;             and the stack forcibly.
  2436. ;
  2437. ;             SI    = offset (in _text) of error message to print
  2438. ;
  2439. abort_load:
  2440.                 mov ax,cs                       ; Restore CS = DS = ES
  2441.                 mov ds,ax
  2442.                 mov es,ax
  2443.                 cli
  2444.                 mov sp,StackBuf-2*3            ; Reset stack
  2445.                 mov ss,ax                       ; Just in case...
  2446.                 sti
  2447.                 call writestr                   ; Expects SI -> error msg
  2448. al_ok:          jmp enter_command               ; Return to command prompt
  2449. ;
  2450. ; End of abort_check
  2451. ;
  2452. ac_ret:         popa
  2453.                 ret
  2454.  
  2455. ;
  2456. ; searchdir: Search the root directory for a pre-mangled filename in
  2457. ;         DS:DI.  This routine is similar to the one in the boot
  2458. ;         sector, but is a little less Draconian when it comes to
  2459. ;         error handling, plus it reads the root directory in
  2460. ;         larger chunks than a sector at a time (which is probably
  2461. ;         a waste of coding effort, but I like to do things right).
  2462. ;
  2463. ;         FIXME: usually we can load the entire root dir in memory,
  2464. ;         and files are usually at the beginning anyway.  It probably
  2465. ;         would be worthwhile to remember if we have the first chunk
  2466. ;         in memory and skip the load if that (it would speed up online
  2467. ;         help, mainly.)
  2468. ;
  2469. ;         NOTE: This file considers finding a zero-length file an
  2470. ;         error.  This is so we don't have to deal with that special
  2471. ;         case elsewhere in the program (most loops have the test
  2472. ;         at the end).
  2473. ;
  2474. ;         If successful:
  2475. ;        ZF clear
  2476. ;        SI    = cluster # for the first cluster
  2477. ;        DX:AX    = file length in bytes
  2478. ;         If unsuccessful
  2479. ;        ZF set
  2480. ;
  2481.  
  2482. searchdir:
  2483.         mov ax,[bsRootDirEnts]
  2484.         mov [DirScanCtr],ax
  2485.         mov ax,[RootDirSize]
  2486.         mov [DirBlocksLeft],ax
  2487.         mov ax,[RootDir1]
  2488.         mov dx,[RootDir2]
  2489. scan_group:
  2490.         mov bp,[DirBlocksLeft]
  2491.         and bp,bp
  2492.         jz dir_return
  2493.         cmp bp,[BufSafeSec]
  2494.         jna load_last
  2495.         mov bp,[BufSafeSec]
  2496. load_last:
  2497.         sub [DirBlocksLeft],bp
  2498.         push ax
  2499.         push dx
  2500.         mov ax,[bsBytesPerSec]
  2501.         mul bp
  2502.         add ax,trackbuf-31
  2503.         mov [EndofDirSec],ax    ; End of loaded
  2504.         pop dx
  2505.         pop ax
  2506.         push bp            ; Save number of sectors
  2507.         push ax            ; Save present location
  2508.         push dx
  2509.         push di            ; Save name
  2510.         mov bx,trackbuf
  2511.         call getlinsec
  2512.         pop di
  2513.         pop dx
  2514.         pop ax
  2515.         pop bp
  2516.         mov si,trackbuf
  2517. dir_test_name:    cmp byte [si],0        ; Directory high water mark
  2518.         je dir_return        ; Failed
  2519.                 test byte [si+11],018h    ; Check it really is a file
  2520.                 jnz dir_not_this
  2521.         push di
  2522.         push si
  2523.         mov cx,11        ; Filename = 11 bytes
  2524.         repe cmpsb
  2525.         pop si
  2526.         pop di
  2527.         je dir_success
  2528. dir_not_this:   add si,byte 32
  2529.         dec word [DirScanCtr]
  2530.         jz dir_return        ; Out of it...
  2531.         cmp si,[EndofDirSec]
  2532.         jb dir_test_name
  2533.         add ax,bp        ; Increment linear sector number
  2534.         adc dx,byte 0
  2535.         jmp short scan_group
  2536. dir_success:
  2537.         mov ax,[si+28]        ; Length of file
  2538.         mov dx,[si+30]
  2539.         mov si,[si+26]        ; Cluster pointer
  2540.         mov bx,ax
  2541.         or bx,dx        ; Sets ZF iff DX:AX is zero
  2542. dir_return:
  2543.         ret
  2544.  
  2545. ;
  2546. ; writechr:    Write a single character in AL to the screen without
  2547. ;        mangling any registers
  2548. ;
  2549. writechr:
  2550.         pusha
  2551.         mov ah,0Eh
  2552.         mov bx,0007h        ; white text on this page
  2553.         int 10h
  2554.         popa
  2555.         ret
  2556.  
  2557. ;
  2558. ; adjust_screen: Set the internal variables associated with the screen size.
  2559. ;        This is a subroutine in case we're loading a custom font.
  2560. ;
  2561. adjust_screen:
  2562.                 mov al,[BIOS_vidrows]
  2563.                 and al,al
  2564.                 jnz vidrows_is_ok
  2565.                 mov al,24                       ; No vidrows in BIOS, assume 25
  2566.                         ; (Remember: vidrows == rows-1)
  2567. vidrows_is_ok:  mov [VidRows],al
  2568.                 mov ah,0fh
  2569.                 int 10h                         ; Read video state
  2570.                 mov [TextPage],bh
  2571.                 dec ah                          ; Store count-1 (same as rows)
  2572.                 mov [VidCols],ah
  2573. bf_ret:        ret
  2574.  
  2575. ;
  2576. ; loadfont:    Load a .psf font file and install it onto the VGA console
  2577. ;        (if we're not on a VGA screen then ignore.)  It is called with
  2578. ;        SI and DX:AX set by routine searchdir
  2579. ;
  2580. loadfont:
  2581.         mov bx,trackbuf            ; The trackbuf is >= 16K; the part
  2582.         mov cx,[BufSafe]        ; of a PSF file we care about is no
  2583.         call getfssec            ; more than 8K+4 bytes
  2584.  
  2585.         mov ax,[trackbuf]        ; Magic number
  2586.         cmp ax,0436h
  2587.         jne bf_ret
  2588.  
  2589.         mov al,[trackbuf+2]        ; File mode
  2590.         cmp al,3            ; Font modes 0-3 supported
  2591.         ja bf_ret
  2592.  
  2593.         mov bh,byte [trackbuf+3]    ; Height of font
  2594.         cmp bh,2            ; VGA minimum
  2595.         jb bf_ret
  2596.         cmp bh,32            ; VGA maximum
  2597.         ja bf_ret
  2598.  
  2599.         mov bp,trackbuf+4        ; Address of font data
  2600.         xor bl,bl
  2601.         mov cx,256
  2602.         xor dx,dx
  2603.         mov ax,1110h
  2604.         int 10h                ; Load into VGA RAM
  2605.  
  2606.         xor bl,bl
  2607.         mov ax,1103h            ; Select page 0
  2608.         int 10h
  2609.  
  2610.         jmp short adjust_screen
  2611.  
  2612. ;
  2613. ; loadkeys:    Load a LILO-style keymap; SI and DX:AX set by searchdir
  2614. ;
  2615. loadkeys:
  2616.         and dx,dx            ; Should be 256 bytes exactly
  2617.         jne loadkeys_ret
  2618.         cmp ax,256
  2619.         jne loadkeys_ret
  2620.  
  2621.         mov bx,trackbuf
  2622.         mov cx,1            ; 1 cluster should be >= 256 bytes
  2623.         call getfssec
  2624.  
  2625.         mov si,trackbuf
  2626.         mov di,KbdMap
  2627.         mov cx,256 >> 2
  2628.         rep movsd
  2629.  
  2630. loadkeys_ret:    ret
  2631.         
  2632. ;
  2633. ; get_msg_file: Load a text file and write its contents to the screen,
  2634. ;               interpreting color codes.  Is called with SI and DX:AX
  2635. ;               set by routine searchdir
  2636. ;
  2637. get_msg_file:
  2638.                 mov word [NextCharJump],msg_putchar ; State machine for color
  2639.                 mov byte [TextAttribute],07h    ; Default grey on white
  2640.                 pusha
  2641.                 mov bh,[TextPage]
  2642.                 mov ah,03h                      ; Read cursor position
  2643.                 int 10h
  2644.                 mov [CursorDX],dx
  2645.                 popa
  2646. get_msg_chunk:  push ax                         ; DX:AX = length of file
  2647.                 push dx
  2648.         mov bx,trackbuf
  2649.         mov cx,[BufSafe]
  2650.         call getfssec
  2651.                 pop dx
  2652.                 pop ax
  2653.         push si                ; Save current cluster
  2654.         mov si,trackbuf
  2655.         mov cx,[BufSafeBytes]        ; No more than many bytes
  2656. print_msg_file: push cx
  2657.                 push ax
  2658.         push dx
  2659.         lodsb
  2660.                 cmp al,1Ah                      ; ASCII EOF?
  2661.         je msg_done_pop
  2662.                 call [NextCharJump]        ; Do what shall be done
  2663.         pop dx
  2664.         pop ax
  2665.                 pop cx
  2666.         sub ax,byte 1
  2667.         sbb dx,byte 0
  2668.         mov bx,ax
  2669.         or bx,dx
  2670.         jz msg_done
  2671.         loop print_msg_file
  2672.         pop si
  2673.         jmp short get_msg_chunk
  2674. msg_done_pop:
  2675.                 add sp,byte 6            ; Lose 3 words on the stack
  2676. msg_done:
  2677.         pop si
  2678.         ret
  2679. msg_putchar:                                    ; Normal character
  2680.                 cmp al,0Fh                      ; ^O = color code follows
  2681.                 je msg_ctrl_o
  2682.                 cmp al,0Dh                      ; Ignore <CR>
  2683.                 je msg_ignore
  2684.                 cmp al,0Ah                      ; <LF> = newline
  2685.                 je msg_newline
  2686.                 cmp al,0Ch                      ; <FF> = clear screen
  2687.                 je msg_formfeed
  2688.                 mov bx,[TextAttrBX]
  2689.                 mov ah,09h                      ; Write character/attribute
  2690.                 mov cx,1                        ; One character only
  2691.                 int 10h                         ; Write to screen
  2692.                 mov al,[CursorCol]
  2693.                 inc ax
  2694.                 cmp al,[VidCols]
  2695.                 ja msg_newline
  2696.                 mov [CursorCol],al
  2697. msg_gotoxy:     mov bh,[TextPage]
  2698.                 mov dx,[CursorDX]
  2699.                 mov ah,02h                      ; Set cursor position
  2700.                 int 10h
  2701. msg_ignore:     ret
  2702. msg_ctrl_o:                                     ; ^O = color code follows
  2703.                 mov word [NextCharJump],msg_setbg
  2704.                 ret
  2705. msg_newline:                                    ; Newline char or end of line
  2706.                 mov byte [CursorCol],0
  2707.                 mov al,[CursorRow]
  2708.                 inc ax
  2709.                 cmp al,[VidRows]
  2710.                 ja msg_scroll
  2711.                 mov [CursorRow],al
  2712.                 jmp short msg_gotoxy
  2713. msg_scroll:     xor cx,cx                       ; Upper left hand corner
  2714.                 mov dx,[ScreenSize]
  2715.                 mov [CursorRow],dh        ; New cursor at the bottom
  2716.                 mov bh,[TextAttribute]
  2717.                 mov ax,0601h                    ; Scroll up one line
  2718.                 int 10h
  2719.                 jmp short msg_gotoxy
  2720. msg_formfeed:                                   ; Form feed character
  2721.                 xor cx,cx
  2722.                 mov [CursorDX],cx        ; Upper lefthand corner
  2723.                 mov dx,[ScreenSize]
  2724.                 mov bh,[TextAttribute]
  2725.                 mov ax,0600h                    ; Clear screen region
  2726.                 int 10h
  2727.                 jmp short msg_gotoxy
  2728. msg_setbg:                                      ; Color background character
  2729.                 call unhexchar
  2730.                 jc msg_color_bad
  2731.                 shl al,4
  2732.                 mov [TextAttribute],al
  2733.                 mov word [NextCharJump],msg_setfg
  2734.                 ret
  2735. msg_setfg:                                      ; Color foreground character
  2736.                 call unhexchar
  2737.                 jc msg_color_bad
  2738.                 or [TextAttribute],al        ; setbg set foreground to 0
  2739.                 mov word [NextCharJump],msg_putchar
  2740.                 ret
  2741. msg_color_bad:
  2742.                 mov byte [TextAttribute],07h    ; Default attribute
  2743.                 mov word [NextCharJump],msg_putchar
  2744.                 ret
  2745.  
  2746. ;
  2747. ; open,getc:    Load a file a character at a time for parsing in a manner
  2748. ;        similar to the C library getc routine.    Only one simultaneous
  2749. ;        use is supported.  Note: "open" trashes the trackbuf.
  2750. ;
  2751. ;        open:    Input:    mangled filename in DS:DI
  2752. ;            Output: ZF set on file not found or zero length
  2753. ;
  2754. ;        getc:    Output: CF set on end of file
  2755. ;                Character loaded in AL
  2756. ;
  2757. open:
  2758.         call searchdir
  2759.         jz open_return
  2760.         pushf
  2761.         mov [FBytes1],ax
  2762.         mov [FBytes2],dx
  2763.         add ax,[ClustSize]
  2764.         adc dx,byte 0
  2765.         sub ax,byte 1
  2766.         sbb dx,byte 0
  2767.         div word [ClustSize]
  2768.         mov [FClust],ax        ; Number of clusters
  2769.         mov [FNextClust],si    ; Cluster pointer
  2770.         mov ax,[EndOfGetCBuf]    ; Pointer at end of buffer ->
  2771.         mov [FPtr],ax        ;  nothing loaded yet
  2772.         popf            ; Restore no ZF
  2773. open_return:    ret
  2774.  
  2775. ;
  2776. getc:
  2777.         stc            ; If we exit here -> EOF
  2778.         mov ecx,[FBytes]
  2779.         jecxz getc_ret
  2780.         mov si,[FPtr]
  2781.         cmp si,[EndOfGetCBuf]
  2782.         jb getc_loaded
  2783.         ; Buffer empty -- load another set
  2784.         mov cx,[FClust]
  2785.         cmp cx,[BufSafe]
  2786.         jna getc_oksize
  2787.         mov cx,[BufSafe]
  2788. getc_oksize:    sub [FClust],cx        ; Reduce remaining clusters
  2789.         mov si,[FNextClust]
  2790.         mov bx,getcbuf
  2791.         push bx
  2792.         push es            ; ES may be != DS, save old ES
  2793.         push ds            ; Trackbuf is in DS, not ES
  2794.         pop es
  2795.         call getfssec        ; Load a trackbuf full of data
  2796.         mov [FNextClust],si    ; Store new next pointer
  2797.         pop es            ; Restore ES
  2798.         pop si            ; SI -> newly loaded data
  2799. getc_loaded:    lodsb            ; Load a byte
  2800.         mov [FPtr],si        ; Update next byte pointer
  2801.         dec dword [FBytes]    ; Update bytes left counter (CF = 1)
  2802.         clc            ; Not EOF
  2803. getc_ret:    ret
  2804.  
  2805. ;
  2806. ; ungetc:    Push a character (in AL) back into the getc buffer
  2807. ;        Note: if more than one byte is pushed back, this may cause
  2808. ;        bytes to be written below the getc buffer boundary.  If there
  2809. ;        is a risk for this to occur, the getcbuf base address should
  2810. ;        be moved up.
  2811. ;
  2812. ungetc:
  2813.         mov si,[FPtr]
  2814.         dec si
  2815.         mov [si],al
  2816.         mov [FPtr],si
  2817.         inc dword [FBytes]
  2818.         ret
  2819.  
  2820. ;
  2821. ; skipspace:    Skip leading whitespace using "getc".  If we hit end-of-line
  2822. ;        or end-of-file, return with carry set; ZF = true of EOF
  2823. ;        ZF = false for EOLN; otherwise CF = ZF = 0.
  2824. ;
  2825. ;        Otherwise AL = first character after whitespace
  2826. ;
  2827. skipspace:
  2828. skipspace_loop: call getc
  2829.         jc skipspace_eof
  2830.         cmp al,1Ah            ; DOS EOF
  2831.         je skipspace_eof
  2832.         cmp al,0Ah
  2833.         je skipspace_eoln
  2834.         cmp al,' '
  2835.         jbe skipspace_loop
  2836.         ret                ; CF = ZF = 0
  2837. skipspace_eof:    cmp al,al            ; Set ZF
  2838.         stc                ; Set CF
  2839.         ret
  2840. skipspace_eoln: add al,0FFh            ; Set CF, clear ZF
  2841.         ret
  2842.  
  2843. ;
  2844. ; getkeyword:    Get a keyword from the current "getc" file; only the two
  2845. ;        first characters are considered significant.
  2846. ;
  2847. ;        Lines beginning with ASCII characters 33-47 are treated
  2848. ;        as comments and ignored; other lines are checked for
  2849. ;        validity by scanning through the keywd_table.
  2850. ;
  2851. ;        The keyword and subsequent whitespace is skipped.
  2852. ;
  2853. ;        On EOF, CF = 1; otherwise, CF = 0, AL:AH = lowercase char pair
  2854. ;
  2855. getkeyword:
  2856. gkw_find:    call skipspace
  2857.         jz gkw_eof        ; end of file
  2858.         jc gkw_find        ; end of line: try again
  2859.         cmp al,'0'
  2860.         jb gkw_skipline        ; skip comment line
  2861.         push ax
  2862.         call getc
  2863.         pop bx
  2864.         jc gkw_eof
  2865.         mov bh,al        ; Move character pair into BL:BH
  2866.         or bx,2020h        ; Lower-case it
  2867.         mov si,keywd_table
  2868. gkw_check:    lodsw
  2869.         and ax,ax
  2870.         jz gkw_badline        ; Bad keyword, write message
  2871.         cmp ax,bx
  2872.         jne gkw_check
  2873.         push ax
  2874. gkw_skiprest:
  2875.         call getc
  2876.         jc gkw_eof_pop
  2877.         cmp al,'0'
  2878.         ja gkw_skiprest
  2879.         call ungetc
  2880.         call skipspace
  2881.         jz gkw_eof_pop
  2882.                 jc gkw_missingpar       ; Missing parameter after keyword
  2883.         call ungetc        ; Return character to buffer
  2884.         clc            ; Successful return
  2885. gkw_eof_pop:    pop ax
  2886. gkw_eof:    ret            ; CF = 1 on all EOF conditions
  2887. gkw_missingpar: pop ax
  2888.                 mov si,err_noparm
  2889.                 call writestr
  2890.                 jmp gkw_find
  2891. gkw_badline_pop: pop ax
  2892. gkw_badline:    mov si,err_badcfg
  2893.         call writestr
  2894.         jmp short gkw_find
  2895. gkw_skipline:    cmp al,10        ; Scan for LF
  2896.         je gkw_find
  2897.         call getc
  2898.         jc gkw_eof
  2899.         jmp short gkw_skipline
  2900.  
  2901. ;
  2902. ; getint:    Load an integer from the getc file.
  2903. ;        Return CF if error; otherwise return integer in EBX
  2904. ;
  2905. getint:
  2906.         mov di,NumBuf
  2907. gi_getnum:    cmp di,NumBufEnd    ; Last byte in NumBuf
  2908.         jae gi_loaded
  2909.         push di
  2910.         call getc
  2911.         pop di
  2912.         jc gi_loaded
  2913.         stosb
  2914.         cmp al,'-'
  2915.         jnb gi_getnum
  2916.         call ungetc        ; Unget non-numeric
  2917. gi_loaded:    mov byte [di],0
  2918.         mov si,NumBuf
  2919.         ; Fall through to parseint
  2920.  
  2921. ;
  2922. ; parseint:    Convert an integer to a number in EBX
  2923. ;        Get characters from string in DS:SI
  2924. ;        Return CF on error
  2925. ;        DS:SI points to first character after number
  2926. ;
  2927. ;               Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M
  2928. ;
  2929. parseint:
  2930.                 push eax
  2931.                 push ecx
  2932.         push bp
  2933.         xor eax,eax        ; Current digit (keep eax == al)
  2934.         mov ebx,eax        ; Accumulator
  2935.         mov ecx,ebx        ; Base
  2936.                 xor bp,bp               ; Used for negative flag
  2937. pi_begin:    lodsb
  2938.         cmp al,'-'
  2939.         jne pi_not_minus
  2940.         xor bp,1        ; Set unary minus flag
  2941.         jmp short pi_begin
  2942. pi_not_minus:
  2943.         cmp al,'0'
  2944.         jb pi_err
  2945.         je pi_octhex
  2946.         cmp al,'9'
  2947.         ja pi_err
  2948.         mov cl,10        ; Base = decimal
  2949.         jmp short pi_foundbase
  2950. pi_octhex:
  2951.         lodsb
  2952.         cmp al,'0'
  2953.         jb pi_km        ; Value is zero
  2954.         or al,20h        ; Downcase
  2955.         cmp al,'x'
  2956.         je pi_ishex
  2957.         cmp al,'7'
  2958.         ja pi_err
  2959.         mov cl,8        ; Base = octal
  2960.         jmp short pi_foundbase
  2961. pi_ishex:
  2962.         mov al,'0'        ; No numeric value accrued yet
  2963.         mov cl,16        ; Base = hex
  2964. pi_foundbase:
  2965.                 call unhexchar
  2966.                 jc pi_km                ; Not a (hex) digit
  2967.                 cmp al,cl
  2968.         jae pi_km        ; Invalid for base
  2969.         imul ebx,ecx        ; Multiply accumulated by base
  2970.                 add ebx,eax             ; Add current digit
  2971.         lodsb
  2972.         jmp short pi_foundbase
  2973. pi_km:
  2974.         dec si            ; Back up to last non-numeric
  2975.         lodsb
  2976.         or al,20h
  2977.         cmp al,'k'
  2978.         je pi_isk
  2979.         cmp al,'m'
  2980.         je pi_ism
  2981.         dec si            ; Back up
  2982. pi_fini:    and bp,bp
  2983.         jz pi_ret        ; CF=0!
  2984.         neg ebx            ; Value was negative
  2985. pi_done:    clc
  2986. pi_ret:        pop bp
  2987.                 pop ecx
  2988.                 pop eax
  2989.         ret
  2990. pi_err:        stc
  2991.         jmp short pi_ret
  2992. pi_isk:        shl ebx,10        ; x 2^10
  2993.         jmp short pi_done
  2994. pi_ism:        shl ebx,20        ; x 2^20
  2995.         jmp short pi_done
  2996.  
  2997. ;
  2998. ; unhexchar:    Convert a hexadecimal digit in AL to the equivalent number;
  2999. ;               return CF=1 if not a hex digit
  3000. ;
  3001. unhexchar:
  3002.                 cmp al,'0'
  3003.         jb uxc_ret        ; If failure, CF == 1 already
  3004.                 cmp al,'9'
  3005.                 ja uxc_1
  3006.         sub al,'0'        ; CF <- 0
  3007.         ret
  3008. uxc_1:          or al,20h        ; upper case -> lower case
  3009.         cmp al,'a'
  3010.                 jb uxc_ret        ; If failure, CF == 1 already
  3011.                 cmp al,'f'
  3012.                 ja uxc_err
  3013.                 sub al,'a'-10           ; CF <- 0
  3014.                 ret
  3015. uxc_err:        stc
  3016. uxc_ret:    ret
  3017.  
  3018. ;
  3019. ;
  3020. ; getline:    Get a command line, converting control characters to spaces
  3021. ;               and collapsing streches to one; a space is appended to the
  3022. ;               end of the string, unless the line is empty.
  3023. ;        The line is terminated by ^J, ^Z or EOF and is written
  3024. ;        to ES:DI.  On return, DI points to first char after string.
  3025. ;        CF is set if we hit EOF.
  3026. ;
  3027. getline:
  3028.         call skipspace
  3029.                 mov dl,1                ; Empty line -> empty string.
  3030.                 jz gl_eof               ; eof
  3031.                 jc gl_eoln              ; eoln
  3032.         call ungetc
  3033. gl_fillloop:    push dx
  3034.         push di
  3035.         call getc
  3036.         pop di
  3037.         pop dx
  3038.         jc gl_ret        ; CF set!
  3039.         cmp al,' '
  3040.         jna gl_ctrl
  3041.         xor dx,dx
  3042. gl_store:    stosb
  3043.         jmp short gl_fillloop
  3044. gl_ctrl:    cmp al,10
  3045.         je gl_ret        ; CF clear!
  3046.         cmp al,26
  3047.         je gl_eof
  3048.         and dl,dl
  3049.         jnz gl_fillloop        ; Ignore multiple spaces
  3050.         mov al,' '        ; Ctrl -> space
  3051.         inc dx
  3052.         jmp short gl_store
  3053. gl_eoln:        clc                     ; End of line is not end of file
  3054.                 jmp short gl_ret
  3055. gl_eof:         stc
  3056. gl_ret:        pushf            ; We want the last char to be space!
  3057.         and dl,dl
  3058.         jnz gl_xret
  3059.         mov al,' '
  3060.         stosb
  3061. gl_xret:    popf
  3062.         ret
  3063.  
  3064.  
  3065. %ifdef debug        ; This code for debugging only
  3066. ;
  3067. ; dumpregs:    Dumps the contents of all registers
  3068. ;
  3069.                 assume ds:_text, es:NOTHING, fs:NOTHING, gs:NOTHING
  3070. dumpregs    proc near        ; When calling, IP is on stack
  3071.         pushf            ; Store flags
  3072.         pusha
  3073.         push ds
  3074.         push es
  3075.         push fs
  3076.         push gs
  3077.         push cs            ; Set DS <- CS
  3078.         pop ds
  3079.         cld            ; Clear direction flag
  3080.         mov si,offset crlf_msg
  3081.         call writestr
  3082.         mov bx,sp
  3083.         add bx,byte 26
  3084.         mov si,offset regnames
  3085.         mov cx,2        ; 2*7 registers to dump
  3086. dump_line:    push cx
  3087.         mov cx,7        ; 7 registers per line
  3088. dump_reg:    push cx
  3089.         mov cx,4        ; 4 characters/register name
  3090. wr_reg_name:    lodsb
  3091.         call writechr
  3092.         loop wr_reg_name
  3093.         mov ax,ss:[bx]
  3094.         dec bx
  3095.         dec bx
  3096.         call writehex
  3097.         pop cx
  3098.         loop dump_reg
  3099.         mov al,0Dh        ; <CR>
  3100.         call writechr
  3101.         mov al,0Ah        ; <LF>
  3102.         call writechr
  3103.         pop cx
  3104.         loop dump_line
  3105.         pop gs
  3106.         pop fs
  3107.         pop es
  3108.         pop ds
  3109.         popa            ; Restore the remainder
  3110.         popf            ; Restore flags
  3111.         ret
  3112. dumpregs    endp
  3113.  
  3114. regnames    db ' IP: FL: AX: CX: DX: BX: SP: BP: SI: DI: DS: ES: FS: GS:'
  3115.  
  3116. ;
  3117. ; writehex:    Writes a 16-bit hexadecimal number (in AX)
  3118. ;
  3119. writehex    proc near
  3120.         push bx
  3121.         push cx
  3122.         mov cx,4        ; 4 numbers
  3123. write_hexdig:    xor bx,bx
  3124.         push cx
  3125.         mov cx,4        ; 4 bits/digit
  3126. xfer_digit:    shl ax,1
  3127.         rcl bx,1
  3128.         loop xfer_digit
  3129.         push ax
  3130.         mov ax,bx
  3131.         or al,'0'
  3132.         cmp al,'9'
  3133.         jna ok_digit
  3134.         add al,'A'-'0'-10
  3135. ok_digit:    call writechr
  3136.         pop ax
  3137.         pop cx
  3138.         loop write_hexdig
  3139.         pop cx
  3140.         pop bx
  3141.         ret
  3142. writehex    endp
  3143.  
  3144. debug_magic    dw 0D00Dh
  3145.  
  3146. %endif ; debug
  3147. ;
  3148. ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
  3149. ;           to by ES:DI; ends on encountering any whitespace
  3150. ;
  3151.  
  3152. mangle_name:
  3153.         mov cx,11            ; # of bytes to write
  3154. mn_loop:
  3155.         lodsb
  3156.         cmp al,' '            ; If control or space, end
  3157.         jna mn_end
  3158.         cmp al,'.'            ; Period -> space-fill
  3159.         je mn_is_period
  3160.         cmp al,'a'
  3161.         jb mn_not_lower
  3162.         cmp al,'z'
  3163.         ja mn_not_uslower
  3164.         sub al,020h
  3165.         jmp short mn_not_lower
  3166. mn_is_period:    mov al,' '            ; We need to space-fill
  3167. mn_period_loop: cmp cx,3            ; If <= 3 characters left
  3168.         jbe mn_loop            ; Just ignore it
  3169.         stosb                ; Otherwise, write a period
  3170.         loop mn_period_loop        ; Dec CX and (always) jump
  3171. mn_not_uslower: cmp al,ucase_low
  3172.         jb mn_not_lower
  3173.         cmp al,ucase_high
  3174.         ja mn_not_lower
  3175.         mov bx,ucase_tab-ucase_low
  3176.                 cs xlatb
  3177. mn_not_lower:    stosb
  3178.         loop mn_loop            ; Don't continue if too long
  3179. mn_end:
  3180.         mov al,' '            ; Space-fill name
  3181.         rep stosb            ; Doesn't do anything if CX=0
  3182.         ret                ; Done
  3183.  
  3184. ;
  3185. ; Upper-case table for extended characters; this is technically code page 865,
  3186. ; but code page 437 users will probably not miss not being able to use the
  3187. ; cent sign in kernel images too much :-)
  3188. ;
  3189. ; The table only covers the range 129 to 164; the rest we can deal with.
  3190. ;
  3191. ucase_low    equ 129
  3192. ucase_high    equ 164
  3193. ucase_tab    db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
  3194.         db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
  3195.         db 157, 156, 157, 158, 159, 'AIOU', 165
  3196.  
  3197. ;
  3198. ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
  3199. ;                filename to the conventional representation.  This is needed
  3200. ;                for the BOOT_IMAGE= parameter for the kernel.
  3201. ;                NOTE: A 13-byte buffer is mandatory, even if the string is
  3202. ;                known to be shorter.
  3203. ;
  3204. ;                DS:SI -> input mangled file name
  3205. ;                ES:DI -> output buffer
  3206. ;
  3207. ;                On return, DI points to the first byte after the output name,
  3208. ;                which is set to a null byte.
  3209. ;
  3210. unmangle_name:
  3211.                 push si                 ; Save pointer to original name
  3212.                 mov cx,8
  3213.                 mov bp,di
  3214. un_copy_body:   lodsb
  3215.                 call lower_case
  3216.                 stosb
  3217.                 cmp al,' '
  3218.                 jbe un_cb_space
  3219.                 mov bp,di               ; Position of last nonblank+1
  3220. un_cb_space:    loop un_copy_body
  3221.                 mov di,bp
  3222.                 mov al,'.'              ; Don't save
  3223.                 stosb
  3224.                 mov cx,3
  3225. un_copy_ext:    lodsb
  3226.                 call lower_case
  3227.                 stosb
  3228.                 cmp al,' '
  3229.                 jbe un_ce_space
  3230.                 mov bp,di
  3231. un_ce_space:    loop un_copy_ext
  3232.                 mov di,bp
  3233.                 mov byte [es:di], 0
  3234.                 pop si
  3235.                 ret
  3236.  
  3237. ;
  3238. ; lower_case: Lower case a character in AL
  3239. ;
  3240. lower_case:
  3241.                 cmp al,'A'
  3242.                 jb lc_ret
  3243.                 cmp al,'Z'
  3244.                 ja lc_1
  3245.                 or al,20h
  3246.                 ret
  3247. lc_1:           cmp al,lcase_low
  3248.                 jb lc_ret
  3249.                 cmp al,lcase_high
  3250.                 ja lc_ret
  3251.                 push bx
  3252.                 mov bx,lcase_tab-lcase_low
  3253.                    cs xlatb
  3254.                 pop bx
  3255. lc_ret:         ret
  3256.  
  3257. ;
  3258. ; Lower-case table for codepage 865
  3259. ;
  3260. lcase_low       equ 128
  3261. lcase_high      equ 165
  3262. lcase_tab       db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
  3263.                 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
  3264.                 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
  3265.                 db 161, 162, 163, 164, 164
  3266. ;
  3267. ; Various initialized or semi-initialized variables
  3268. ;
  3269. copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
  3270.         db 0Dh, 0Ah, 0
  3271. boot_prompt    db 'boot: ',0
  3272. wipe_char    db 08h, ' ', 08h, 0
  3273. err_notfound    db 'Could not find kernel image: ',0
  3274. err_notkernel    db 0Dh, 0Ah, 'Invalid or corrupt kernel image.', 0Dh, 0Ah, 0
  3275. err_not386    db 'It appears your computer uses a 286 or lower CPU.'
  3276.         db 0Dh, 0Ah
  3277.         db 'You cannot run Linux unless you have a 386 or higher CPU'
  3278.         db 0Dh, 0Ah
  3279.         db 'in your machine.  If you get this message in error, hold'
  3280.         db 0Dh, 0Ah
  3281.         db 'down the Ctrl key while booting, and I will take your'
  3282.         db 0Dh, 0Ah
  3283.         db 'word for it.', 0Dh, 0Ah, 0
  3284. err_noram    db 'It appears your computer has less than 608K of low ("DOS")'
  3285.         db 0Dh, 0Ah
  3286.         db 'RAM.  Linux needs at least this amount to boot.  If you get'
  3287.         db 0Dh, 0Ah
  3288.         db 'this message in error, hold down the Ctrl key while'
  3289.         db 0Dh, 0Ah
  3290.         db 'booting, and I will take your word for it.', 0Dh, 0Ah, 0
  3291. err_badcfg      db 'Unknown keyword in syslinux.cfg.', 0Dh, 0Ah, 0
  3292. err_noparm      db 'Missing parameter in syslinux.cfg.', 0Dh, 0Ah, 0
  3293. err_noinitrd    db 0Dh, 0Ah, 'Could not find ramdisk image: ', 0
  3294. err_nohighmem   db 'Not enough memory to load specified kernel.', 0Dh, 0Ah, 0
  3295. err_highload    db 0Dh, 0Ah, 'Kernel transfer failure.', 0Dh, 0Ah, 0
  3296. err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
  3297.                 db 0Dh, 0Ah, 0
  3298. err_notdos    db ': attempted DOS system call', 0Dh, 0Ah, 0
  3299. err_comlarge    db 'COMBOOT image too large.', 0Dh, 0Ah, 0
  3300. err_bootsec    db 'Invalid or corrupt boot sector image.', 0Dh, 0Ah, 0
  3301. loading_msg     db 'Loading ', 0
  3302. dotdot_msg      db '.'
  3303. dot_msg         db '.', 0
  3304. aborted_msg    db ' aborted.'            ; Fall through to crlf_msg!
  3305. crlf_msg    db 0Dh, 0Ah, 0
  3306. syslinux_cfg    db 'SYSLINUXCFG'
  3307. ;
  3308. ; Command line options we'd like to take a look at
  3309. ;
  3310. ; mem= and vga= are handled as normal 32-bit integer values
  3311. initrd_cmd    db 'initrd='
  3312. initrd_cmd_len    equ 7
  3313. ;
  3314. ; Config file keyword table
  3315. ;
  3316.         align 2
  3317. keywd_table    db 'ap' ; append
  3318.         db 'de' ; default
  3319.         db 'ti' ; timeout
  3320.         db 'fo'    ; font
  3321.         db 'kb' ; kbd
  3322.         db 'di' ; display
  3323.         db 'pr' ; prompt
  3324.         db 'la' ; label
  3325.                 db 'im' ; implicit
  3326.         db 'ke' ; kernel
  3327.         db 'f1' ; F1
  3328.         db 'f2' ; F2
  3329.         db 'f3' ; F3
  3330.         db 'f4' ; F4
  3331.         db 'f5' ; F5
  3332.         db 'f6' ; F6
  3333.         db 'f7' ; F7
  3334.         db 'f8' ; F8
  3335.         db 'f9' ; F9
  3336.         db 'f0' ; F10
  3337.         dw 0
  3338. ;
  3339. ; Extensions to search for (in *reverse* order).  Note that the last
  3340. ; (lexically first) entry in the table is a placeholder for the original
  3341. ; extension, needed for error messages.  The exten_table is shifted so
  3342. ; the table is 1-based; this is because a "loop" cx is used as index.
  3343. ;
  3344. exten_table:
  3345. OrigKernelExt:    dd 0            ; Original extension
  3346.         db 'COM',0        ; COMBOOT (same as DOS)
  3347.         db 'BS ',0        ; Boot Sector 
  3348.         db 'BSS',0        ; Boot Sector (add superblock)
  3349.         db 'CBT',0        ; COMBOOT (specific)
  3350.  
  3351. exten_count    equ (($-exten_table) >> 2) - 1    ; Number of alternates
  3352. ;
  3353. ; Misc initialized (data) variables
  3354. ;
  3355. AppendLen       dw 0                    ; Bytes in append= command
  3356. KbdTimeOut      dw 0                    ; Keyboard timeout (if any)
  3357. FKeyMap        dw 0            ; Bitmap for F-keys loaded
  3358. CmdLinePtr    dw cmd_line_here    ; Command line advancing pointer
  3359. initrd_flag    equ $
  3360. initrd_ptr    dw 0            ; Initial ramdisk pointer/flag
  3361. VKernelCtr    dw 0            ; Number of registered vkernels
  3362. ForcePrompt    dw 0            ; Force prompt
  3363. AllowImplicit   dw 1                    ; Allow implicit kernels
  3364. ;
  3365. ; Stuff for the command line; we do some trickery here with equ to avoid
  3366. ; tons of zeros appended to our file and wasting space
  3367. ;
  3368. linuxauto_cmd    db 'linux '
  3369. auto_cmd    db 'auto',0
  3370. linuxauto_len   equ $-linuxauto_cmd
  3371. auto_len        equ $-auto_cmd
  3372. boot_image      db 'BOOT_IMAGE='
  3373. boot_image_len  equ $-boot_image
  3374.                 align 4, db 0        ; For the good of REP MOVSD
  3375. command_line    equ $
  3376. default_cmd    equ $+(max_cmd_len+2)
  3377. ldlinux_end    equ default_cmd+(max_cmd_len+1)
  3378. kern_cmd_len    equ ldlinux_end-command_line
  3379. ldlinux_len    equ ldlinux_end-ldlinux_magic
  3380. ;
  3381. ; Put the getcbuf right after the code, aligned on a sector boundary
  3382. ;
  3383. end_of_code    equ (ldlinux_end-bootsec)+7C00h
  3384. getcbuf        equ (end_of_code + 511) & 0FE00h
  3385.